文章目录
1. 函数栈帧
我们知道寄存器有eax,ebx,ecx,edx,并且还有ebp,esp。其中ebp,esp这两个寄存器存放的是地址,这两个地址是用来维护函数栈帧的。
我们知道,每一个函数调用,都要在栈区创建一个空间。
举个例子:
我们知道main函数也会开辟自己的栈帧:
那么main函数被调用了,是被谁调用了呢?
在VS2013中,main函数是被__tmainCRTStartup调用的,而__tmainCRTStartup是被mainCRTStartup调用的。所以,__tmainCRTStartup和mainCRTStartup也会被创建函数栈帧。
下面我们就开始详细讲解上面函数创建的过程:
在main被调用前,有esp和ebp来指向这个函数的栈帧。下面我们来看一下这个例子的反汇编:
第一步将寄存器ebp给push到main函数的栈帧中:
那么压栈是从高地址到低地址的,就说明esp的地址会从高变低:
第二步的意思是:把esp的值赋值给ebp:
第三步给esp减去一个0E4h的值:
这段空间就是给main函数申请的空间。后面三步是push三个值进去。
lea的意思是:加载有效地址。这个步骤的意思是:将后面的值赋给epi。后面两个步骤也是赋值。rep stos意思是:从edi里面地址开始的39h个数据全部变成eax里面的数据。
然后将10放到ebp-8的位置上,把20放到ebp-14h上,把0放到ebp-20h上。
下面我们就需要进行函数调用的传参:
这里首先把20放到eax中,把10放到ecx中。call就是去调用这个函数,并且会把下一个指令的地址压栈,因为当我们调用函数时会再回来往下执行。
然后我们进入到函数当中:
第一步是将ebp给push到栈里,然后将esp赋值给ebp,最后将esp减0CCh数据,这三步是给Add函数开辟函数栈帧。然后将三个寄存器压栈,初始化Add函数的栈帧为0CCCCCCCCh。
然后我们将ebp-8的位置上设置为0,ebp+8上面说过放的是10,ebp+0Ch放的是20。Add的意思是:在eax寄存器中加上20。最后将eax中的值放到ebp-8地方,也就是z处。
这里是先压栈的b,然后压栈的是a。所以函数在传参是时候是从右向左传的。
把z通过寄存器返回后,pop出edi,esi,ebx。
然后将ebp赋值给esp:
这样Add函数栈帧就销毁了。最后再pop掉ebp,就弹出了main函数的ebp了。此时ebp和esp就重新指向main函数的栈帧了。
Add函数结束后,将esp地址加8。意思是:把两个形参销毁了。
最后的mov是把寄存器里的值赋给c。