2018-03-24 00:05:55 +07:00
|
|
|
|
# 4.9 patch 二进制文件
|
2017-12-01 15:05:49 +07:00
|
|
|
|
|
2018-03-24 00:05:55 +07:00
|
|
|
|
- [什么是 patch](#什么是-patch)
|
2017-12-01 15:05:49 +07:00
|
|
|
|
- [手工 patch](#手工-patch)
|
|
|
|
|
- [使用工具 patch](#使用工具-patch)
|
|
|
|
|
|
|
|
|
|
|
2018-03-24 00:05:55 +07:00
|
|
|
|
## 什么是 patch
|
2017-12-01 15:05:49 +07:00
|
|
|
|
许多时候,我们不能获得程序源码,只能直接对二进制文件进行修改,这就是所谓的 patch,你可以使用十六进制编辑器直接修改文件的字节,也可以利用一些半自动化的工具。
|
|
|
|
|
|
|
|
|
|
patch 有很多种形式:
|
|
|
|
|
- patch 二进制文件(程序或库)
|
|
|
|
|
- 在内存里 patch(利用调试器)
|
|
|
|
|
- 预加载库替换原库文件中的函数
|
|
|
|
|
- triggers(hook 然后在运行时 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
|
|
|
|
|
一个简单的例子:
|
|
|
|
|
```c
|
|
|
|
|
#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.puts`,`sym.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](https://github.com/lunixbochs/patchkit) 可以让我们通过 Python 脚本来 patch ELF 二进制文件。
|