AWDP一般分为两个板块,Break(自己的payload打通)和Fix(让主办方的payload打不通)
patch:指防御。
checker:指在部署题目的时候,检查选手提交的补丁(patch文件)是否符合预设条件的专门脚本。常见的非常规防御的例子就是把二进制文件直接删除、把关键漏洞语句直接删除、在堆利用的题目中将free语句直接删除等,checker就是用于检查选手是否存在这些非常规补丁的。它主要是通过和提交的二进制文件通信来判断这个二进制文件是否有异常。
打补丁有下面2个基本原则:
❑不改变程序的正常运行流程。
❑能够防御想要防御的攻击
tar -czvf patch.tar.gz notekeeper patch.sh
patch实例入门

这个题的漏洞主要是get函数的无限制读入
1 | text:00000000004009CC call _puts |
要patch就要把它改为有限制的输入
改为read(0, s, 0x20);或者fgets(s, 0x20, stdin);
但问题来了:❌ 原地改不了
gets(s) 只需要:一个参数:s
而 read(0, s, 0x20) 需要:3 个参数:fd、buf、size
所以要从call gets
变化为(修改为read类型)
1 | mov rax,0 |
原来的位置放不下,所以要让程序跳转到其他位置去执行
在很多 CTF / AWDP 程序中:
选取跳转地址有两个要求
1.有x权限
2.更改之后不会影响函数正常运行
一般会选取eh_frame或者eh_frame_hdr
“填补 NOP 指令直到下一条指令边界”
作用:
当用一条新指令替换原来的多条指令、或长度更短时,用NOP把剩余空间补齐,避免留下半条旧指令的字节。在代码段(.text)里 patch 指令,且新指令比原指令短:
原来:
1
2 >mov rax, rbx ; 3字节
>add rax, rcx ; 3字节改成:
1 >xor eax, eax ; 2字节→ 剩下 4 字节会残留旧机器码
→ 必须用 NOP 填满
👉 需要勾你现在这种情况:在
.eh_frame写 read
.eh_frame原本是db xx(纯数据)- 不是指令流
- 没有“下一条指令边界”的概念
在_eh_frame上放read函数

patch call get过去到jmp_addr

之后应用就可以了

基础前置
字节
在 x86 指令中,mov 指令是一个极其通用的操作。当你写 mov [rax+rdx], 0 时,CPU 产生歧义:
- 你是要存 1 个字节(byte)?
- 还是 2 个字节(word)?
- 还是 4 个字节(dword)?
- 还是 8 个字节(qword)?
CPU 不知道该怎么分配内存宽度。 如果它猜测错了,可能会覆盖掉周围不该覆盖的数据。因此,汇编编译器(Assembler)会直接报错,因为该指令的语义是不完整的(Ambiguous)。
mov qword ptr [rax+rdx], 0 :ok:
mov [rax+rdx], 0 :x:
跳转
| 分类 | 指令 | 条件(EFLAGS) | 含义 | 常用 Patch |
|---|---|---|---|---|
| 无条件 | JMP | 无 | 一定跳 | — |
| 相等类 | JE / JZ | ZF=1 | 相等 | ↔ JNE / NOP / JMP |
| 相等类 | JNE / JNZ | ZF=0 | 不等 | ↔ JE / NOP / JMP |
| 无符号 | JA | CF=0 & ZF=0 | > | ↔ JBE |
| 无符号 | JAE | CF=0 | ≥ | ↔ JB |
| 无符号 | JB | CF=1 | < | ↔ JAE |
| 无符号 | JBE | CF=1 | ZF=1 | ≤ | ↔ JA |
| 有符号 | JG | ZF=0 & SF=OF | > | ↔ JLE |
| 有符号 | JGE | SF=OF | ≥ | ↔ JL |
| 有符号 | JL | SF≠OF | < | ↔ JGE |
| 有符号 | JLE | ZF=1 | SF≠OF | ≤ | ↔ JG |
| 标志位 | JC | CF=1 | 进位 | ↔ JNC |
| 标志位 | JNC | CF=0 | 无进位 | ↔ JC |
| 标志位 | JO | OF=1 | 溢出 | ↔ JNO |
| 标志位 | JNO | OF=0 | 无溢出 | ↔ JO |
| 标志位 | JS | SF=1 | 负数 | ↔ JNS |
| 标志位 | JNS | SF=0 | 正数 | ↔ JS |
| 标志位 | JP | PF=1 | 奇偶 | ↔ JNP |
| 标志位 | JNP | PF=0 | 非奇偶 | ↔ JP |
| 特殊 | JECXZ | ECX=0 | ECX为0跳 | 改逻辑 / NOP |
| 特殊 | JRCXZ | RCX=0 | RCX为0跳 | 改逻辑 / NOP |
压缩指令
1.常见短指令
❑push/pop指令。
❑xor eax,eax,等价于mov eax,0,但是比mov eax,0短。
❑xchg eax,ebx,表示交换两个寄存器的值,这里是交换eax,ebx寄存器的值。
比如,看到mov eax,0指令,就要想到用xor eax,eax这种指令来替换它,这样可以节省出3字节的空间,用来放置修补需要的指令(mov eax,0占5字节,xor eax,eax只占2字节)。
2.合并指令
1 | lea rax ,[rbp+buf] |
缩为lea rsi , [rbp+buf]
这样就可以腾出3字节的空间(mov rsi,rax占3字节)。
常见漏洞
uaf
1.跳转到可执行段进行指针清空
0x15AC 前12字节改成48 C7 04 10 00 00 00 00 90 90 90 90
即mov qword ptr [rax+rdx], 0指针清空


