天天看點

神奇的棧幀

C語言中,每個棧幀對應着一個未運作完的函數。棧幀中儲存了該函數的傳回位址和局部變量。

先看張圖對棧的位置有個基本的印象哈:

神奇的棧幀

首先必須明确一點也是非常重要的一點,棧是向下生長的,所謂向下生長是指從記憶體高位址->低位址的路徑延伸,那麼就很明顯了,棧有棧底和棧頂,那麼棧頂的位址要比棧低低。

寄存器ebp在未受改變之前始終指向棧幀的開始,也就是棧底,是以ebp的用途是在堆棧中尋址用的。

寄存器esp是會随着資料的入棧和出棧移動的,也就是說,esp始終指向棧頂。

下面我們看一個有趣的程式:

神奇的棧幀

感興趣的童鞋可以運作試試哈(先保證沒有重要的臨時檔案在運作,害怕你到時砍我),整個程式運作起來你的電腦就會重新開機,很好看出來,重新開機的程式在fun函數中,可是整個過程看起來我們并沒有調用fun,為什麼它還是run起來了?

原因就在這棧幀之中,首先先說明一點,ELF格式的入口是_start而不是mian,也就是說在mian函數的棧幀之前還有一個_start函數,我們先轉到彙編,看看函數的棧幀到底是怎麼開辟的:

先是_start的函數棧幀:

神奇的棧幀

再看看main的:

神奇的棧幀

通過簡單的圖來表示下就是這樣的:

神奇的棧幀

關鍵的部分是fun1的彙編,它具體給我們展示了到底為什麼fun會被調用,下面看看吧:

神奇的棧幀

再來張示意圖:

神奇的棧幀

結合上面兩個圖和注釋可以看出,在fun1調用完後我們需要傳回到之前儲存的位址中去,而在fun1中我們将其傳回的位址改成了fun函數的入口位址,是以在fun1結束後我們就跳到了fun函數,然後就悲催了。。。。。

如果我們了解了棧幀的原理,那可以做很多神奇的事情,比如在fun1中我們可以不使用b變量就将b的值改掉,很簡單,通過上面的彙編可以看出隻要将fun1中的p--,改成p++;就找到b變量的位址啦,然後解引用就可以修改啦,不信可以去試試喽。

繼續閱讀