程序员的内功修炼——深入理解函数栈帧( 三 )


看图顶部地址就是call指令的下一条地址 。那为什么要记住这个地址呢?
因为我们接下来就进入到了Add函数当中了 , 我们进入Add函数之后总要回来吧 , 这个时候就是方便函数调用完毕后回来 。
接下来就要按F11真正进入到了Add函数当中:
int Add(int x, int y){00553CD0pushebp00553CD1movebp,esp00553CD3subesp,0CCh00553CD9pushebx00553CDApushesi00553CDBpushedi00553CDCleaedi,[ebp+FFFFFF34h]00553CE2movecx,33h00553CE7moveax,0CCCCCCCCh00553CECrep stosdword ptr es:[edi]int z = 0;00553CEEmovdword ptr [ebp-8],0z = x + y;00553CF5moveax,dword ptr [ebp+8]00553CF8addeax,dword ptr [ebp+0Ch]00553CFBmovdword ptr [ebp-8],eaxreturn z;00553CFEmoveax,dword ptr [ebp-8]}
这个时候我们再看代码是不是就轻松多了?前面的几行代码和main函数中的很像 , 其实这就是在为Add函数创建函数的栈帧:
00553CD0pushebp00553CD1movebp,esp00553CD3subesp,0CCh00553CD9pushebx00553CDApushesi00553CDBpushedi00553CDCleaedi,[ebp+FFFFFF34h]00553CE2movecx,33h00553CE7moveax,0CCCCCCCCh00553CECrep stosdword ptr es:[edi]
画图:
int z = 0;00553CEEmovdword ptr [ebp-8],0z = x + y;00553CF5moveax,dword ptr [ebp+8]00553CF8addeax,dword ptr [ebp+0Ch]00553CFBmovdword ptr [ebp-8],eax return z;00553CFEmoveax,dword ptr [ebp-8]
接下来的代码就是先将z的值放入Add函数当中 。再将ebp+8的值放入寄存器eax中 , 再将ebp+0ch的值加入到函数eax中 , 那ebp+8和ebp+0ch的值是多少呢?
通过上面的图我们清晰的知道 , 分别是20  , 10 , 也就是找到了形参的位置并将其加起来放入到了寄存在eax中 , 现在eax的值就变成了30 。最后一条指令就是将eax中的值放入ebp - 8 中 , 而ebp - 8 的值就是 z 所在的位置 。我们要清楚的就是 , eax寄存器不会因函数栈帧的摧毁消失而消失 , 它会一直存在 。
00553D01popedi00553D02popesi00553D03popebx00553D04movesp,ebp00553D06popebp00553D07ret
接下来的练习的三个pop就是 , 出栈将栈顶三个元素直接弹出 。然后再将ebp的地址给esp , 在弹出ebp , 我们画图来看:
有人就会问为啥ebp就跑到下面去了呢?
因为我们开始push了一下ebp地址 , 然后我们直接pop弹出ebp的时候 , ebp就直接找到了main函数的栈顶 , 然后esp就顺势的指向了开始我们留下来的那个地址 。
现在的栈顶有一个call指令的下一条指令 , 我们ret这个地址的时候 , 我们就是直接走到了call指令的下一条指令了 。
所以在原先在栈顶push一个call指令的下一条指令的地址 , 就是为了让我们的代码得到连续 。就是让我们的代码不仅走得出去 , 还能走得回来 。
最后两条代码(add和mov)
00553B80addesp,800553B83movdword ptr [ebp-20h],eax
先将esp的地址加上8 , 再将eax的值放入ebp - 20h(c)中 。
我们所有的汇编代码就完了 , 最后将c中的值打印出来(也是一个函数 , 有兴趣的朋友可以自己去看汇编代码) 。
最后的图:
至于后面main函数的释放 , 有兴趣的朋友可以试试 。
最后谢谢大家 , 有错误希望大家及时指正!!一起加油!!!!