GNU汇编.data段result值执行syscall后损坏,求排查原因
问题原因与解决方案
1. 错误的段布局:.lcomm指令不能放在.data段内
.lcomm是GNU汇编中用于在bss段分配本地未初始化存储的指令,它不应该被放在.data段(初始化数据段)中。强行将.lcomm混入.data段会导致汇编器错误地混合初始化与未初始化数据的内存布局,破坏预期的内存结构,进而引发意外的数据损坏。
正确的段划分方式应该是:
.data result: .byte 1 # 初始化数据放在.data段 .bss .lcomm input 1 # 未初始化数据放在.bss段 .lcomm cha 2
2. x86_64环境下误用32位系统调用接口(int $0x80)
你当前使用的是32位系统调用接口,但运行环境是x86_64架构的Debian。虽然Linux内核提供32位兼容接口,但在64位程序中使用它可能会引发寄存器处理、内存地址解析等兼容性问题,甚至意外改写内存数据。
对于x86_64程序,应当使用原生的64位系统调用接口(syscall指令),对应的调用号和寄存器约定完全不同:
- 系统调用号存储在
rax寄存器中 - 参数依次存入
rdi,rsi,rdx,r10,r8,r9寄存器 - 返回值会被写入
rax
修改后的read与exit系统调用代码示例:
.global _start _start: # 64位read系统调用:读取1字节到input mov $0, %rax # 64位read调用号为0 mov $0, %rdi # 文件描述符0:标准输入 mov $input, %rsi # 缓冲区地址 mov $1, %rdx # 读取字节数 syscall # 执行64位系统调用 # 64位exit系统调用:退出程序,返回码0 mov $60, %rax # 64位exit调用号为60 mov $0, %rdi # 程序返回码 syscall
为什么result会被意外改写?
当你错误地将.lcomm放在.data段时,input的内存位置紧邻result之后,再结合32位系统调用的寄存器处理漏洞(比如64位寄存器高32位未清零导致地址解析错误),内核在处理系统调用时可能意外将数据写入了result的地址。修复段布局并改用64位系统调用接口后,这个问题就能解决。
调试时可以用gdb查看内存状态,比如用x/b &result查看result的字节值,x/b &input查看input的字节值,确认内存布局是否符合预期。
内容的提问来源于stack exchange,提问作者user9863913
相关产品推荐
相关产品推荐

