Following images are from http://www.xuebuyuan.com/528715.html
In general, the process of calling function is put the parameters into the stack, move the EBP and ESP (jump into another function), protect the information of father function, restore the information of father function. (参数入栈、函数跳转、保护现场、回复现场)
There are three special pointer in compiler: EIP(Instruction pointer)是指令指针,point to the address of next instrction (即指向下一条即将执行的指令的地址);EBP (bottom pointer) 为基址指针,point to the bottom of stack 常用来指向栈底;ESP (stack pointer) 为栈指针,point to the top of stack 常用来指向栈顶.
For example, here is the code:
First, we are at the main function. Every function has its EBX (basic register),ESI,EDI (destination register)分别为基址寄存器,源变址寄存器,目的变址寄存器. They are some important information of a particular function. When we call the g_func, we firstly need to transport the
parameters into it. As I mentioned in my C++ note, when we are using the transportation of value, we will create copies of the parameters for the function. Therefore, here is the first step: put the parameters into the function stack.
三条push指令,分别将三个参数压入栈中,可以发现参数的压栈顺序是从右向左的。这时我们可以查看栈中的数据验证一下。如图3所示,从右边的实时寄存器表中我们可以看到ESP(栈顶指针)值为0x0012FEF0,然后从中间的内存表中找到内存地址0x0012FEF0处,我们可以看到内存中依次存储了0x00000001(即参数1),0x00000002(即参数2),0x00000003(即参数3),即此时栈顶存储的是三个参数值,说明压栈成功。
If we translate the code into instructions, we will get:
We firstly push three parameters into stack. Then we call a instruction at the address of 00401005. Following this address, we can see this:
We can see this is a jump instruction at the instruction address of 00401005. It jumps to 00401030. That is the begin instruction address of g_func.
Until now, we see how the instructions like before entering the instructions of function. Firstly push the parameters into stack, then use a call instruction to a jump instruction to jump to the actual instruction address of the function. So, until now,
the stack is like above. It has three parameters in it now.
Now, we turn to protect information before really entering function.
This step consists of three actual movement:
1)第一步的隐含的,并没有显式的指令去指挥其完成。就是将之前的call 指令之后EIP地址(main 函数的下一个指令的地址)压进去栈中。在这个例子中,call之后则为add。地址是00401093.
2) ebp is the original bottom pointer of the main function. 变了函数的话,栈底都是要变化的。我们要保存之前的函数的栈底地址,作为保护现场的第一步. 所以第二步就是将之前的函数的EBP压进栈中。
下一条mov ebp, esp 将此时的栈顶地址作为该函数的栈基址,确定g_func函数的栈区域(ebp为栈底,esp为栈顶)。再往下的指令是sub esp, 48h,指令的字面意思是将栈顶指针往上移动48h Byte。那为什么要移动呢?这中间的内存区域用来做什么呢?这个区域为间隔空间,将两个函数的栈区域隔开一段距离,如图7所示。而该间隔区域的大小固定为40h,即64Byte,然后还要预留出存储局部变量的内存区域。g_func函数有两个局部变量x和y,所以esp需移动的长度为40h+8=48h。
注意,存放局部变量的区域是64Bytes另外移出来的。并不会占用64Byte的空间。确保至少函数的栈空间之间至少有64Bytes的间隔。
移动之后的栈空间分布如图:
3) 将之前提到的EBX (basic register),ESI,EDI (destination register)分别为基址寄存器,源变址寄存器,目的变址寄存器压进去当前ESP的上面的地址。作为保存现场的第三步。
此时我们可以知道,栈内存是这样的:
The following content is talking about how to restore the information, I just directly copied form http://www.xuebuyuan.com/528715.html