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

# 栈迁移的应用场景
栈迁移主要是为了解决栈溢出空间大小不足的问题,当我们的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)
-
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)
【这里我们迁移过去要看看我们输入的值是在哪个地址,有时不是在迁移过去的起始处输入的】
-
leave
mov esp,ebp //将ebp的地址给esp,这次使esp指向fake_esp

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

-
ret
pop eip //将system_plt给eip让它去执行,这里就可以getshell了,然后eip+4 ,【这里也意味着我们要将system放在fake_ebp+4的地方】
这里我们也可以看见直接用ret执行了命令,这相当于我们平常栈溢出利用的ret,通过这种方式我们可以在没有执行权限的地方来构造我们的getshell代码来执行(但是不能执行shellcode,执行方式不同)
这样我们就完成了栈迁移