每次函数调用,都为函数开辟一块空间,称为栈帧。
我们应该知道,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),我们称为栈底指针,寄存器esp指向当前的栈帧的顶部(低地址),我们称为栈顶指针。
下面我们以一个简单的程序为例,分析一下函数的调用过程:
#include
int Add(int x, int y)
{
int ret = 0;
ret = x + y;
return ret;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%d\n", c);
system("pause");
return 0;
}
以下是这段代码的反汇编代码:
11: int main()
12: {
00FE1420 push ebp
00FE1421 mov ebp,esp
00FE1423 sub esp,0E4h
00FE1429 push ebx
00FE142A push esi
00FE142B push edi
00FE142C lea edi,[ebp-0E4h]
00FE1432 mov ecx,39h
00FE1437 mov eax,0CCCCCCCCh
00FE143C rep stos dword ptr es:[edi]
13: int a = 10;
00FE143E mov dword ptr [a],0Ah
14: int b = 20;
00FE1445 mov dword ptr [b],14h
15: int c = 0;
00FE144C mov dword ptr [c],0
16: c = Add(a, b);
00FE1453 mov eax,dword ptr [b]
00FE1456 push eax
00FE1457 mov ecx,dword ptr [a]
00FE145A push ecx
00FE145B call _Add (0FE10E6h)
00FE1460 add esp,8
00FE1463 mov dword ptr [c],eax
17: printf("%d\n", c);
00FE1466 mov esi,esp
00FE1468 mov eax,dword ptr [c]
00FE146B push eax
00FE146C push 0FE5858h
00FE1471 call dword ptr ds:[0FE9110h]
00FE1477 add esp,8
00FE147A cmp esi,esp
00FE147C call __RTC_CheckEsp (0FE1140h)
18: system("pause");
00FE1481 mov esi,esp
00FE1483 push 0FE585Ch
00FE1488 call dword ptr ds:[0FE9118h]
00FE148E add esp,4
00FE1491 cmp esi,esp
00FE1493 call __RTC_CheckEsp (0FE1140h)
19: return 0;
20: }
接下来我们要分析上面这一段反汇编代码:
首先要知道,连接器对控制台程序设置的入口函数是mainCRTStartup,mainCRTStartup 再调用main 函数;
所以当我们操作时,首先会给mainCRTStartup()函数开辟一段空间,然后让esp和ebp指向在这段空间的顶部和底部,然后_mainCRTStartup()函数又会调用mainCRTStartup()函数:
push ebp;//把ebp压入栈帧
mov ebp,esp ;//把esp给ebp
sub esp,0E4h //把esp向上生长0E4h
push ebx
push esi
push edi
lea edi,[ebp-0E4h]
mov ecx,39h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
/*3个push 分别把ebx,esi,edi 3个寄存器压入栈中。
lea 就是把 [ebp-4Ch]的地址放在edi中,ebp-0E4h是3个push之前esp的位置做2个move操作,ecx寄存器的值为39h,eax为初始化值00CCCCCCCCh
然后rep stos:实际上就是把初始化开辟的空间,初始值为eax寄存器内的值0CCCCCCCCh,
从edi开始(edi保存的esp的位置),向高地址的部分进行字节拷贝,每一次拷贝4个字节。
拷贝的内容就是eax的内容,拷贝次数为39h次。*/
实参入栈:
13: int a = 10;
00FE143E mov dword ptr [a],0Ah
14: int b = 20;
00FE1445 mov dword ptr [b],14h
15: int c = 0;
00FE144C mov dword ptr [c],0
形参入栈:
16: c = Add(a, b);
00FE1453 mov eax,dword ptr [b]
00FE1456 push eax
00FE1457 mov ecx,dword ptr [a]
00FE145A push ecx
函数调用指令:call
00FE145B call _Add (0FE10E6h)
00FE1460 add esp,8
Add函数调用过程:
5: int Add(int x, int y)
6: {
00FE13D1 mov ebp,esp
00FE13D3 sub esp,0CCh
00FE13D9 push ebx
00FE13DA push esi
00FE13DB push edi
00FE13DC lea edi,[ebp-0CCh]
00FE13E2 mov ecx,33h
00FE13E7 mov eax,0CCCCCCCCh
00FE13EC rep stos dword ptr es:[edi]
7: int ret = 0;
00FE13EE mov dword ptr [ret],0
8: ret = x + y;
00FE13F5 mov eax,dword ptr [x]
00FE13F8 add eax,dword ptr [y]
00FE13FB mov dword ptr [ret],eax
9: return ret;
00FE13FE mov eax,dword ptr [ret]
10: }
00FE1401 pop edi
00FE1402 pop esi
00FE1403 pop ebx
00FE1404 mov esp,ebp
00FE1406 pop ebp
00FE1407 ret
函数调用结束后,释放栈帧。