我认为没有任何可靠的方法可以做到这一点。机器代码格式非常复杂,比汇编文件还要复杂。实际上不可能采用已编译的二进制文件(例如,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 现在只是内存中某处的一些随机偏移量 - 很难创建在正确位置包含此常量的汇编源文件,因为汇编器和链接器可以自由选择这些常量的位置。
其次,我对此并不完全确定(这取决于位置无关代码之类的事情),但我相信对 printf 的引用实际上根本没有在该代码中的指针地址处进行编码,但是 ELF 标头包含一个查找表在运行时动态替换其地址。因此,反汇编代码与源汇编代码并不完全对应。
综上所述,源程序集有symbols虽然编译后的机器代码有地址这是很难扭转的。
第二个主要的复杂性是汇编源文件无法包含原始 ELF 文件头中存在的所有信息,例如要动态链接的库以及原始编译器放置在那里的其他元数据。重建这个是很困难的。
正如我所说,一种特殊的工具可能可以操纵所有这些信息,但不太可能简单地生成可以重新组装回可执行文件的汇编代码。
如果您只想修改可执行文件的一小部分,我建议您采用比重新编译整个应用程序更微妙的方法。使用 objdump 获取您感兴趣的函数的汇编代码。手动将其转换为“源汇编语法”(在这里,我希望有一个工具能够以与输入相同的语法实际产生反汇编) ,然后根据需要进行修改。完成后,重新编译这些函数并使用 objdump 找出修改后的程序的机器代码。然后,使用十六进制编辑器手动将新机器代码粘贴到原始程序相应部分的顶部,注意新代码与旧代码的字节数完全相同(否则所有偏移量都会错误) )。如果新代码较短,您可以使用 NOP 指令将其填充。如果它更长,您可能会遇到麻烦,并且可能必须创建新函数并调用它们。