栈迁移

栈迁移:一种精巧的栈空间操控技术

栈迁移是什么?

想象一下你的程序正在执行,突然发现当前的“工作台”(栈空间)太小了,放不下需要的工具和数据。这时候,栈迁移就像是为程序搬了个新家——把执行环境整体搬迁到更宽敞的地方,无论是bss段还是栈的其他区域都可以成为新的落脚点。

为什么需要栈迁移?

应用场景分析

当你遇到这两种情况时,栈迁移就派上用场了:

  • 空间严重不足:溢出的长度只够覆盖ebp,连返回地址都碰不到
  • 刚好但不够用:虽然能覆盖返回地址,但后面的参数区域太短,payload塞不进去

简单来说,就是原来的栈空间不够发挥,需要换个场地大展拳脚

技术核心

理解关键指令

栈迁移的魔法主要依靠leave;ret这个指令组合的两次精彩演出。

leave指令分解

1
2
mov esp, ebp    ; 让栈顶指针指向当前栈底
pop ebp ; 将栈顶内容弹出到ebp寄存器

这个过程相当于先确定新的工作基准点,然后更新基址指针。

ret指令作用

1
pop eip         ; 将栈顶内容作为下一条指令地址

这是程序执行流的转向器,决定了接下来要去哪里。

栈迁移实战解析

第一阶段:

首先通过溢出漏洞,我们做了两件关键事:

  1. 把ebp的内容替换成目标迁移地址
  2. 将返回地址覆盖为leave;ret的地址

此时main函数结束

第二阶段:

第一次leave;ret

  • leave执行mov esp, ebp让两个指针汇合,接着pop ebp时出现了转折——ebp被我们预设的迁移地址填充
  • ret执行pop eip将我们准备好的leave;ret地址装入,为第二次迁移做准备

为什么需要两次leave;ret?
因为main函数正常结束只会执行一次leave;ret,我们要实现完美迁移,就必须再来一次!

第三阶段:

第二次执行leave时,真正的迁移发生了:

  • mov esp, ebp:此时esp指向了我们设定的新地址
  • pop ebp:更新ebp,同时esp自动下移一个单元

最后,当ret执行时,栈顶的system函数地址被弹入eip,成功GetShell!

技术要点总结

栈迁移的精髓可以概括为:

  • 第一次leave;ret:把ebp“骗”到目标位置
  • 第二次leave;ret:让esp跟上脚步,同时为system调用铺好道路