摘要:當(dāng)函數(shù)返回時,系統(tǒng)棧會彈出該函數(shù)所對應(yīng)的棧幀。四寄存器與函數(shù)棧幀每一個函數(shù)獨占自己的棧幀空間。棧幀調(diào)整具體包括。
棧是函數(shù)執(zhí)行的內(nèi)存區(qū)域,是C語言運行時最重要的元素之一
寄存器是CPU內(nèi)部用來存放數(shù)據(jù)的一些小型存儲區(qū)域,用來暫時存放參與運算的數(shù)據(jù)和運算結(jié)果。
寄存器有eax,ebx,ecx,edx,還有ebp,esp。本文主要介紹最后兩個,由于寄存器不是本次博客的重點,其他請自行了解!
ESP | 棧指針寄存器(extended stack pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的棧頂 |
---|---|
EBP | 基址指針寄存器(extended base pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的底部 |
想要了解函數(shù)棧幀,就必須先了解好這兩個地址。
ebp,esp這兩個寄存器中存放的是地址,且這兩個地址是用來維護(hù)函數(shù)棧幀的
棧區(qū):
用于動態(tài)地存儲函數(shù)之間的調(diào)用關(guān)系,以保證被調(diào)用函數(shù)在返回時恢復(fù)到母函數(shù)中繼續(xù)執(zhí)行。
棧的最常見操作有兩種:
壓棧(push)
彈棧(pop)
用于標(biāo)識棧的屬性也有兩個:
棧頂(top)
棧底( base)。
什么意思?
可以把棧想象成一摞撲克牌。
push | 為棧增加一個元素的操作叫做推,相當(dāng)于在這摞撲克牌的最上面再放上一張。 |
---|---|
pop | 從棧中取出一個元素的操作叫做流行,相當(dāng)于從這摞撲克牌取出最上面的一張。 |
pop | 從棧中取出一個元素的操作叫做流行,相當(dāng)于從這摞撲克牌取出最上面的一張。 |
---|---|
base | 標(biāo)識棧底位置,它記錄著撲克牌最下面一張的位置。base用于防止??蘸罄^續(xù)彈棧(牌發(fā)完時就不能再去揭了)。很明顯,一般情況下,base是不會變動的。 |
以下面代碼為例:
#define _CRT_SECURE_NO_WARNINGS 1#include int Add(int x, int y){ int z = 0; z = x + y; return z;}int main(){ int a = 10; int b = 20; int c = 0; c = Add(a, b); printf("%d/n", c); return 0;}
當(dāng)中央處理器在執(zhí)行調(diào)用Add函數(shù)的時候,會從代碼區(qū)中
主要函數(shù)對應(yīng)的機(jī)器指令的區(qū)域跳轉(zhuǎn)到Add函數(shù)對應(yīng)的機(jī)器指令區(qū)域,在那里取指并執(zhí)行;當(dāng)Add函數(shù)執(zhí)行完閉,需要返回的時候,又會跳回到主要函數(shù)對應(yīng)的指令區(qū)域,緊接著調(diào)用Add后面的指令繼續(xù)執(zhí)行主要函數(shù)的代碼。
如下圖:
那么中央處理器是怎么知道要去Add函數(shù)的代碼區(qū)取指,
在執(zhí)行完Add后又是怎么知道跳回到主要函數(shù)(而不是其他未知的代碼區(qū))的呢?
這是因為
當(dāng)函數(shù)被調(diào)用時,系統(tǒng)棧會為這個函數(shù)開辟一個新的棧幀,并把它壓入棧中。這個棧幀中的內(nèi)存空間被它所屬的函數(shù)獨占,正常情況下是不會和別的函數(shù)共享的。當(dāng)函數(shù)返回時,系統(tǒng)棧會彈出該函數(shù)所對應(yīng)的棧幀。
每一個函數(shù)獨占自己的棧幀空間。當(dāng)前正在運行的函數(shù)的棧幀總是在棧頂。
函數(shù)棧幀:
esp和ebp之間的內(nèi)存空間為當(dāng)前棧幀,esp標(biāo)識了當(dāng)前棧幀的頂部,ebp標(biāo)識了當(dāng)前棧幀的底部。
函數(shù)調(diào)用大致包括以下幾個步驟。
(1)參數(shù)入棧:將參數(shù)從右向左依次壓入系統(tǒng)棧中。 (2)返回地址入棧:將當(dāng)前代碼區(qū)調(diào)用指令的下一條指令地址壓入棧中,供函數(shù)返回時繼續(xù)執(zhí)行。
(3)代碼區(qū)跳轉(zhuǎn):處理器從當(dāng)前代碼區(qū)跳轉(zhuǎn)到被調(diào)用函數(shù)的入口處。
(4)棧幀調(diào)整:具體包括。保存當(dāng)前棧幀狀態(tài)值,已備后面恢復(fù)本棧幀時使用(ebp入棧);將當(dāng)前棧幀切換到新棧幀(將esp值裝入ebp,更新棧幀底部);給新棧幀分配空間(把esp減去所需空間的大小,抬高棧頂);
將上面的代碼轉(zhuǎn)到反匯編模式查看
對應(yīng)觀察:
1.函數(shù)返回過程
(1)保存返回值:通常將函數(shù)的返回值保存在寄存器EAX中。
(2)彈出當(dāng)前棧幀,恢復(fù)上一個棧幀。
1.在堆棧平衡的基礎(chǔ)上,給esp加上棧幀的大小,降低棧頂,回收當(dāng)前棧幀的空間。
2.將當(dāng)前棧幀底部保存的前棧幀ebp值彈入ebp寄存器,
復(fù)出上一個棧幀。
3.將函數(shù)返回地址彈給ebi寄存器。
(3)跳轉(zhuǎn):按照函數(shù)返回地址跳回母函數(shù)中繼續(xù)執(zhí)行。
本文內(nèi)容來自于《oday安全軟件漏洞分析技術(shù)》第2版與自己學(xué)習(xí)筆記所,本文僅記述自己學(xué)習(xí)歷程與僅供自己學(xué)習(xí),復(fù)習(xí)用,不做任何商業(yè)用途。
若有興趣深入了解函數(shù)棧幀,請自行查閱王清主編的《Oday安全:軟件漏洞分析技術(shù)(第2版)》一書。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/121674.html
摘要:這里分塊講解六函數(shù)棧幀的銷毀過程一解析的作用是將棧頂?shù)臄?shù)據(jù)彈出,彈出數(shù)據(jù)儲存到相應(yīng)寄存器中。 ?前言? 讀完這篇博客,你可以明白什么? ①局部變量到底是怎么在棧上創(chuàng)建的? ②為什么局部變量不初始化為隨機(jī)值? ③函數(shù)是怎么傳參的?傳參的先后順序是什么? ④形參和實參是什么關(guān)系? ⑤函數(shù)調(diào)用是怎...
摘要:是那些可能在虛擬機(jī)正常運行期間拋出的異常的超類。運行時異常定義及其子類都被稱為運行時異常。對于語言中的關(guān)鍵字和,虛擬機(jī)中并沒有特殊的字節(jié)碼指令去支持它們,都是通過編譯器生成字節(jié)碼片段以及不同的異常處理器來實現(xiàn)。 前言 在一些傳統(tǒng)的編程語言,如C語言中,并沒有專門處理異常的機(jī)制,程序員通常用方法的特定返回值來表示異常情況,并且程序的正常流程和異常流程都采用同樣的流程控制語句。Java語言...
摘要:函數(shù)棧幀的銷毀匯編語言了解函數(shù)傳參函數(shù)返回值如何返回函數(shù)中變量如何初始化和賦值函數(shù)執(zhí)行結(jié)束后系統(tǒng)進(jìn)行了什么操作 文章目錄 一、什么是函數(shù)棧幀 1.寄存器2.函數(shù)棧幀3.棧幀的作用和維護(hù)4.棧幀結(jié)構(gòu)二、函數(shù)棧幀的創(chuàng)建? 1.匯編2.main函數(shù)3.Add函數(shù)的創(chuàng)建三、函數(shù)...
閱讀 2118·2021-11-24 09:39
閱讀 904·2021-09-30 09:48
閱讀 1072·2021-09-22 15:29
閱讀 2496·2019-08-30 14:17
閱讀 1944·2019-08-30 13:50
閱讀 1429·2019-08-30 13:47
閱讀 1051·2019-08-30 13:19
閱讀 3473·2019-08-29 16:43