(之前写的没保存,这是第二遍写,我要杀人...)

# 栈的结构

# 栈迁移的应用场景

栈迁移主要是为了解决栈溢出空间大小不足的问题,当我们的ROP链过长时很可能栈空间不够,并且ebp之前的空间其实只是填充一些没什么用的数据,所以需要一个新的地址空间来存放当前的payload

我们的栈

# 1. leave和ret命令

leave:

mov  esp , ebp
pop   ebp       #执行pop时,ebp出栈,并且将esp地址内的值放入ebp

这里要注意 mov esp,ebp是将ebp的地址付给esp,也就是说将esp从栈顶拉下来到ebp的位置,pop ebp是将esp内部的值给ebp

ret:

pop eip  #将esp的值放入eip,让eip寄存器去执行,并且esp+4

# 2.栈迁移

首先要利用栈迁移需要通过溢出来改写部分数据,将ebp的值改为伪造的栈的栈顶,ret一般改到read处来改写伪造的栈的数据【使伪造的栈的栈顶(fake_esp)存入fake_ebp的栈低】

栈迁移要使用两次leave_ret来转移栈

# 第一次leave_ret(来源于ebp后的ret)

  1. leave:

    mov esp , ebp //将ebp的地址赋给esp,相当于将esp指向ebp相同的地方

pop ebp //将esp指向的地方的值给ebp,这里是值,值!(等于将fake_esp的地址给了ebp),然后esp+4

第一次写的时候有个疑问,第一次的leave_ret到底用的是谁的leave_ret ,如果是ebp后的ret的,那么为什么后面还要在ret后加一个leave_ret,这样加上read里面的leave_ret相当与3个了;而如果用的是read内的leave_ret那不是会将esp拉到伪造的ebp处吗,一个ret的地址就执行了两个leave_ret

我自己的理解:调用的read会在开辟一个新的栈帧,它的内部的leave_ret不会干扰外面栈的寄存器,所以等于使用的是ebp后面的ret

2.ret

pop eip //将这里esp的值给eip,让eip去执行`read`,然后esp+4

# 第二次调用leave_ret(来源于leave_ret)

【这里我们迁移过去要看看我们输入的值是在哪个地址,有时不是在迁移过去的起始处输入的】

  1. leave

    mov esp,ebp //将ebp的地址给esp,这次使esp指向fake_esp

    pop ebp //将esp内的值给ebp(fake_ebp的地址给ebp)

  2. ret

    pop eip //将system_plt给eip让它去执行,这里就可以getshell了,然后eip+4 ,【这里也意味着我们要将system放在fake_ebp+4的地方】

这里我们也可以看见直接用ret执行了命令,这相当于我们平常栈溢出利用的ret,通过这种方式我们可以在没有执行权限的地方来构造我们的getshell代码来执行(但是不能执行shellcode,执行方式不同)

这样我们就完成了栈迁移