如何拆解、修改并重新组装Linux可执行文件?

如何拆解、修改并重新组装Linux可执行文件?,linux,x86,disassembly,objdump,Linux,X86,Disassembly,Objdump,有没有办法做到这一点?我使用过objdump,但它不会产生我所知道的任何汇编程序都能接受的汇编输出。我希望能够在可执行文件中更改指令,然后再对其进行测试。我认为没有任何可靠的方法可以做到这一点。机器代码格式非常复杂,比汇编文件更复杂。获取已编译的二进制文件(例如,ELF格式)并生成将编译为相同(或足够相似)二进制文件的源汇编程序是不可能的。要了解差异,请将GCC直接编译到汇编程序(GCC-S)的输出与可执行文件(objdump-D)上objdump的输出进行比较 我能想到两个主要的复杂问题。首先

有没有办法做到这一点?我使用过objdump,但它不会产生我所知道的任何汇编程序都能接受的汇编输出。我希望能够在可执行文件中更改指令,然后再对其进行测试。

我认为没有任何可靠的方法可以做到这一点。机器代码格式非常复杂,比汇编文件更复杂。获取已编译的二进制文件(例如,ELF格式)并生成将编译为相同(或足够相似)二进制文件的源汇编程序是不可能的。要了解差异,请将GCC直接编译到汇编程序(
GCC-S
)的输出与可执行文件(
objdump-D
)上objdump的输出进行比较

我能想到两个主要的复杂问题。首先,由于指针偏移等原因,机器代码本身与汇编代码不是1:1对应关系

例如,考虑C代码到Hello World:

int main()
{
    printf("Hello, world!\n");
    return 0;
}
这将编译为x86汇编代码:

.LC0:
    .string "hello"
    .text
<snip>
    movl    $.LC0, %eax
    movl    %eax, (%esp)
    call    printf
.LC0:
.string“hello”
.文本
movl$.LC0,%eax
移动%eax,(%esp)
调用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>
80483cd:b8 b0 84 04 08 mov$0x80484b0,%eax
80483d2:89 04 24 mov%eax,(%esp)
80483d5:e8 1a ff ff呼叫80482f4
首先,常量.LC0现在只是内存中某个地方的某个随机偏移量——由于汇编程序和链接器可以自由选择这些常量的位置,因此很难创建在正确位置包含该常量的程序集源文件

其次,我不完全确定这一点(这取决于位置无关的代码),但我相信printf的引用实际上根本不是在代码中的指针地址处编码的,但是ELF头包含一个查找表,它在运行时动态地替换它的地址。因此,反汇编代码与源汇编代码并不完全对应

总之,源程序集具有符号,而编译的机器代码具有难以反转的地址

第二个主要的复杂性是程序集源文件不能包含原始ELF文件头中存在的所有信息,例如要动态链接的库以及原始编译器放置在其中的其他元数据。很难重建这个

正如我所说的,一个特殊的工具可以处理所有这些信息,但不太可能简单地生成可以重新组装回可执行文件的汇编代码


如果您对只修改可执行文件的一小部分感兴趣,我建议使用比重新编译整个应用程序更微妙的方法。使用objdump获取您感兴趣的函数的汇编代码。手动将其转换为“源程序集语法”(在这里,我希望有一个工具能够以与输入相同的语法实际生成反汇编),并根据需要修改它。完成后,只需重新编译这些函数,并使用objdump为修改后的程序计算机器代码。然后,使用十六进制编辑器手动将新机器代码粘贴到原始程序相应部分的顶部,注意新代码的字节数与旧代码的字节数完全相同(否则所有偏移量都会出错)。如果新代码较短,则可以使用NOP指令填充它。如果它更长,您可能会遇到麻烦,可能需要创建新函数并调用它们。

要更改二进制程序集中的代码,通常有3种方法

  • 如果它只是一个常量之类的小事,那么您只需使用十六进制编辑器更改位置。假设你能找到它开始
  • 如果您需要修改代码,那么利用LD_预加载覆盖程序中的某些函数。但是,如果函数不在函数表中,则这不起作用
  • 在你想修复的函数处破解代码,直接跳转到你通过LD_PRELOAD加载的函数,然后跳回同一位置(这是上述两种方法的组合)
当然,如果组件进行任何类型的自我完整性检查,则只有第二个可以工作


编辑:如果不是很明显,那么玩二进制程序集是非常高级的开发人员的事情,你在这里很难问清楚,除非你问的是非常具体的事情。

你可能有兴趣做的另一件事:

  • 二进制指令插入-更改现有代码

如果有兴趣,请查看:Pin、Valgrind(或执行此操作的项目:NaCl-谷歌的原生客户端,可能是QEmu)。

您可以在ptrace(换句话说,像gdb这样的调试器)的监督下运行可执行文件,这样,您就可以在不修改实际文件的情况下控制执行。当然,这需要通常的编辑技巧,比如找到您想要影响的特定指令在可执行文件中的位置。

@mgiuca从技术角度正确地回答了这个问题。事实上,将可执行程序分解为易于重新编译的程序集源并非易事

为了给讨论增添一些内容,有一些技术/工具值得探讨,尽管它们在技术上很复杂

  • 静态/动态仪表。这种技术需要分析可执行文件格式,插入/删除/替换指定用途的特定汇编指令,修复对可执行文件中变量/函数的所有引用,并发出新的修改后的可执行文件。我知道的一些工具是:。考虑配置SU
         f40:   aa1503e3    mov x3, x21
         f44:   97fffeeb    bl  af0 <error@plt>
         f48:   f94013f7    ldr x23, [sp, #32]
    
    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.
    
    00000f40  e3 03 15 aa 1f 20 03 d5  f7 13 40 f9 e8 02 40 39  |..........@...@9|
    
         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]