标签:
上文讲到call之后,程序发生跳转。之后因为博主设错了一个断点,结果折腾了整整一周,真是欲哭无泪。
这才是正确的断点设置啊TAT。这是call之后的语句。
01281427 83 C4 08 add esp,8
执行call之后ESP的值减少4。在memory窗体中查看ESP的值。
由小端机存储可只栈顶正是函数的返回地址。
012813C0 55 push ebp
012813C1 8B EC mov ebp,esp
函数体开头两条指令是将EBP压栈,将ESP赋给EBP。
Intel几个通用寄存器的作用概括:
EAX — Accumulator for operands and results data
? EBX — Pointer to data in the DS segment
? ECX — Counter for string and loop operations
? EDX — I/O pointer
? ESI — Pointer to data in the segment pointed to by the DS register; source pointer for string operations
? EDI — Pointer to data (or destination) in the segment pointed to by the ES register; destination pointer for
string operations
? ESP — Stack pointer (in the SS segment)
? EBP — Pointer to data on the stack (in the SS segment)
因为ESP的值一直在变化,而函数参数的值和返回值又是根据栈地址的偏移量来计算的,所以需要EBP暂存ESP的值来寻址。
这两条操作后,栈的结构如下图所示:
之后我们来看函数如何取得形参的值。前文已经说过,在函数执行前参数的值已经压栈。观察接下来的指令:
mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0Ch]
在这两条指令之前并无对EBP的操作,所以参数的地址分别为EBP+8和EBP+c
而上述两条指令正是将两个地址处的值相加并传给EAX(正如EAX的作用)
那么函数如何返回值sum?观察下一条指令-
mov dword ptr [ebp-8],eax
EAX的值又传递给了EBP-8的地址。栈结构如图所示。
查看EBP-8的值,恰为1和2的和3
为什么要存在EBP-8而非EBP-4,原书上说是为了防止溢出攻击,我一个记笔记的当然不不明觉厉啦。先在此mark,以后学到相关内容再补充。
函数执行完毕后,当然需要返回喽。上文已经详细介绍了RET指令的过程。观察函数体部分对栈的操作。
push ebx
push esi
push edi
之后都有相对应的pop,所以最后ESP的指向恰和上图的ESP相同。之后pop EBP后,ESP恰好指在返回地址。RET指令返回,函数执行完毕。
执行完后的扫尾工作:
注意到call指令下面的那条指令:
add esp,8
RET作用之一是Pops the top-of-stack value (the return instruction pointer) into the EIP register.
所以RET之后,ESP+4处的1和ESP+8处的2仍然存在,而函数已经调用完毕。将ESP+8即标记1和2已经无用,实现栈回收。
接下来下面的操作如何获得返回值,以及RET特殊用法,release优化,将在下节讨论。
?
?
?
?
标签:
原文地址:http://www.cnblogs.com/puorc/p/4430503.html