一.環境:
x86/WinXP/VC 6.0
二.用例:
int swap(int a, int b)
{
int v;
v = a;
a = b;
b = v;
return v;
}
void main(void)
{
int a = 7;
int b = 10;
int c = 0;
c = swap(a,b);
return;
}
三.分析:
1: int swap(int a, int b)
2: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,44h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
3: int v;
4: v = a;
00401038 mov eax,dword ptr [ebp+8]
0040103B mov dword ptr [ebp-4],eax
5: a = b;
0040103E mov ecx,dword ptr [ebp+0Ch]
00401041 mov dword ptr [ebp+8],ecx
6: b = v;
00401044 mov edx,dword ptr [ebp-4]
00401047 mov dword ptr [ebp+0Ch],edx
7: return v;
0040104A mov eax,dword ptr [ebp-4]
8: }
0040104D pop edi
0040104E pop esi
0040104F pop ebx
00401050 mov esp,ebp
00401052 pop ebp
00401053 ret
swap函數內部參數處理:
1.將基址指標EBP壓棧;
2.將堆棧指標ESP拷貝一份到EBP中;
3.將ESP值減0x44(為了將這68個位元組填充為0xCC);
4.將EBX/ESI/EDI壓棧;
5.將EBP-0x44的地址位移量存到目標地址指標EDI;
6.將計數器ECX置為0x11(即17個整數);
7.將EDI所開始的大小為17的記憶體空間填充為0xCCCCCCCC;
8.取出堆棧中被壓的a的值(EBP+8所存的值)放到EAX中;
9.將EAX中的值(參數a的值)賦給到v;
10.取出堆棧中被壓的b的值(EBP+0CH所存的值)放到ECX中;
11.將ECX中的值(參數b的值)賦給到a;
12.取出堆棧中被壓的b的值(EBP-4所存的值)放到EDX中;
13.將EDX中的值(變數v的值)賦給到a;
14.將傳回值(v的值)拷貝到EAX中;
15.恢複EDI/ESI/EBX;
16.恢複ESP為EBP(即進入swap時的ESP);
17.恢複EBP為調用swap前的值;
18.swap函數返回;
10: void main(void)
11: {
00401070 push ebp
00401071 mov ebp,esp
00401073 sub esp,4Ch
00401076 push ebx
00401077 push esi
00401078 push edi
00401079 lea edi,[ebp-4Ch]
0040107C mov ecx,13h
00401081 mov eax,0CCCCCCCCh
00401086 rep stos dword ptr [edi]
12: int a = 7;
00401088 mov dword ptr [ebp-4],7
13: int b = 10;
0040108F mov dword ptr [ebp-8],0Ah
14: int c = 0;
00401096 mov dword ptr [ebp-0Ch],0
15: c = swap(a,b);
0040109D mov eax,dword ptr [ebp-8]
004010A0 push eax
004010A1 mov ecx,dword ptr [ebp-4]
004010A4 push ecx
004010A5 call @ILT+5(_swap) (0040100a)
004010AA add esp,8
004010AD mov dword ptr [ebp-0Ch],eax
16: return;
17: }
004010B0 pop edi
004010B1 pop esi
004010B2 pop ebx
004010B3 add esp,4Ch
004010B6 cmp ebp,esp
004010B8 call __chkesp (004010e0)
004010BD mov esp,ebp
004010BF pop ebp
004010C0 ret
15: c = swap(a,b);
0040109D mov eax,dword ptr [ebp-8]
004010A0 push eax
004010A1 mov ecx,dword ptr [ebp-4]
004010A4 push ecx
004010A5 call @ILT+5(_swap) (0040100a)
004010AA add esp,8
004010AD mov dword ptr [ebp-0Ch],eax
分析函數swap調用過程:
1.將參數值b拷到EAX;
2.將EAX值壓棧;
3.將參數值a拷到ECX;
4.將ECX值壓棧;
5.CALL swap函數
6.將堆棧指標加8,丟棄堆棧中a,b的值;
7.將EAX中的傳回值拷貝到變數c;
調用swap時的堆棧情況:
0012FEC8 | EDI |<-- ESP (執行到 v = a語句時堆棧指標)
0012FECC | ESI |
0012FED0 | EBX |
0012FED4 | 0xCCCCCCCC |
| . |
| . |
| . |
0012FF14 | 0xCCCCCCCC |
0012FF18 | EBP |<-- EBP (執行到 v = a語句時堆棧指標)
0012FF1C | 0x004010AA | 執行完調用swap後的返回地址
0012FF20 | ECX | a的值
0012FF24 | EAX | b的值
四.總結:
1.實參壓棧過程是從右至左,然後壓入函數調用後的返回地址;
2.被調用的函數從堆棧中取實參值;
3.如果採用編譯器進行最佳化,那麼被調用的函數內部是從寄存器還是從堆棧中取實參值?具體情況根據CPU架構和參數個數有關(可以繼續分析哦!);
4.如果函數參數個數超過較多(如果超過5個參數),函數參數如何傳遞呢?
5.函數的傳回值一般會存在一個寄存器,如EAX.