函數調用過程中函數棧詳解

函數調用過程中函數棧詳解

當進程被加載到內存時,會被分成很多段

l 代碼段:保存程序文本,指令指針EIP就是指向代碼段,可讀可執行不可寫,如果發生寫操作則會提示segmentation fault

l 數據段:保存初始化的全局變量和靜態變量,可讀可寫不可執行

l BSS:未初始化的全局變量和靜態變量

l 堆(Heap):動態分配內存,向地址增大的方向增長,可讀可寫可執行

l 棧(Stack):存放局部變量,函數參數,當前狀態,函數調用信息等,向地址減小的方向增長,可讀可寫可執行

l 環境/參數段(environment/argumentssection):用來存儲系統環境變量的一份複製文件,進程在運行時可能需要。例如,運行中的進程,可以通過環境變量來訪問路徑、shell 名稱、主機名等信息。該節是可寫的,因此在緩衝區溢出(buffer overflow)攻擊中都可以使用該段

寄存器

EAX:累加(Accumulator)寄存器,常用於函數返回值

EBX:基址(Base)寄存器,以它為基址訪問內存

ECX:計數器(Counter)寄存器,常用作字符串和循環操作中的計數器

EDX:數據(Data)寄存器,常用於乘除法和I/O指針

ESI:源變址寄存器

DSI:目的變址寄存器

ESP:堆棧(Stack)指針寄存器,指向堆棧頂部

EBP:基址指針寄存器,指向當前堆棧底部

EIP:指令寄存器,指向下一條指令的地址

入棧push和出棧pop

push ebp就等於將ebp的值保存到棧中,並且將當前esp下移

pop ebp就等於將ebp的值從棧中取出來,將ebp指向這個值

下面用一個例子來講函數調用過程中棧的變化

int sum(int _a,int _b)

{

int c=0;

c=_a+_b;

return c;

}

int main()

{

int a=10;

int b=20;

int ret=0;

ret=sum(a,b);

return 0;

}

main函數的棧在調用之前如圖:

函數調用過程中函數棧詳解

Ok現在講一講ret=sum(a,b);的執行過程

Step 1:

函數參數從右至左入棧

函數調用過程中函數棧詳解

Step 2:

ret=sum(a,b);

call @ILT+0(sum) (00401005) call指令實際上分兩步

push EIP 將下一條指令入棧保存起來

esp-4 esp指針下移

函數調用過程中函數棧詳解

Step 3:

push ebp 將main函數基指針入棧保存

函數調用過程中函數棧詳解

mov ebp esp 將esp的值存入ebp也就等於將ebp指向esp

函數調用過程中函數棧詳解

sub esp 44H將esp下移動一段空間創建sum函數的棧棧幀

函數調用過程中函數棧詳解

Step 4:

push ebx

push esi

push edi

lea edi,[ebp-44h] 從ebp-44h的地方開始拷貝

mov ecx,11h 拷貝11次

mov eax,0CCCCCCCCh 拷貝內容為0CCCCCCCCh

rep stos dword ptr [edi] 每次拷貝雙字

函數調用過程中函數棧詳解

Step 5:

int c = 0;

mov dword ptr [ebp-4],0 將sum的局部變量c放入[ebp-4]的空間內

函數調用過程中函數棧詳解

Step 6:

執行函數操作

函數調用過程中函數棧詳解

Step 7:

return c = 0;

mov eax,dword ptr [ebp-4] 將返回值(變量c所在的地址的內容)放入eax寄存器保存住

Step 8:

pop edi //將之前入棧的值重新返回給edi寄存器

0040104C pop exi ////將之前入棧的值重新返回給exi寄存器

0040104D pop ebx ////將之前入棧的值重新返回給ebx寄存器

函數調用過程中函數棧詳解

Step 9:

mov esp ebp //將ebp的值賦給esp,也就等於將esp指向ebp,銷燬sum函數棧幀

函數調用過程中函數棧詳解

Step 10:

pop ebp //ebp出棧,將棧中保存的main函數的基址賦值給ebp

函數調用過程中函數棧詳解

Step 11:

ret //ret相當於pop eip 就是把之前保存的函數返回地址(也就是main函數中下一條該執行的指令的地址)出棧

函數調用過程中函數棧詳解

Step 12:

add esp,8 //此時若傳入sum函數的參數已經不需要了,我們將esp指針上移

函數調用過程中函數棧詳解

此時函數整個調用過程就結束了,main函數棧恢復到了調用之前的狀態


分享到:


相關文章: