2026ciscn半决赛awdp部分复现
第一次复现awdp比赛赛题,主要练习一下patch能力
catchme

ida打开菜单题,简单查看会发现free后未置零,存在uaf
在patch中的思路一般是修改关键处指令为jmp,然后跳转到合适区域,一般是eh_frame节(作用:存储异常处理帧信息(Exception Handling Frame)。它包含了 CIE(公共信息条目)和 FDE(帧描述符条目),用于在程序崩溃或抛出异常时进行栈展开(Stack Unwinding),以便调试器或运行时系统能找回调用栈。),然后进行修补接着再跳转回去。
虽然在ida里看的时候这里是只读权限,但是在gdb调试时查看vmmap可以发现这块区域是有执行权限的
可以看到里面存储着一些代码
那么要如何跳转到eh_frame节上呢?我们得先看看程序中jmp指令是如何写的,在option-general中可以设置显示机器码

可以看到jmp short loc_1107的机器码是EB 2D,其中EB是jmp short(短距离跳转),那么2D是什么?
我们惊奇的发现用0x1107减去顺序执行时下一条指令的地址0x10da得到的值就是2D!(具体原理查看汇编语言的跳转指令)
那么我们要跳转到eh_frame节,只需要jmp+偏移即可,也有直接加地址的,但是这种指令所占的空间较大,比如下面这个就是直接加地址的跳转
也有比这个小一个字节的加偏移的跳转
还有通过寄存器直接跳转的
我们看看需要patch的点,call free后面跟着的指令有5个字节长,可以用E9的跳转跳转eh_frame上
简单计算0x14b0-0xe2a=0x686,所以跳转指令应为E9 86 06 00 00

当然如果有比如patching的插件(patching插件安装放在文章末尾了,后面我们全程使用插件来patch,主要是手动查找机器码和计算偏移太麻烦了)就更加方便了
然后我们继续观察,因为我们是需要令free后置零,避免悬空指针也就是uaf,我们需要找到存储着堆块地址的地址
rdi中存储着的是堆块地址,而rdi的值是由rax赋予的,rax的值是从heap_array中配合var_14的值*8获得的,那么我们在rax前停下,就能拿到存储堆块地址的地址了(heap_array+8*[rbp+var_14]),由于free后直接返回了,大概率是用不到rax,rdx或rdi之类的寄存器值的,所以不用考虑保存寄存器的值。
具体的汇编代码应该是
1 | mov eax, [rbp+var_14] |
此时rax的值就是存储着堆块地址的地址了(原函数中的代码是获取堆块地址,这里获取的是存储堆块地址的地址),接下来我们要令其为0(注意在x86/x64 汇编中,MOV指令不支持“立即数直接到内存”的 64 位写入,所以我们此处写入的立即数范围是0~0xfffffffe,以及我们需要注明要写入的数据大小,即要覆盖多少字节,如果后面要写入个64位的立即数,需要通过先赋值给寄存器,再由寄存器写入的间接方法)
1 | mov qword ptr [rax], 0 |
最后再jmp回去
1 | jmp loc_E2A |
让我们写入0x14b0中
需要先选取一块区域全转换为NOP
然后再按c键,转换为代码(mov指令是我一开始改的)
接着用patching插件一个指令一个指令修改(我指的是下图的汇编选项)


还要找到var_14和heap_array对应的具体值替换var_14和heap_array
最后得到
让我们保存后运行调试看看(保存前记得备份)
我们申请了两个堆块后查看存放堆块地址的位置
然后释放堆块,并下断点看看执行过程,进入了jmp
执行了我们加入的代码
执行完成后可以看见堆块地址已被清零,修补成功。
easy_rw_revenge
简单分析pwn程序,发现是个伪装成web服务的菜单题
在add函数中发现了uaf,开始patch会发现call _free后面没有多余的指令了,那就只能改call _free了
找到.eh_frame节地址
然后保险起见,动态调试看了看权限,果然上面那题是凑巧,那这一次我们得去最前面的节表修改这个节的执行权限
比对了一下范围,应该是这个项
将Flags从100修改为101,给予这块区域可执行权限,然后修改call _free为jmp 0x3620
依旧圈出一块区域给NOP掉
并按c令其识别为代码
patch思路是先call free,然后置零,最后返回。那我们先看看原函数获取堆地址的思路
其中蓝色框框是判断存储堆地址的位置是否有值
其中test rax, rax的意思是
绿色框框是判断那个堆的大小是否大于0x500
红色框框则是最后执行free的过程,其中cdqe的意思是将32位寄存器EAX中的有符号数,按其符号位(最高位)扩展到64位寄存器RAX中,同时保持数值本身不变。
patch后如图
Patching安装
用于修补二进制文件中汇编代码的插件
首先准备好patching插件,github上的有点小bug,这里使用的是52pojie的大佬准备的版本
https://www.52pojie.cn/thread-2095708-1-1.html
将解压好的文件(一个py文件,一个文件夹)放入ida目录下的plugins文件夹中
然后此时还不能使用,需要编译keystone,先切换到一个合适的文件夹中执行以下指令
1 | git clone https://github.com/keystone-engine/keystone |
最后输入ldconfig -p | grep keystone查看安装是否成功
然后再打开ida查看patching是否能使用
看下方的日志,已经成功加载了,右键汇编代码也能看见NOP等patch功能
