diff --git a/README.md b/README.md index 982c92a..6da88ab 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ - [4.7 通用 gadget](doc/4.7_common_gadget.md) - [4.8 使用 DynELF 泄露函数地址](doc/4.8_dynelf.md) - [4.9 给 ELF 文件打 patch](doc/4.9_patch_elf.md) + - [4.10 给 PE 文件打 patch](doc/4.10_patch_pe.md) - [五、高级篇](doc/5_advanced.md) - [5.1 Fuzz 测试](doc/5.1_fuzz.md) diff --git a/SUMMARY.md b/SUMMARY.md index 5362c70..addb0ef 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -58,6 +58,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [4.7 通用 gadget](doc/4.7_common_gadget.md) * [4.8 使用 DynELF 泄露函数地址](doc/4.8_dynelf.md) * [4.9 给 ELF 文件打 patch](doc/4.9_patch_elf.md) + * [4.10 给 PE 文件打 patch](doc/4.10_patch_pe.md) * [五、高级篇](doc/5_advanced.md) * [5.1 Fuzz 测试](doc/5.1_fuzz.md) * [5.2 Pin 动态二进制插桩](doc/5.2_pin.md) diff --git a/doc/2.5_radare2.md b/doc/2.5_radare2.md index 25fde62..c20be04 100644 --- a/doc/2.5_radare2.md +++ b/doc/2.5_radare2.md @@ -679,6 +679,8 @@ block size 是在我们没有指定行数的时候使用的默认值,输入 `b | aav [sat] find values referencing a specific section or map | aau [len] list mem areas (larger than len bytes) not covered by functions ``` +- `afl`:列出所有函数。 +- `axt [addr]`:找到对给定地址的交叉引用。 #### Flags flag 用于将给定的偏移与名称相关联,flag 被分为几个 flag spaces,用于存放不同的 flag。 diff --git a/doc/4.10_patch_pe.md b/doc/4.10_patch_pe.md new file mode 100644 index 0000000..f3c86d8 --- /dev/null +++ b/doc/4.10_patch_pe.md @@ -0,0 +1 @@ +# 4.10 给 PE 文件打 patch diff --git a/doc/4.9_patch_elf.md b/doc/4.9_patch_elf.md index b56bfbd..43ee689 100644 --- a/doc/4.9_patch_elf.md +++ b/doc/4.9_patch_elf.md @@ -1 +1,101 @@ -# 给 ELF 文件打 patch +# 4.9 给 ELF 文件打 patch + +- [手工 patch](#手工-patch) +- [使用工具 patch](#使用工具-patch) + + +许多时候,我们不能获得程序源码,只能直接对二进制文件进行修改,这就是所谓的 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 +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 二进制文件。 diff --git a/doc/4_tips.md b/doc/4_tips.md index abc31ee..e41c14a 100644 --- a/doc/4_tips.md +++ b/doc/4_tips.md @@ -9,3 +9,4 @@ - [4.7 通用 gadget](4.7_common_gadget.md) - [4.8 使用 DynELF 泄露函数地址](4.8_dynelf.md) - [4.9 给 ELF 文件打 patch](4.9_patch_elf.md) +- [4.10 给 PE 文件打 patch](4.10_patch_pe.md) diff --git a/doc/7.1_Linuxtools.md b/doc/7.1_Linuxtools.md index 1becf6b..8e2ad83 100644 --- a/doc/7.1_Linuxtools.md +++ b/doc/7.1_Linuxtools.md @@ -31,6 +31,11 @@ skip=N skip N ibs-sized blocks at start of input bs=BYTES read and write up to BYTES bytes at a time ``` +patch 偏移 12345 处的一个字节: +``` +echo 'X' | dd of=binary.file bs=1 seek=12345 count=1 +``` + #### 常见用法 ```shell $ dd if=[file1] of=[file2] skip=[size] bs=[bytes] @@ -353,12 +358,13 @@ $ strings [executable] | grep -i upx -g number of octets per group in normal output. Default 2 (-e: 4). -i output in C include file style. -l len stop after octets. +-r reverse operation: convert (or patch) hexdump into binary. -u use upper case hex letters. ``` #### 常见用法 ```shell -$ xxd -g1 +$ xxd -g1 [binary] ``` #### 练习