我在 Ubuntu 14.04 上编译了您的程序并将其放在https://mega.co.nz/#!gdRRxRzZ!dw08GEHvXeTxXqurcpMLOxpXVjZa807TJN0PH60h4Rg 上;如果您想回溯以下步骤,您可能想使用该二进制文件,因为如果您没有 C 编译器和库的确切版本,您的二进制文件可能会有所不同。
该文件是一个 zip,包括原始 detour.c、已编译程序 (detour.orig) 和修补程序 (detour.patched)。
首先,让我们使用 objdump 反汇编二进制文件:
$ objdump -d detour.orig|less
.. stuff omitted ..
000000000040057d <hello>:
40057d: 55 push %rbp
40057e: 48 89 e5 mov %rsp,%rbp
400581: bf 34 06 40 00 mov $0x400634,%edi
400586: b8 00 00 00 00 mov $0x0,%eax
40058b: e8 d0 fe ff ff callq 400460 <printf@plt>
400590: bf 3b 06 40 00 mov $0x40063b,%edi
400595: e8 b6 fe ff ff callq 400450 <puts@plt>
40059a: 5d pop %rbp
40059b: c3 retq
000000000040059c <main>:
40059c: 55 push %rbp
40059d: 48 89 e5 mov %rsp,%rbp
4005a0: e8 d8 ff ff ff callq 40057d <hello>
4005a5: 5d pop %rbp
4005a6: c3 retq
4005a7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
4005ae: 00 00
00000000004005b0 <__libc_csu_init>:
.. more stuff omitted ..
并检查数据部分:
$ objdump -s detour.orig | less
.. stuff omitted ..
Contents of section .rodata:
400630 01000200 48656c6c 6f200077 6f726c64 ....Hello .world
400640 2100 !.
Contents of section .eh_frame_hdr:
.. more stuff omitted ..
如您所见,字符串Hello,world位于只读数据部分,位于 400634 和 40063b。这些偏移被传递到printf和puts在hello。为什么puts?好吧,优化器足够聪明,可以将以 '\n' 结尾的常量字符串的 printf 重写为 puts;您可以看到 .rodata 部分的字符串中省略了 '\n'。
现在,我们要插入一个puts("detoured"). 但是 hello 函数中没有空间,如果我们试图在那里插入一些字节,程序中的其他所有内容都会被移动,这是我们想要避免的。此外,.rodata 部分中没有空间用于另一个字符串,.eh_frame_hdr 直接在它后面开始。
但是,请检查地址 4005a7。main 函数在 4005a6 处返回,C 编译器使用一些填充来获取下一个函数 __libc_csu_init,在 16 字节边界处。这意味着我们可以使用一些未使用的字节。查看反汇编的其余部分,我们发现更多这样的字节:
4004ba 66 0f 1f 44 00 00
4004e9 0f 1f 80 00 00 00 00
400529 0f 1f 80 00 00 00 00
4005a7 66 0f 1f 84 00 00 00 00 00
400615 66 66 2e 0f 1f 84 00 00 00 00 00
其中最后一个 400615 的大小适合“绕道”的字符串。
现在我们要对程序集做的是将其修补为:
400590 jmp 4004e9 e9 54 ff ff ff
4004e9 mov $0x400615, %edi; jmp 400529 bf 15 06 40 00 eb 39
400529 callq 400460; jmp 4005a7 e8 32 ff ff ff eb 77
4005a7 mov 0x40063b, %edi; jmp 400595 bf eb 06 40 00 eb e7
它覆盖400590处的指令,将需要的指令放在备用字节中,在部分之间添加跳转,并在跳转到下一条指令之前恢复被覆盖的mov。
现在,是时候使用十六进制编辑器将这些补丁应用到二进制文件中了(不要忘记将 'detoured' 字符串也移动到 400615)。生成的二进制文件应该与 zip 中的 detour.patched 相同。
最后,我们运行打过补丁的程序:
$ detour.patched
Hello detoured world!
如您所见,诀窍是让我们在程序中使用未使用的区域,以避免四处移动东西,这会破坏偏移量。当我将“绕道”字符串放入代码部分时,我有点作弊——如果有任何东西想要对字符串进行写访问,这将失败。如果我想要可写数据,我需要使用数据部分,甚至可能扩展它 - 但这种情况要复杂得多,因为我必须摆弄 ELF 标头和大小,而且我可能需要特定的 ELF正确使用的工具。当前示例只需要一个十六进制编辑器。我什至根据操作码手工制作了十六进制编码;要更复杂一些,请使用radare2 工具。