You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何向已编译的ELF文件中插入汇编指令

简便向ELF二进制插入汇编指令的实用方法

嘿,我太懂手动改十六进制的糟心了——不仅容易搞错字节顺序,还得对着指令集手册查编码,效率极低!下面是几个我日常用的、不用碰十六进制编辑器的简便方案,亲测靠谱:

方法一:用GDB实时修改并导出新二进制

GDB不仅能调试,还能直接在内存里汇编指令,最后导出修改后的二进制:

  • 先加载目标ELF:gdb ./your_target_binary
  • 定位到你想修改的位置:比如要改main函数开头,就用break main然后run停在断点,或者直接用x/i main查看指令地址(比如0x401120
  • 如果目标内存段是只读的,先临时修改权限:call mprotect(0x401000, 0x1000, 7)(这里0x401000是段起始地址,0x1000是长度,7代表可读可写可执行)
  • 直接汇编指令:输入assemble 0x401120,然后逐行敲你要插入的汇编,比如:
    push rbx
    mov rbx, 0xdeadbeef
    pop rbx
    
    敲完空回车结束汇编
  • 导出修改后的二进制:dump binary memory modified_binary 0x400000 0x403000(这里的地址范围要覆盖整个ELF的代码段和数据段,你可以用readelf -l your_target_binary查程序头的地址范围)

方法二:用汇编文件+objcopy插入独立代码段

如果要插入的代码比较长,写个独立汇编文件更方便:

  1. 写一个带跳转的汇编文件(比如insert_code.s),要注意跳回原执行流程:
    .global my_custom_code
    my_custom_code:
        ; 这里写你的自定义汇编
        mov rax, 0x1234
        ; 跳回原来的指令地址,比如原来的main函数开头是0x401125
        jmp 0x401125
    
  2. 编译成目标文件:as -o insert_code.o insert_code.s
  3. 把这个目标文件作为新段插入到ELF:
    objcopy --add-section .custom_code=insert_code.o \
            --set-section-flags .custom_code=code,alloc,exec \
            your_target_binary modified_binary
    
  4. 最后用GDB或者逆向工具,把原来的指令改成跳转到my_custom_code的地址(用readelf -s modified_binary能查到这个符号的地址)

方法三:用逆向可视化工具(Ghidra/Binary Ninja/IDA)

如果不想敲命令,可视化工具是最省心的:

  • 比如用Ghidra:加载ELF后,找到要修改的指令位置,右键选择「Edit > Patch Instruction」,直接输入汇编指令
  • 如果要插入新代码(不是替换原有指令),可以在空闲内存区域(比如readelf -S查到的空闲段,或者程序末尾的空白区)右键「Edit > Patch Instruction」写新代码
  • 然后把原来的指令改成跳转,指向新代码的起始地址,再在新代码末尾加跳转回原流程的指令
  • 全部改完后,选择「File > Export Program」导出修改后的二进制,一键搞定

避坑提醒

  • 指令长度匹配:如果是替换原有指令,要确保新指令的字节数和原来的一致,不然会破坏后续指令的地址偏移。如果字节数不一样,一定要用跳转(trampoline)到空闲空间执行新代码
  • PIE文件注意:如果是位置无关可执行文件(PIE),不能用绝对地址跳转,要用rip相对寻址,比如jmp my_custom_code(%rip)
  • 权限问题:修改前确认目标段有可写可执行权限,不然GDB或者工具会报错,用patchelf或者objcopy可以调整段权限

内容的提问来源于stack exchange,提问作者Dylan Göpel

火山引擎 最新活动