因为要去参加某比赛的线下赛,所以准备了一下线下patch相关的东西(然而参加了才发现根本没pwn选手什么事,全程在帮web大佬运维,各种删马、各种杀进程)。本文主要参考的是P4nda师傅写的patch相关的文章,在此基础上自己patch了一番。
使用IDA直接patch
这种方式适合于较简单的修改,不能修改文件结构,直接在Edit–>Patch program–>Assemble中进行修改即可:
例如这里存在off-by-null
,直接将该指令nop掉即可:
如下图所示:
此时查看反编译的结果可以发现已经没有off-by-null
漏洞了,最终还需要将修改的结果保存至文件中:
Edit–>Patch program–>Apply patches to input file
使用LIEF
项目的地址:
https://github.com/lief-project/LIEF
安装如下:
1 | pip install setuptools --upgrade |
工具的使用参考官方文档:
https://lief.quarkslab.com/doc/latest/index.html
使用LIEF增加段来patch
程序的源代码如下:1
2
3
4
5
6
7
8
9
int main(int argc, char** argv) {
printf("/bin/sh%d",102);
puts("let's go\n");
printf("/bin/sh%d",102);
puts("let's gogo\n");
return EXIT_SUCCESS;
}
目标是修改其中的printf函数为我们自己的函数
hook程序中的导入函数
编写hook函数
首先要先编写我们的hook函数,编写hook函数有几个要求:
- 汇编代码必须是位置独立的(也就是要使用-fPIC或-pie / -fPIE标志编译)
- 不要使用libc.so等外部库(使用:-nostdlib -nodefaultlibs flags)
根据上面的限制条件,我们编译hook程序时使用的编译指令如下所示:
gcc -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook
我们编写的hook函数my_printf
如下:1
2
3
4
5
6
7
8
9
10void myprintf(char *a,int b){
asm(
"mov %rdi,%rsi\n"
"mov $0,%rdi\n"
"mov $0x20,%rdx\n"
"mov $0x1,%rax\n"
"syscall\n"
);
}
将hook函数注入到程序并修改got表
1 | import lief |
运行patch后的程序可以发现patch成功:
hook指定地址的函数调用
使用下面的代码可以完成hook程序中指定地址的call函数调用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import lief
from pwn import *
def patch_call(file,srcaddr,dstaddr,arch = "amd64"):
print hex(dstaddr)
length = p32((dstaddr - (srcaddr + 5 )) & 0xffffffff)
order = '\xe8'+length
print disasm(order,arch=arch)
file.patch_address(srcaddr,[ord(i) for i in order])
binary = lief.parse("./vulner")
hook = lief.parse('./hook')
# inject hook program to binary
segment_added = binary.add(hook.segments[0])
hook_fun = hook.get_symbol("myprintf")
dstaddr = segment_added.virtual_address + hook_fun.value
srcaddr = 0x400584
patch_call(binary,srcaddr,dstaddr)
binary.write('vulner.patched')
修改.eh_frame段实现patch
eh_frame
段在执行的时候对程序的影响不大,所以可以把hook代码添加到该段中,通过修改函数跳转的方式来执行hook代码
对section的操作参考官方文档-section部分
section对象中的content属性就是该section中的内容,所以要对待patch程序的.eh_frame
段进行修改,直接将hook
程序中的.text
段的内容赋值到.eh_frame
段的内容就行。赋值完成之后,在通过与前面一致的方法修改函数跳转地址,使其跳转到.eh_frame
段来执行我们的hook代码
具体的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28import lief
from pwn import *
def patch_call(file,srcaddr,dstaddr,arch = "amd64"):
print hex(dstaddr)
length = p32((dstaddr - (srcaddr + 5 )) & 0xffffffff)
order = '\xe8'+length
print disasm(order,arch=arch)
file.patch_address(srcaddr,[ord(i) for i in order])
binary = lief.parse("./vulner")
hook = lief.parse('./hook')
# write hook's .text content to binary's .eh_frame content
sec_ehrame = binary.get_section('.eh_frame')
print sec_ehrame.content
sec_text = hook.get_section('.text')
print sec_text.content
sec_ehrame.content = sec_text.content
print binary.get_section('.eh_frame').content
# hook target call
dstaddr = sec_ehrame.virtual_address
srcaddr = 0x400584
patch_call(binary,srcaddr,dstaddr)
binary.write('vulner.patched')
直接将hook程序中的.text
段的content
赋值到binary程序中的.eh_frame
段的content
段得到的效果如下图所示,内容确实是我们的hook函数:
修改指定的函数调用,使其跳转到我们修改后的.eh_frame
段来执行,效果如下图所示:
参考链接
http://p4nda.top/2018/07/02/patch-in-pwn/