这里放一个ciscncatchme的patch演示
2.直接重写free操作

1 | ; --- 第一部分:寻址与取值 (Locating the Pointer) --- |
1 | mov rax, qword ptr [rbp+var_4] ; 取idx |

strcpy
strcpy会把最后的/0也收进去造成of by null
把strcpy改成memcpy,按字节收入
strcpy(dest, src);
memcpy(dest, src, 3);

空间不足,让它到其它地方
1 | patch_stub: |
fmt
方法一
把printf改为puts,比较简单。但是puts函数与printf函数还是有一点点区别的,因为puts函数是自动在输出的字符串尾部加入一个回车符,在有些比赛的check脚本中是通过比较两次输入与输出是否全等,就会导致这种patch方法不能过关。
方法二
给printf函数添加参数
首先需要在.eh_frame写入参数%s
然后改变printf参数
改变前
改变后
整数溢出

n0x100是unsorted int,在malloc的时候会转化为int,若输入是一个负数会转为非常大的数
所以,漏洞是由于没有检查输入的size有负数的情况造成的。完成修补
有两个思路:
❑将判断改为size>0x100||size<0。
❑将size改为无符号数。
这里需要探讨的是jle这种指令的形式,可以简称为J指令。
J指令分为两种:
❑短跳转J指令。
❑长跳转J指令。
短跳转J指令只有2个字节。第1个字节为指令标识,表示什么情况下跳转,如大于、小于、等于等;第2个字节为跳转的偏移,这里的偏移也是下一条指令的地址到目标地址的偏移。
| 逻辑含义 | 有符号 (Signed / int) | 操作码 | 无符号 (Unsigned / uint) | 操作码 |
|---|---|---|---|---|
| 大于 / 高于 | JG (Greater) | $7F |
JA (Above) | $77 |
| 大于等于 / 不低于 | JGE (Greater or Equal) | $7D |
JAE (Above or Equal) | $73 |
| 小于 / 低于 | JL (Less) | $7C |
JB (Below) | $72 |
| 小于等于 / 不高于 | JLE (Less or Equal) | $7E |
JBE (Below or Equal) | $76 |
当把一个 32 位的值放入 64 位寄存器时,处理方式完全不同。
| 操作类型 | 有符号指令 (int -> long) | 功能描述 | 无符号指令 (uint -> ulong) | 功能描述 |
|---|---|---|---|---|
| 位数扩展 | MOVSXD / CDQE | 符号扩展:高位补符号位(正补0,负补1)。 | MOV (32位到64位) | 零扩展:高位直接补 0,不看正负。 |
虽然加减法指令(ADD/SUB)是通用的,但乘除法在底层是严格区分的。
| 运算类型 | 有符号指令 (Signed) | 解释 | 无符号指令 (Unsigned) | 解释 |
|---|---|---|---|---|
| 乘法 | IMUL | 考虑补码符号的乘法。 | MUL | 纯二进制数值乘法。 |
| 除法 | IDIV | 考虑补码符号的除法。 | DIV | 纯二进制数值除法。 |
| 右移 | SAR (算术右移) | 保留符号位。 | SHR (逻辑右移) | 高位补 0,不看符号。 |
把int改成unsorted int

通防
把文件放入该目录下
1 | python3 evilPatcher.py ./文件名 sandboxs/对应的asm脚本名 |
awdp赛后复盘
2024全国大学生信息安全竞赛(ciscn)半决赛华东北赛区Pwn
参考链接



