返回介绍

3.2.1 patch 二进制文件

发布于 2022-02-28 21:35:52 字数 6275 浏览 940 评论 0 收藏 0

什么是 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

一个简单的例子:

#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 二进制文件。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文