ida插件——kaypatch与findcrypt
安装
安装python3
更换pip源
以管理员身份运行cmd
pip install pip 升级pip版本:>>> pip install --user --upgrade pip 提示 No module named pip 解决方法:>>> python -m ensurepip (好像不进行这一步也问题不大 pip install keystone-engine pip install six pip install yara-python
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
> 工具地址:https://github.com/keystone-engine/keypatch
>
> https://github.com/polymorf/findcrypt-yara
下载工具,并将`keypatch.py`、`findcrypt3.py`、 `findcrypt3.rules`放到ida的plugins目录,重启ida即可
## 使用
### keypatch
edit - keypatch ,或者control+alt+k即可
修改后可以看到旁边的注释,或者ctrl+alt+p 可以看到曾经的修改
如果需要保存,edit - patch program - apply patches to input file ,然后ok即可
### findcrypt
Edit->Plugins->Findcrypt,具体的等遇到再补(摸了
# 自动patch(限制execve等危险函数
参考:[homura_pwn_waf](https://github.com/wjbsyc/homura_pwn_waf/tree/ed27c8dbdb48d08f858f150fb6ffdcc3ee14ee7a)
可能的坑点:我在ubuntu20和mac本机上死活跑不通,缺一个keystone,即使我 `pip install keystone-engine`也不好使,但是我在裸ubuntu18上虽然也缺,但是`pip install keystone-engine`后就好了,可能是我之前尝试的地方都有python3的问题。
结果我整了一天,装成功之后patch了程序跑不了。可能是程序本身调用了这些玩意?行吧,三点了,该睡了。
# 手动patch
## 栈溢出
32位:更改参数大小即可
64位:找到对应的寄存器
## 格式化字符串
### 方法一 更改函数
如果有puts函数可以更改成函数的地址,即call _puts
首先找到puts函数的plt表地址,然后找到call命令的下一条命令地址,相减得到偏移,如果是负数记得使用补码形式
![](https://space.0bs3rver.workers.dev/0bs3rver/Picture/master//blogimg/pwn-patch-1.png)
E8 是操作码 后四位是偏移,注意x86是小端序 所以改成 E8 46 FE FF FF 即可
但是这种方式有一个问题,因为puts函数是自动在输出的字符串尾部加入一个回车符,如果check脚本中是比较两次输入与输出是否全等,这种patch方法就不能过关。
### 方法二 加入一个%s参数
需要有汇编指令可供修改,参考 [pwn之简单patch](http://baijiahao.baidu.com/s?id=1680405668162970131&wfr=spider&for=pc)
## uaf
### lief
lief是一个开源的跨平台的可执行文件修改工具,链接: https://github.com/lief-project/LIEF
提供了python、C、C++等接口,用于解析/修改各种不同平台/格式的文件。
python安装:`sudo pip install lief`
它最新的安装包只支持python3.6以上,如果要用python2.7需要安装以前的包
python2安装`sudo pip install lief==0.8.3.post3`
api文档:https://lief.quarkslab.com/doc/latest/index.html
### 增加segment
这个方法的目的是增加一个程序段,在这个程序段中加入一个修复漏洞的程序代码,一般程序会在call某个函数时触发漏洞,一般语句为call 0x8041234,可以劫持这句话的逻辑,改成call我们定义的修复函数。
示例代码如下:
```c
#include <stdio.h>
#include <stdlib.h>
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 0;
}
如果我们想把第一个printf改成我们自己的逻辑,首先需要编译一个包含实现patch函数的静态库,比如:
1 | void myprintf(char *a,int b){ |
如上,将printf改成了write(0,”/bin/sh%d”,0x20),利用注释的gcc命令将其编译。
patch程序的流程是首先将代码段加入到binary程序中,然后修改跳转逻辑,将call printf@plt,改成call myprintf。
lief中提供了add参数可以用于为二进制文件增加段:
1 | binary = lief.parse(binary_name) |
在修改跳转语句部分,由程序的call执行寻址方法是相对寻址的,即call addr = EIP + addr
因此需要计算写入的新函数距离要修改指令的偏移,计算方法如下:
1 | call xxx =(addr of new segment + offset function ) - (addr of order + 5 /*length of call xx*/) |
由于偏移地址是补码表示的,因此在用python计算时需要对结果异或0xffffffff,最终patch脚本如下:
1 | import lief |
可以看到确实patch成功了
但是中间的hook编译时有点问题,我在不同的虚拟机上跑的hook,并不能成功,a.c 和 hook.c 在同一个地方编译,才成功,之后patch可能也需要注意这个问题。
不过缺点可以看到,文件大小变化很大,可能会被判定为通防或者宕机。
增加library
当程序中加载两个库时,在调用某一函数在两个库内同名存在时,是有一定查找顺序的,也就是可以实现,在不修改程序正常代码的前提下,对全部libc函数进行hook。
优势很明显,可以执行任意libc内函数代码,让编程更容易。
不过缺点也很明显,首先程序变得巨大,并且当不存在这个静态链接库的时候,程序跑不起来…
修改程序.eh_frame段——重写函数调用
感觉上改.en_frame是目前对程序改动最小的通用方案,.eh_frame段会加载到程序中来,并且自身带有可执行权限,如果我们把patch的放到这里,首先我们没有附加任何东西,所以对程序的大小影响不大,其次他自带可执行权限,非常方便,缺点可能就是.en_frame的大小有限。
脚本如下:
1 | import lief |
可以看到先跳到 .eh_frame 然后回来
修改程序.eh_frame段——指针清零
pwn中的堆题最常见的漏洞就是free后指针未清零,我们尝试patch一下
我们以ciscn2021的loneywolf为例
1 | unsigned __int64 sub_C60() |
可以很明显的看到,存在free后未清零的问题
1 | .text:0000000000000C60 ; __unwind { |
我们在.en_frame上随便找个地,然后开始对着改,需要注意的是字符串是通过计算偏移来实现的,更换地址后需要重新计算偏移。
old: arm_addr = 0xC64 + 0x7 = 0xc6b str_addr = 0xf48 0xf48 - 0xc6b = 0x2dd
new: arm_addr = 0x1074 + 0x7 = 0x107b str_addr = 0xf48 0xf48 - 0x107b = 0xfffffecd
但是写着写着我发现有点不对劲,这一个个偏移算的很烦,而且有的指令还看起来不太对劲,比如mov [rsp+18h+var_10], rax
,我还没办法用keypatch直接改,仔细想想,我为什么要重复写这么一大坨玩意呢,我需要的只是加上几行命令而已,直接把一行改成跳转然后执行完了再跳回去不就得了。
这里我偷了个懒,直接把最后的canary检查给关了,其实不关也行,或者是干脆用这几行的空闲来加上patch,我这里主要是想试试jmp,keypatch会直接帮你计算偏移,确实好用
修改之后如下:
反汇编如下:
参考: