^^^^^^^^^^^开始调用函数^^^^^^^^^^^^^^^^^^^^^^^
A 每次调用函数 都在caller里 从右到左压入参数
(返回值直接在eax里 不会入栈 如果没有返回值 就异或自己 从而取0)
B 然后压入返回地址(函数的下一个指令的地址)
是返回caller的地址
^^^^^^^^开始进入子栈帧^^^^^^^^^^^^^^^^^^^^^^^^^^
C push老的ebp
D 将老caller的esp赋给新callee的ebp
这样 callee的ebp指向caller 的ebp
E esp-一个值得到新的esp
现在 esp与ebp之间的空间用来存储局部变量
之后的一些新的东西 新的压入stack的东西
都是往后面(地址更小的方向)压入
F 开始压入一些奇怪的寄存器的量
执行函数
F弹出到奇怪的寄存器
D 将callee的ebp赋给callee的esp
这一步 将callee的栈帧全部放弃
只有这个时候 两者才会相等
一起指向老caller的ebp
C 弹出老caller的ebp给新callee的esp
esp回退一个
ebp回到caller
内存的栈帧指针完全回到jump之前的状态
但是eip还不是
B ret汇编语言把返回地址弹出到eip
所以 返回地址是caller通过call汇编语句压入栈中 但是是通过callee来弹出的
eip回到call之前的状态
^^^^^回到caller^^^^^^^^^^^
A esp直接加一个量 把形式参数赶出去
^^^^^^^^^^^^^^^小结
小结: 出栈主要就是移动esp 让那个值不在esp和ebp之间 就不在栈中了
移动esp有两种方式
pop和add
pop指令先把值放到寄存器 然后移动esp(如果先移动esp然后再放值是不行的)
add则是直接移动esp 不管值了
ebp只有一个地方会和esp相等 那就是callee刚刚开始的时候 压入ebp之后 会令新的ebp等于老的esp
eip就是pc 指向的是下一条指令的地址
巧妙的是 debug调试的时候黄色箭头指向的也是下一个运行的指令 所以两者是一样的
[ebp-4]
中括号是说明这是一个地址
至于是取出地址存储的东西还是取地址的值 那就要看指令是什么样子的了