CTF-All-In-One/doc/3.2.1_patch_binary.md
2018-08-05 17:43:10 +08:00

3.7 KiB
Raw Blame History

3.2.1 patch 二进制文件

什么是 patch

许多时候,我们不能获得程序源码,只能直接对二进制文件进行修改,这就是所谓的 patch你可以使用十六进制编辑器直接修改文件的字节也可以利用一些半自动化的工具。

patch 有很多种形式:

  • patch 二进制文件(程序或库)
  • 在内存里 patch利用调试器
  • 预加载库替换原库文件中的函数
  • triggershook 然后在运行时 patch

手工 patch

手工 patch 自然会比较麻烦,但能让我们更好地理解一个二进制文件的构成,以及程序的链接和加载。有许多工具可以做到这一点,比如 xxd、dd、gdb、radare2 等等。

xxd

$ echo 01: 01 02 03 04 05 06 07 08 | xxd -r - output
$ xxd -g1 output
00000000: 00 01 02 03 04 05 06 07 08                       .........
$ echo 04: 41 42 43 44 | xxd -r - output
$ xxd -g1 output
00000000: 00 01 02 03 41 42 43 44 08                       ....ABCD.

参数 -r 用于将 hexdump 转换成 binary。这里我们先创建一个 binary然后将将其中几个字节改掉。

radare2

一个简单的例子:

#include<stdio.h>
void main() {
    printf("hello");
    puts("world");
}
$ gcc -no-pie patch.c
$ ./a.out
helloworld

下面通过计算函数偏移,我们将 printf 换成 puts

[0x004004e0]> pdf @ main
            ;-- main:
/ (fcn) sym.main 36
|   sym.main ();
|              ; DATA XREF from 0x004004fd (entry0)
|           0x004005ca      55             push rbp
|           0x004005cb      4889e5         mov rbp, rsp
|           0x004005ce      488d3d9f0000.  lea rdi, str.hello          ; 0x400674 ; "hello"
|           0x004005d5      b800000000     mov eax, 0
|           0x004005da      e8f1feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x004005df      488d3d940000.  lea rdi, str.world          ; 0x40067a ; "world"
|           0x004005e6      e8d5feffff     call sym.imp.puts           ; sym.imp.printf-0x10 ; int printf(const char *format)
|           0x004005eb      90             nop
|           0x004005ec      5d             pop rbp
\           0x004005ed      c3             ret

地址 0x004005da 处的语句是 call sym.imp.printf,其中机器码 e8 代表 call,所以 sym.imp.printf 的偏移是 0xfffffef1。地址 0x004005e6 处的语句是 call sym.imp.putssym.imp.puts 的偏移是 0xfffffed5

接下来找到两个函数的 plt 地址:

[0x004004e0]> is~printf
vaddr=0x004004d0 paddr=0x000004d0 ord=003 fwd=NONE sz=16 bind=GLOBAL type=FUNC name=imp.printf
[0x004004e0]> is~puts
vaddr=0x004004c0 paddr=0x000004c0 ord=002 fwd=NONE sz=16 bind=GLOBAL type=FUNC name=imp.puts

计算相对位置:

[0x004004e0]> ?v 0x004004d0-0x004004c0
0x10

所以要想将 printf 替换为 puts,只要替换成 0xfffffef1 -0x10 = 0xfffffee1 就可以了。

[0x004004e0]> s 0x004005da
[0x004005da]> wx e8e1feffff
[0x004005da]> pd 1
|           0x004005da      e8e1feffff     call sym.imp.puts           ; sym.imp.printf-0x10 ; int printf(const char *format)

搞定。

$ ./a.out
hello
world

当然还可以将这一过程更加简化,直接输入汇编,其他的事情 r2 会帮你搞定:

[0x004005da]> wa call 0x004004c0
Written 5 bytes (call 0x004004c0) = wx e8e1feffff
[0x004005da]> wa call sym.imp.puts
Written 5 bytes (call sym.imp.puts) = wx e8e1feffff

使用工具 patch

patchkit

patchkit 可以让我们通过 Python 脚本来 patch ELF 二进制文件。