[CSAPP學習筆記] 棧幀

CSAPP第三章的重點在我看來就集中在對棧幀的理解上了。IA32過程調用就是通過各種棧幀來實現的。

 

棧幀(Stack Frame):爲單個過程(Procedure)分配的那部分棧。

因此棧幀存在於棧上,每個過程又各自擁有自己獨自的棧幀。棧幀以兩個指針定界:棧指針%esp寄存器和幀指針%ebp寄存器,%esp在執行時需要不停移動,因此信息一般是靠單個過程中固定不變的幀指針%ebp來訪問。下圖是一個典型的棧幀結構(注意棧結構是地址向下增長的):

假設調用幀(Caller’sframe)爲過程P的棧幀結構,而當前幀(Currentframe)爲所調用的過程Q的棧幀結構。

由圖中可以看出,在調用Q之前,P要把傳遞給過程Q的參數Argument1到Argument n按照地址增長的順序存放(注意此時幀指針%esp指向Argument1)。

一旦執行call 指令調用過程Q,返回地址會被立即壓入棧中(注意push指令會將減少%esp的值)。

而在開始執行Q過程前,要把P的幀指針%ebp保存起來(這樣從Q返回之後才能找到P棧幀的位置),然後把%esp棧指針賦值給%ebp,讓幀指針%ebp指向這個新棧幀的幀底,接着減小棧指針%esp的值使其指向新棧幀的幀頂,過程Q的棧幀空間也就分配好了。

這些空間還可以用來保存Q過程中一些局部變量(這一點需要注意,後面會進行擴展),被調用者保存的寄存器(Callee_saved Registers),以及(假如Q過程還要調用其他過程)新調用過程所需要的一些參數。

 

以前我一直不理解棧溢出帶來的危害,不懂爲什麼gets函數因爲沒有對輸入範圍進行限制會導致很嚴重的後果,直到學習到這裏才明白。正如上面所說,棧幀空間有一部分是用來保存過程中的局部變量。假如我們在Q中定義一個數組buf[2](編譯器會自動給過程分配合適的空間),這樣在Q的棧幀中會有這麼一個2字節的空間。我們使用gets(buf),如果輸入數據有很多的話,那麼2字節之後的數據就會向上繼續填入(注意棧是地址向下增長),可以看出,新填入的數據會將之前保存的寄存器和P的幀指針%ebp甚至返回地址都覆蓋了,整個程序也就因此變得混亂了。所謂的蠕蟲、病毒往往就是攻擊程序中這些漏洞。

 

擴展到x86-64的話,棧幀與IA32的不同之處在於沒有幀指針%ebp了,一旦分配好棧幀空間,%esp保持不變,信息的訪問也變成了相對於棧指針%esp來進行。還有一點就是對於IA32來說,過程不能使用其棧指針之外的空間,而x86-64可以使用%esp之外128字節的範圍(成爲Red Zone)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章