1.函数调用机制

关键寄存器比较:

x86-64 mips32 arm32
传参寄存器 rdi,rsi,rdx,rcx、r8、r9 $a0 到 $a3 r0 到 r3
栈顶栈底 rbp,rsp $fp,$sp r7,sp
保存返回地址 无,保存在栈上 $ra lr(r14)
指令指针 rip $pc pc(r15)

跳转函数指令比较:

x86-64 mips32 arm32
跳转指令 call fun; jal fun; bl fun;
特点 将下一条指令的地址压入栈中 将下一条指令的地址存入ra 将下一条指令的地址存入lr

但是,由于mips分支延迟效应,遇到jl,jalr,程序会优先执行后面代码再执行跳转指令:会先执行addiu,再执行jr跳转

1
2
jr $ra
addiu $sp, 0xE8

函数内部指令比较:

x86-64:
  fun:  
    push rbp            ; 保存旧的基址指针  
    mov rbp, rsp        ; 设置新的基址指针  
    sub rsp, 16         ; 为局部变量分配空间  
	; 函数体(执行计算等)  
	...  
 
	leave               ; 恢复基址指针  
	ret                 ; 返回
mips32:
foo:  
    addi $sp, $sp, -12   # 栈上分配12字节空间
    sw $ra, 4($sp)      # 返回地址保存到$sp+4位置  
    sw $fp, 0($sp)      # $fp保存到$sp  
    move $fp, $sp       # 设置新的基址指针  
    addi $sp, $sp, -16  # 为局部变量分配空间 
    高地址
            +----------------------+
0x7ffffff0: |  调用者的栈帧         |
            +----------------------+
0x7fffffe8: |  (预留)               |
0x7fffffe4: |  $ra = 0x80001234     |
0x7fffffe0: |  $fp = 0x7fffff00     |   <- $fp
0x7fffffd0: |  局部变量 (16B)       |   <- $sp
            +----------------------+
低地址
	# 函数体(执行计算等)  
	...  
 
	move $sp, $fp       # 恢复栈指针  
	lw $fp, 0($sp)      # 恢复旧的基址指针  
	lw $ra, 4($sp)      # 恢复返回地址  
	addi $sp, $sp, 8    # 释放栈空间  
	jr $ra              # 返回
arm32:
foo:  
    sub sp, sp, #8      ; 为保存旧的基址指针分配空间  
    str lr, [sp, #4]    ; 保存返回地址  
    str r7, [sp]        ; 保存旧的基址指针  
    mov r7, sp          ; 设置新的基址指针  
    sub sp, sp, #16     ; 为局部变量分配空间  
	; 函数体(执行计算等)  
	...  
 
	add sp, sp, #16     ; 释放局部变量空间  
	ldr r7, [sp]        ; 恢复旧的基址指针  
	ldr lr, [sp, #4]    ; 恢复返回地址  
	add sp, sp, #8      ; 释放栈空间  
	bx lr               ; 返回  

执行返回地址内容时,要注意此时sp的位置,一般来说,sp此时已经来到了返回地址在栈上位置的上方

2.栈溢出利用

算溢出长度

x86-64 mips32 arm32
找buf和ebp的偏移 找buf和$ra寄存器的偏移(确切地说是$ra压入栈中的位置) 找buf和lr寄存器的偏移(同mips)

ret2system的常用gadget

system(“/bin/sh”)
x86-64 mips32 arm32
pop_rdi_ret addiu a0,sp,0x10; lw t9,sp;jalr t9; pop{r0,pc}
ROPgadget –binary pwn –only “pop 一般用ida插件mipsrop 常用ropper工具
其他

arm架构:

pop{r0,pc}:将sp+0地址给r0,sp+4给pc;

若找不到,可以用 pop_r3_pc 和 mov_r0_sp;blx_r3这两条gadget实现跳转 ,如图:

img

把system的地址放在r3寄存器,后续直接跳转到r3寄存器,并且,跳转之前直接可以给r0传参

mips架构:

一个函数开头中(对比上文提到的函数内部指令比较),经常这样把寄存器内容保存到栈上,除了ra,fp以外,还有s0-s7等寄存器

img

在函数结尾,又会把栈上内容取回到原来的寄存器中,如果覆盖到ra,这是我们完成劫持返回地址的原理,同时如果覆盖s0到s7,那就相当于控制了s0-s7

img

s0-s7能提前控制的话,那么就可以使用这样的gadget,比如

img

在s0处布置一个system即可

img