stack_migration

[TOC]

栈迁移

栈迁移的核心是leave_ret指令,要清楚leave_ret指令的作用

  • 栈迁移:rbp覆盖为我们想要转移到的地址-8(注意有个pop指令,因此要减8)(因为程序结束本来就有个leave_ret,再加上我们的leave_ret,最终结果就是让rsp=rbp覆盖的值+8),ret覆盖为任意一个leave_ret指令的地址即可。进行栈迁移的时候最好动笔写一写看看rbp,rsp的值到底变成了多少就知道payload写没写对
  • 栈迁移要注意这个rbp改变后,read函数还是根据rbp-buffer来确定rsi,因此通过栈迁移可以实现任意写

伪栈帧

  • 伪栈帧的核心就是找到一块可写的位置写入我们的rop链,再将rip指向这个地方开始执行。一般这个位置就是bss段后面一点的位置,最好后面多一点,不然可能覆盖了原本有用的地方让程序异常终止
  • buffer padding|fake rbp|leave ret addr|
  • 构造伪栈帧的时候要想清楚返回地址应当覆盖为什么,有时候是main中的read函数,有时候是leave_ret,可以多次调用main中的read,只要我们设计好这个伪栈帧,控制好rbp和rsp的值,就可以随意地控制程序的流程
  • 控制rsp有很多种方式,比如push pop leave ret指令,但是leave指令很特殊,它可以控制rbp的值,特别是我们的read函数一般都是read(0,rbp-0xc0,0x100),我们要清楚我们向我们构造的伪栈帧哪个位置read写入rop链
  • 向bss写入rop时要控制rsi,有多种控制rsi方式:1.如果给的可溢出的空间太小,一般通过mov rax,[rbp-buf]来控制,也就是我们覆盖rbp的值通过leave中的push rbp来实现 2.直接通过libc中的gadget来控制,如图可以用pop rax来控制

构造伪栈帧最好画出我们构造的rop链的流程图,可以清楚地看见rop链执行到何处。同时要注意我们rop链中rsp的变化,因为我们的ret指令依赖于rsp,这也是控制的核心 画出构造出的伪栈帧方便看rsp的变化 引起rsp变化的有 1.add rsp,8这种直接变化的 2.push pop指令 3.ret指令(这个很容易忽略),注意leave_ret指令

一些细节

如果在bss段构造伪栈帧,最好把偏移弄大一点,不然有时候会写到不该访问的地址,造成segmentfault