栈迁移
当我们的rop链过长时很可能栈空间不够,并且ebp之前的空间其实只是填充一些没什么用的数据
栈迁移机理
与传统的pop_ret类似,利用level_ret实现栈的迁移,都是寻找很小短的零碎代码,进行拼接,和拼积木很像。
level ret//拆解
mov ebp,esp//esp跟着ebp走
pop ebp //栈基指针的转移
pop eip//ret
示例
.data:0804A008 count dd 539h //1337用十六进制表示就是539h
当我们还不知道level_ret时,采用一般的rop必然要在回到输入点,就这道题就是(main函数),但发现main函数受到count的影响仅一次main(无法重复利用main),所以要换一种思路。那我们能否利用有效的代码段与一段可以容纳数据(在我们的脚本中的要发送的数据)构成类似栈的结构,答案当然是可以的
显然有read 函数,但是(0x40-0x28)真正的数据填充区域有限,可有read函数,就想能否将数据写入某个位置(比如.bss)read(0,buf,0x100)
| buf(ebp) | //pop数值到ebp,改变基址
| read(ret) | //pop到eip,执行read(0,buf,0x100)
| ret | //执行完read后返回,pop到eip
| 0 |
| buf |
| 0x100 |
这是一开始的栈的布局,看着很合理,但实际上还有缺陷,,比如执行完read后执行流终断,,因为.bss没有栈的pop的机制段,放在多数据却没法用。
| buf |//pop数值到ebp,改变基址
| read_plt |//pop到eip,执行read(0,buf,0x100)
| leave_ret |//pop到eip,执行leave_ret=>esp被代跑(触发)
| 0 |//此时栈指针都没了(栈已被移走;触发后)
| buf |
| 0x100 |
泄露地址
.bss | buf2 |//这里写buf2的原因是pop ebp, 改变基址
| puts_plt |//这里要打印泄露puts的装载地址,leave_ret 的ret那一步
| pop_ret |//puts(puts_got)后,pop到eip,执行
| puts_gots |//要泄露的参数值,之后pop掉
pop ret =》 | read_plt |//pop到eip,执行read(0,buf,0x100)
| leave_ret |//pop到eip,执行leave_ret=>esp被代跑(触发)
| 0 |//此时栈指针都没了(已被移走;触发后)
| buf |
| 0x100 |
调用system 函数
.bss | bbbb |//随便 改变基址pop ebp 那一步
| system |//leave_ret 的ret那一步
| cccc |//返回地址随便写
| binsh_addr |
上图仅供参考