final 40% 1.reverse 4 2.code auditing 4 3.pwn 4
hurdle:
顺序一:
顺序二:
什么是reversing? -阅读反汇编 -恢复C伪代码 -识别常见代码结构(条件、循环、switch) -判断变量类型 -找出bug(可能是一题)
patterns
compare-> conditional jump -> if compare + jump + backward jump -> while 连续的 mov -> 初始化数组 结构体字段通过固定偏移访问
结构正确 变量名有意义 逻辑清晰
评分标准: var_20 r8 v13
int i char *buffer struct node *next size_t size counter += 1
patterns
1.大多数计算都是在寄存器中发生的 mov eax, [rbp-0x4] add eax, 1 mov [rbp-0x4], eax i++
2.常用寄存器 RAX return value RDI RSI RDX RCX RSP stack pointer RBP frame pointer
mov dword ptr [rbp-0x14], edi int x = arg1
stack frame:
push rbp
mov rbp, RSP
sub rsp,
rbp-0x4, rbp-0x8 –> local variables rbp+0x8,rbp+0x10 –> caller’s frame
mov mov al, [x] char mov ax, [x] short eax, 4 bytes int rax, 8 bytes long pointer
mov al, [rbp-0x1] char flag int flag (x)
pointer dereference: mov rax, qword ptr [rbp-0x18] mov eax, dword ptr [rax+4]
struct something { int field0; int field1; }
struct something *obj; int x = obj->field1;
control flow:
if/ if-else
cmp eax, 5 jl .L1 … .L1:
if (x >= 5){ … }
cmp eax, ebx jne fail
if (a != b) goto fail
je jne jg jl jge jle
while
.Lloop: cmp eax, 0 je .Lend … loop body … jmp .Lloop .Lend:
while (x != 0){ … }
for
mov local_i, 0
i++ backward jump
mov dword ptr [rbp-4], 0 .Lcond: cmp dword ptr [rbp-4], edi jge .Lend … body … add dword ptr [rbp-4], 1 jmp .Lcond .Lend:
for (int i=0; i < arg1; i++){ … }
switch-case:
Case A:连续小整数 (jump table) cmp eax, 5 ja default jmp qword ptr [rax*8 + table]
switch(x){ case 0: … case 1: … case 2: … }
Case B:稀疏整数 (多个 cmp + jmp) cmp eax, 4 je L4 cmp eax, 100 je L100 …
char short int long pointer
mov al, [rbp-0x1] mov eax, [rbp-0x4] mov rax, [rbp-0x18]
1.mov指令宽度决定变量类型 al char ax short eax int rax long/pointer
mov rax, qword ptr [rbp-0x18] void *ptr
2.指针dereference的识别
mov rax, qword ptr [rbp-0x18] ;ptr mov eax, dword ptr [rax+4] ;ptr->field1
3.结构体的汇编证据 3.1 固定大小的对分配 mov edi, 32 call malloc
3.2 多个字段通过固定偏移访问 mov rax, qword ptr [rdi] ;field0 mov eax, dword ptr [rdi+0x8] ;field1 mov byte ptr [rdi+0xC], 1 ;field2
struct X { long field0; int field1; char flag }
4.数组识别 mov eax, [rbp-0x20 + rdx*4]
数组访问 pattern = base + index * element_size
rdx elsement_size [rbp-0x20]
movsx eax, byte ptr [rbp-0x20 + rdx] char arr[]
mov eax, dword ptr [rbp-0x20 + rdx*4] int arr[]
5.动态数组 vs 静态数组 静态数组: sub rsp, 0x40 mov [rbp-0x40 + rdx*4]
动态数组:malloc | calloc mov edi, rax ;size call malloc
gdb b func run stepi , ni info registers x/20gx addr continue
IDA
int counter int index struct node *next char flag int len
技巧: 1.识别函数的基本结构 2.识别控制流 3.识别关键变量
add dword ptr [rbp-4], 1 i++
4.推测语义
5.组合成伪代码 if(…){
}else{
}
for(…){
}
struct node { int val; struct node *next;
}
bug finding: out-of-bounds integer overflow pointer dereference -> null return
code auditing
明显的漏洞 (buffer overflow, 格式化字符串…) subtle vulnerabilities (整数溢出, 类型转换错误…) 逻辑漏洞(missing checks, 错误的return) API misuse (不检查返回值, 用错大小…)
常见漏洞类型 一.BAD API Usage gets()
char s[4] gets(s)
2.strcpy()
char s[4] strcpy(s, argv[1])
3.strncpy()
strncpy(buf, input, 16); printf("%s\n", buf)
null-byte
4.printf(argv[1]) - fmt printf(argv[1])
%s %n %x
任何用户输入直接传给printf,不带格式化字符串,就是 format string attack
5.memset/memcpy/memmove 参数反了
memset(void *s, int c, size_t n);
memset(p, 100, 0) memset(dest, size, ‘A’)
memset 2
二. Integer Overflow / Underflow
int len = header + size; if (len > buffer_size) return; read(fd, buf, len);
len 负数(signed int overflow)
signed -> unsigned int len = INT_MAX len += 10; //overflow, len becomes negative read(fd, buf, len); //3 size_t (unsigned)
0xfffffffffffffffc
越界 覆盖整个堆/栈 RCE
任何 signed int 序列最后传给 size_t 的 API, 都是有风险。
read write malloc memcpy calloc(double-multiply overflow)
类型截断问题
整数被转换成short
int become_user (int uid); short assume_priv(shor uid);
uid=0x10000000 传给short -> 截断: 0x0000
short 0 -> root -> 提权
Array Out-of-Bounds 越界访问
固定长度数组写越界 for循环上界错误 通过整数溢出绕过边界检查
char buf[16] for (int i=0; i<=16; i++){ buf[i] = input[i]; }
HEAP BUGS (UAF, Double Free) use-after-free double free
free(ptr); free(ptr); //double free
Logic Bugs (逻辑漏洞)
1.忘记检查malloc
2.忘记 return/ break/ early exit
if(x<0>) free(buf); //forgot return buf[0] = ‘A’
3.Error path 返回了错误的状态 if(len < 0) return 1; if(len > max) return 1;
process(buf, len); return 0;
4.检查顺序不对: TOCTOU
if(!exists(“file”)){ do_stuff(); } -> 修改文件 race condition open(“file”)
Race Condition (竞态条件) 1.检查某个资源 2.进行一些操作 3.再次使用相同资源
if(!access(filename)){ //ToC do_something(); //ToU f=open(filename); }
check 和使用 use 之间有时间差,可以被攻击者利用
Code Auditing 实战策略 1.大框架:Top-down vs Bottom-up Top-down(从main开始) Bottom-up(从输入开始)
高危函数 上下界 i <= size i < length index
类型转换 signed unsigned long int int short
hidden bug