尝试用scasb读取程序自身代码遇段错误,咨询该操作是否可行?
用SCASB读取自身.text段的可行性与问题修复
首先可以明确:使用SCASB指令访问程序自身的.text段是完全可行的,你的段错误来自几个关键的寄存器配置和指令使用误区,下面逐一拆解并给出修复方案:
核心问题分析
1. SCASB的本质是扫描比较,不是直接读取
SCASB指令的作用是将AL寄存器中的值与ES:EDI(32位)/RDI(64位)指向的字节做比较,然后根据DF标志位自动增减地址寄存器。你当前的代码既没有设置AL的目标值,也没有利用它的扫描逻辑——这不是“读取”操作的正确用法,如果只是想读取字节,用movb (%rdi), %al更直接;但如果要利用SCASB做扫描(比如找某个特定字节),必须先把目标字节存入AL。
2. 32位模式下段寄存器配置错误
在32位保护模式下,ES寄存器不能直接设为0(空选择子会触发段错误)。你需要让ES指向与CS相同的段选择子,因为CS对应的代码段默认是可读可执行的(只要链接脚本没特殊限制)。通过mov %cs, %ax; mov %ax, %es可以让ES和CS同段,从而合法访问.text段的地址。
3. 64位模式下的隐性问题
64位平坦内存模型不需要段寄存器干预,但你的代码存在两个小问题:
- 没有初始化
AL的值(虽然这不会直接导致段错误,但会让SCASB的比较结果无意义) - 程序返回时没有设置返回值(不过这不是段错误的根源,只是不规范)
修复后的示例代码
64位版本(编译命令:gcc -m64 -nostdlib yourcode.s -o test)
.section .text .globl main main: mov $0x03, %al ; 设置要扫描的目标字节(比如找0x03) leaq str(%rip), %rdi ; 用RIP相对寻址加载str的地址(64位推荐写法) scasb ; 比较AL与(RDI),然后RDI自动+1(DF默认是0) ; 此时如果ZF=1,说明找到目标字节,RDI指向str+1的位置 xor %eax, %eax ; 设置返回值0 ret str: .byte 0x01,0x02,0x03,0x04,0x05
32位版本(编译命令:gcc -m32 -nostdlib yourcode.s -o test)
.section .text .globl main main: mov %cs, %ax ; 将CS段选择子加载到AX mov %ax, %es ; 设置ES与CS同段,允许访问.text段 mov $0x03, %al ; 设置目标扫描字节 mov $str, %edi ; 加载str的地址到EDI scasb ; 比较AL与ES:EDI,EDI自动+1 xor %eax, %eax ; 返回0 ret str: .byte 0x01,0x02,0x03,0x04,0x05
额外注意事项
- 确保你的.text段具有读权限:绝大多数编译链接工具默认会给.text段添加可读权限(因为要支持字符串常量、只读数据等),如果你的链接脚本做了特殊限制,需要修改配置添加
READONLY属性。 DF标志的影响:如果之前的代码修改过DF标志(比如用std指令设置为1),SCASB会让地址寄存器递减,记得用cld指令恢复默认的DF=0(地址递增)。
内容的提问来源于stack exchange,提问作者qwr
相关产品推荐
相关产品推荐

