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,其中EBjmp 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
2
3
4
mov eax, [rbp+var_14]
lea rdx, ds:0[rax*8]
lea rax, heap_array
add rax, rdx

此时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 _freejmp 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
2
3
4
5
6
7
git clone https://github.com/keystone-engine/keystone
cd keystone
mkdir build && cd build
cmake -DBUILD_SHARED_LIBS=ON ..
make -j$(nproc)
sudo make install
sudo ldconfig

最后输入ldconfig -p | grep keystone查看安装是否成功

然后再打开ida查看patching是否能使用

看下方的日志,已经成功加载了,右键汇编代码也能看见NOP等patch功能

本作品由 automata 于 2026-04-15 00:00:00 发布
作品地址:2026ciscn半决赛awdp部分复现
除特别声明外,本站作品均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自 凹凸麦塔
Logo
下一篇从链接过程看ret2dlresolve