總是被問及函式呼叫過程中堆疊的變化以及棧回溯的原理,最近好好的研究了一下函式呼叫過程中各個暫存器和堆疊狀態的變化,在這邊分享一下,如果有不對的地方,希望得到指正,本文使用vs2008工具執行下面這段**執行,並將各個過程中堆疊資料的變化記下。
**如下:
int _cdecl fund(int a, int b)
int _cdecl main(int argc, char* argv)
暫存器名稱
功能
eax存放函式返回值
ecx存放迴圈次數
esp堆疊指標
ebp基址指標
eip
call: 將下一條指令位置push進堆疊,並跳轉到對應函式位置;
rep: 重複執行直到ecx = 0;
stos: 將eax中資料拷貝到目標位置,如果方向標誌位為1,則目標位置-1,否則+1。
// 主函式
int _cdecl main(int argc, char* argv)
;↑該部分是函式的功能性操作,就兩個值相加,但是注意將返回值放置在了eax中
********************************************/
至此,可以整個過程中通過儲存呼叫函式(主函式)的基位址ebp來還原呼叫函式的棧底資訊,通過將引數入棧出棧來還原棧頂資訊,整體實現子函式的呼叫過程。/*******************************************
00021eda pop edi
00021edb pop esi
00021edc pop ebx
00021edd mov esp,ebp
00021edf pop ebp
00021ee0 ret
; ↑該部分處理從子函式返回主函式的堆疊恢復操作
; edi esi ebx 依次恢復,
; mov esp,ebp 將棧頂位置放置到子函式的棧基址位置,從圖3可以看見該位置存放了主函式的棧基址
; pop ebp 恢復主函式的棧基址位址
********************************************/
/*********子函式呼叫結束*******************/
/*******************************************
00021817 add esp,8
;此處將esp +8,用於恢復主函式在呼叫子函式之前的棧頂位址,
;+8是因為子函式有兩個變數,在呼叫之前push進堆疊中。
0002181a mov dword ptr [r],eax
********************************************/
return 0;
/*******************************************
0002181d xor eax,eax
}0002181f pop edi
00021820 pop esi
00021821 pop ebx
00021822 add esp,0cch
00021828 cmp ebp,esp
0002182a call @ilt+355(__rtc_checkesp) (21168h)
0002182f mov esp,ebp
00021831 pop ebp
00021832 ret
********************************************/
深入淺出》根據函式呼叫過程談棧回溯原理
通過分析函式呼叫過程的堆疊變化,可以看出在被調函式的ebp暫存器位址存放的是呼叫函式的ebp暫存器位址,ebp位址 4存放的是函式呼叫完成後的下一條指令存放位址,該指令的前一條指令則是呼叫函式的指令。說起來有點拗口,接下來 分析一下 分析使用的原始碼如下 cpp view plain copy pr...
函式呼叫堆疊變化分析
比如 我們有這樣乙個c函式 include long test int a,int b void main 寫成32位彙編就是這樣 386 model flat,stdcall 這裡我們用stdcall 就是函式引數 壓棧的時候從最後乙個開始壓,和被呼叫函式負責清棧 option casemap n...
函式呼叫堆疊變化分析
跟乙個朋友談堆疊的時候 就寫下了這段文字,順便發到這裡給需要的看看吧 彙編初學者比較頭痛的乙個問題 比如 我們有這樣乙個c函式 include long test int a,int b void main 寫成32位彙編就是這樣 386 model flat,stdcall 這裡我們用stdcal...