明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

Win32程序函數(shù)調(diào)用時堆棧變化情況區(qū)分

[摘要]在經(jīng)典的匯編語言教程中,函數(shù)調(diào)用時堆棧的使用都是著重講解的問題。如今隨著高級語言的越來越完善,單純使用匯編開發(fā)的程序已經(jīng)不多了。但對函數(shù)調(diào)用時堆棧動向的了解仍有助于我們明晰程序的執(zhí)行流程,從而在程序...
    在經(jīng)典的匯編語言教程中,函數(shù)調(diào)用時堆棧的使用都是著重講解的問題。如今隨著高級語言的越來越完善,單純使用匯編開發(fā)的程序已經(jīng)不多了。但對函數(shù)調(diào)用時堆棧動向的了解仍有助于我們明晰程序的執(zhí)行流程,從而在程序編寫和調(diào)試的過程中有一個清晰的思路。

一.調(diào)用約定
在Win32中,有關(guān)函數(shù)的調(diào)用主要有兩種約定。
1._stdcall
        以__stdcall方式調(diào)用的函數(shù)有以下特征:
    •  參數(shù)由右至左壓棧
    • 調(diào)用返回時,堆棧由被調(diào)函數(shù)調(diào)整
2.__cdecl
__cdecl約定是C/C++函數(shù)的默認(rèn)調(diào)用約定。它有以下特征:
    • 參數(shù)由右至左壓棧
    • 調(diào)用返回時,堆棧由調(diào)用者調(diào)整

二.Win32函數(shù)調(diào)用過程

1.    壓入?yún)?shù)
這里依據(jù)以上的調(diào)用方式將調(diào)用者給出的參數(shù)一一壓入堆棧。

2.    壓入斷點
當(dāng)程序執(zhí)行到Call指令的時候,當(dāng)前語句的地址作為斷點地址壓入堆棧。

3.    跳轉(zhuǎn)
eip的值被重新設(shè)置為被調(diào)函數(shù)的起始地址。

4.    mov ebp, esp
這里ebp被用來在堆棧中尋找調(diào)用者壓入的參數(shù),同時作為調(diào)用者堆棧指針的一個備份。在此前還應(yīng)該執(zhí)行一條:
push ebp
把ebp中原來的數(shù)值保存。

5.    sub esp,N
這里N是函數(shù)內(nèi)局部變量的總字節(jié)數(shù)加上一個整數(shù),一般為40。此后esp即為被調(diào)函數(shù)的堆棧指針了。

6.    初始化esp ~ esp-N之間的N字節(jié)空間
這是對堆棧中已分配給局部變量使用的內(nèi)存空間的初始化,一般全部設(shè)置為0xcc。

7.    順序執(zhí)行函數(shù)內(nèi)語句。
此時函數(shù)的堆棧位于所有局部變量的內(nèi)存空間之后,二者之間一般有40字節(jié)的隔離帶。

8.返回
為保障調(diào)用的正常返回,函數(shù)內(nèi)應(yīng)當(dāng)保證規(guī)范使用堆棧,使即將返回的時候esp的值恢復(fù)為執(zhí)行第一條語句前的狀態(tài)。說明白點,就是每一條push都要有相應(yīng)的pop。
調(diào)用返回的過程如下:
mov esp, ebp
執(zhí)行后,esp恢復(fù)為調(diào)用者的堆棧指針,棧頂除斷點地址外,還存有原ebp的值和調(diào)用時壓入的參數(shù)。
然后依次彈出ebp的值和斷點地址。如果是__cdecl約定則直接返回調(diào)用者,調(diào)用者將負(fù)責(zé)調(diào)整堆棧,丟棄調(diào)先前壓入的參數(shù)。如果是__stdcall則這個工作由被調(diào)函數(shù)來執(zhí)行。

程序樣例如下:
……
0040B8E8   push        1            ;壓入?yún)?shù)
0040B8EA   call        00401028        ;調(diào)用函數(shù)
……
00401028   jmp         0040b7c0        ;跳轉(zhuǎn)到函數(shù)入口
……
0040B7C0   push        ebp            ;保存ebp
0040B7C1   mov         ebp,esp        
0040B7C3   sub         esp,44h        ;設(shè)置函數(shù)的堆棧指針,此函數(shù)中有4
;字節(jié)的局部變量
0040B7C6   push        ebx
0040B7C7   push        esi        
0040B7C8   push        edi
0040B7C9   lea         edi,[ebp-44h]    
0040B7CC   mov         ecx,11h
0040B7D1   mov         eax,0CCCCCCCCh
0040B7D6   rep stos    dword ptr [edi]    ;初始化局部變量空間
0040B7D8   mov         eax,dword ptr [ebp+8]
0040B7DB   mov         dword ptr [ebp-4],eax
……
0040B7DE   pop         edi            ;彈出曾壓棧的數(shù)據(jù)
0040B7DF   pop         esi
0040B7E0   pop         ebx
0040B7E1   mov         esp,ebp        ;恢復(fù)調(diào)用者的堆棧
0040B7E3   pop         ebp            ;彈出原ebp值
0040B7E4   ret         4            ;返回并將堆棧向上調(diào)整4字節(jié)。
;此處為__stdcall約定,所以由函數(shù)調(diào)
;整堆棧

相應(yīng)的C代碼如下:

void __stdcall fun(int);

int main(void)
{
    ……    
fun(1);
……
    return 0;
}

void __stdcall fun(int para)
{
    int localpara = para;
    ……
}