有没有办法实现这个?我尝试使用了objdump,但是它无法产生任何汇编输出,并且也不被我知道的任何汇编器接受。我想能够更改可执行文件中的指令,然后进行测试。
有没有办法实现这个?我尝试使用了objdump,但是它无法产生任何汇编输出,并且也不被我知道的任何汇编器接受。我想能够更改可执行文件中的指令,然后进行测试。
我认为没有可靠的方法可以实现这一点。机器代码格式非常复杂,比汇编文件更加复杂。无法从已编译的二进制文件(例如ELF格式)生成一个源汇编程序,该程序编译后会得到相同或类似的二进制文件。要了解差异,请比较GCC直接编译成汇编语言(gcc -S
)与可执行文件上的objdump输出(objdump -D
)。
我能想到两个主要的问题。首先,机器代码本身与汇编代码并不是一一对应的,因为存在指针偏移等问题。
例如,考虑输出 Hello World 的C代码:
int main()
{
printf("Hello, world!\n");
return 0;
}
这将编译为x86汇编代码:.LC0:
.string "hello"
.text
<snip>
movl $.LC0, %eax
movl %eax, (%esp)
call printf
.LCO是一个命名常量,printf是共享库符号表中的一个符号。与objdump的输出进行比较:
80483cd: b8 b0 84 04 08 mov $0x80484b0,%eax
80483d2: 89 04 24 mov %eax,(%esp)
80483d5: e8 1a ff ff ff call 80482f4 <printf@plt>
首先,常量.LC0现在只是内存中某个随机偏移量——要创建一个包含此常量的汇编源文件并将其放置在正确位置上会很困难,因为汇编器和链接器可以自由选择这些常量的位置。objconv
反汇编器将反汇编为NASM、YASM、MASM或GNU as语法。该输出可以重新汇编成类似的二进制文件,但代码对齐/偏移的任何假设可能已经改变。例如,过程链接表(PLT)需要使用jmp rel32
编码,以便在运行时填充正确的偏移量。 - Peter Cordesdisp32
:https://dev59.com/n6fja4cB1Zd3GeqPt1Cf。此外,NASM通常可以使用`mov eax,[rdi + strict dword 0]`来强制使用disp32。问题在于,反汇编器在反汇编长于所需指令时不会放入这些覆盖。 - Peter Cordes我用 hexdump
和文本编辑器来完成这个任务。你必须对机器码和存储它的文件格式非常熟悉,并且对于“反汇编、修改,然后重新汇编”有灵活的理解。
如果你只需要进行“局部修改”(重写字节,但不添加或删除字节),那么相对来说会比较容易。
你真的不想替换任何现有的指令,因为这样你就必须手动调整机器码中受影响的绝对地址或相对偏移量,以适应跳转/分支/加载/存储相对于程序计数器的情况,无论是在反汇编中清晰可见的硬编码立即值,还是在动态计算的只能通过在使用之前更改寄存器中的地址或偏移量的指令来修改的值。
你应该始终能够避免删除字节。添加字节可能对于更复杂的修改是必要的,而且会更加困难。
objdump --show-raw-insn -d
输出中,我关心的那一行看起来像这样(为了上下文,给出前后一个指令): f40: aa1503e3 mov x3, x21
f44: 97fffeeb bl af0 <error@plt>
f48: f94013f7 ldr x23, [sp, #32]
0xf44
处的字节0x97fffeeb
。hexdump -Cv
行。00000f40 e3 03 15 aa eb fe ff 97 f7 13 40 f9 e8 02 40 39 |..........@...@9|
00000f40 -- -- -- -- eb fe ff 97 -- -- -- -- -- -- -- -- |..........@...@9|
^
This is offset f44, holding the least significant byte
So the *instruction as a whole* is at the expected offset,
just the bytes are flipped around. Of course, whether the
order matches or not will vary with the architecture.
0xd503201f
反汇编为nop
,所以它似乎是我无操作指令的一个很好的候选。我相应地修改了hexdump
文件中的那一行。00000f40 e3 03 15 aa 1f 20 03 d5 f7 13 40 f9 e8 02 40 39 |..........@...@9|
hexdump -R
将其转换回二进制,使用objdump --show-raw-insn -d
对新的二进制进行反汇编,并验证更改是否正确。 f40: aa1503e3 mov x3, x21
f44: d503201f nop
f48: f94013f7 ldr x23, [sp, #32]
f2c: 350000e8 cbnz w8, f48
f30: b0000002 adrp x2, 1000
f34: 91128442 add x2, x2, #0x4a1
f38: 320003e0 orr w0, wzr, #0x1
f3c: 2a1f03e1 mov w1, wzr
f40: aa1503e3 mov x3, x21
f44: 97fffeeb bl af0 <error@plt>
f48: f94013f7 ldr x23, [sp, #32]
f2c: 14000007 b f48
f30: b0000002 adrp x2, 1000
f34: 91128442 add x2, x2, #0x4a1
f38: 320003e0 orr w0, wzr, #0x1
f3c: 2a1f03e1 mov w1, wzr
f40: aa1503e3 mov x3, x21
f44: 97fffeeb bl af0 <error@plt>
f48: f94013f7 ldr x23, [sp, #32]
f2c: d503201f nop
f30: d503201f nop
f34: d503201f nop
f38: d503201f nop
f3c: d503201f nop
f40: d503201f nop
f44: d503201f nop
f48: f94013f7 ldr x23, [sp, #32]
hexdump -R
是否可用,如果不行,则可以使用替代工具xxd
来替换所有命令中的hexdump
. - mtraceurerror
后不能假设这些寄存器的任何内容(当然,假设error
没有在X0中返回值)。如果编译器需要保存X0、X1等寄存器的内容,它将发出代码从堆栈中加载/恢复这些寄存器和/或使用被调用者保存的寄存器。因此,在您的特定示例中,修补这些寄存器移动是不必要的。 - Léo Lamerror@plt
后的机器代码中,完全可以忽略对调用者保存寄存器的影响。因为对 error
的调用是 在一个 if
块内的最后一件事情,并且使用了编译时常量非零错误代码,这意味着该分支在此 Linux 系统上被定为永远不会返回,编译器有权知道这一点并利用这一知识。 - mtraceur-g
编译源代码通常会提供更好的结果。你可能想试试可重定向反编译器。https://github.com/albertvanderhorst/ciasdis
给出了两个 ELF 可执行文件的示例,附有它们的反汇编和重新组装过程。最初的设计旨在能够修改引导系统,包括代码、解释代码、数据和图形字符,具有从实模式到保护模式的平滑转换等优点(它成功地实现了这一点)。这些示例还演示了如何从可执行文件中提取文本,并用于标签。Debian 软件包适用于 Intel Pentium,但也提供了 Dec Alpha、6809、8086 等插件。miasm
https://github.com/cea-sec/miasm
这似乎是最有前途的具体解决方案。根据项目描述,该库可以:
- 使用Elfesteem打开/修改/生成PE / ELF 32 / 64 LE / BE
- 汇编/反汇编X86 / ARM / MIPS / SH4 / MSP430
因此,它基本上应该:
我认为它不会生成文本反汇编表示,您可能需要遍历Python数据结构。
TODO找到使用库完成所有这些操作的最小示例。一个很好的起点似乎是example/disasm/full.py,它解析给定的ELF文件。关键的顶层结构是Container
,它使用Container.from_stream
读取ELF文件。TODO如何重新组装?这篇文章似乎可以做到:http://www.miasm.re/blog/2016/03/24/re150_rebuild.html
这个问题问是否有其他类似的库:https://reverseengineering.stackexchange.com/questions/1843/what-are-the-available-libraries-to-statically-modify-elf-executables
相关问题:
我认为这个问题不能自动化解决
我认为一般问题并不能完全自动化解决,一般解决方案基本上等同于“如何逆向工程”二进制文件。
为了以有意义的方式插入或删除字节,我们必须确保所有可能的跳转都跳转到相同的位置。
在正式术语中,我们需要提取二进制文件的控制流程图。
然而,对于间接分支(例如:https://en.wikipedia.org/wiki/Indirect_branch),很难确定该图形,参见:间接跳转目的地计算。你可能会对以下内容感兴趣:
如果感兴趣,请查看:Pin、Valgrind(或正在进行此类项目的NaCl - Google的本地客户端,也许是QEmu)。
您可以在 ptrace 的监督下(也就是像 gdb 这样的调试器)运行可执行文件,以此方式控制执行过程,而不需要修改实际文件。当然,这需要像查找要影响的特定指令在可执行文件中的位置等通常的编辑技能。