本系列是 x86 架构平台上 trampoline 的实现,从原理和实现上进行了详细的介绍。


书接上回,使用 fexit 的时候,为什么在 ret 前增加 add $0x8,%rsp 指令就能跳过执行原来的函数?

call

学习一下几条常用指令:

  • call %N: 相当于 push %eip + mov %N, %eip
  • leave: 相当于 mov %ebp, %esp + pop %ebp
  • ret: 相当于 pop %eip

push and pop

看下执行内核 inet_csk_complete_hashdance() 函数、和 attach 到上面的 fexit trampoline 时,寄存器和栈的变化。

0,因为 call inet_csk_complete_hashdance(),所以栈上存有一个旧的 %eip 值。

  • call %0xffffffff9eda55a0 相当于 push %eip + mov %0xffffffff9eda55a0, %eip

1,call %0xffffffffc0431000

  • call %0xffffffffc0431000 相当于 push %eip + mov %0xffffffffc0431000, %eip

此时,栈上保存了两个连续的 %eip 值。

2,执行完 fexit trampolineprologue 后。

  • push %rbp
  • mov %rsp, %rbp
  • sub $0x40, %rsp

3,执行 epilogue 里的 leave 后。

  • leave 相当于 mov %ebp, %esp + pop %ebp

4,执行 epilogue 里的 add $0x8, %rbp 后。

  • add $0x8, %rbp

丢弃栈上保存的 %eip0xffffffff9eda55a5,就不会继续执行原来的函数。

5,执行 epilogue 里的 ret 后。

  • ret 相当于 pop %eip

总结

经过几张寄存器与栈状态变化的图片,便可知道在 leaveret 之间增加 add $0x8,%rsp 指令就能跳过执行原来的函数。

学习汇编、机器指令时,画图分析寄存器和栈的变化,或者通过可视化工具将寄存器和栈的变化呈现出来,才是正确的学习方法。