为何int 0x80调用会使rsi寄存器的整数取值变更而字符取值不变?
汇编行读取代码的调试问题分析与修复
问题代码
lp: save mov rax, 3 mov rbx, 2 mov rcx, rsi mov rdx, 1 int 80h load cmp [rsi], byte 0xa je done inc rsi jmp lp done:
其中save和load是用于push/pop rax、rbx、rcx、rdx寄存器的宏。
问题现象
代码可正常读取整行内容,但第二次迭代时,GDB中观察到[rsi]的整数取值发生变化,但其字符取值保持不变,调试输出如下:
13: $rdi = 4206592 14: $rsi = 4206593 15: *(int*)($rdi) = 14391 16: *(int*)($rdi+1) = 56 17: *(char*)($rdi) = 55 '7' 18: *(char*)($rdi+1) = 56 '8'
问题根源分析
- 32位/64位调用规范混用:你使用
rax、rbx等64位寄存器,但int 80h是32位Linux系统调用接口,它仅处理寄存器的低32位,高32位数据会被破坏。这导致你用*(int*)查看内存时,会包含被破坏的高字节数据,而*(char*)只读取低8位,因此字符显示正常。 - 系统调用参数错误:32位
read调用的文件描述符参数中,你用了2(标准错误流)而非0(标准输入),这意味着你实际读取的不是用户输入;同时64位环境下混用32位调用规范,会导致寄存器状态混乱。 - 缺少返回值检查:
read调用的返回值存储在rax中,你未判断是否读取失败或到达EOF,可能导致后续循环访问非法内存。
修复方案(64位环境适配)
改用64位Linux系统调用规范(syscall指令),修正参数与调用逻辑:
lp: save mov rax, 0 ; 64位read系统调用号为0 mov rdi, 0 ; 标准输入文件描述符为0 mov rdx, 1 ; 每次读取1字节 syscall ; 64位系统调用指令 load cmp rax, 0 ; 检查是否读取到EOF或调用失败 jle done cmp byte [rsi], 0xa ; 判断是否为换行符 je done inc rsi jmp lp done:
关键修改说明
- 替换
int 80h为syscall,遵循64位系统调用规范,避免寄存器高32位被破坏 - 修正
read的系统调用号与文件描述符,确保读取的是标准输入流 - 添加返回值检查,避免读取失败后继续循环
- 规范寄存器使用逻辑,消除寄存器状态混乱导致的内存地址异常
内容的提问来源于stack exchange,提问作者Jakub Anderle
相关产品推荐
相关产品推荐

