函數調用過程內存堆棧變化分析

函數說明,簡單函數調用代碼如下:

代碼:

  

這裏我們重複調用了這個函數,來分析編譯器傳遞參數的過程。

我們通過PUSH將一個字符壓入堆棧,這個相當於我們c函數傳入的參數,然後通過call,調用一個彙編定義的函數,函數裏面通過MOV將字符參數裝入CL,並且輸出到串口。

1.  加載編譯好的系統鏡像,代碼調轉到此處,如下圖,我們可以看到反編譯的系統代碼。

 

 

2.       單步運行PUSH B’,然後查看當前段寄存器和通用寄存器。弱弱的補充一下raxeax64位擴展,eaxax32位擴展。這裏的r都可以忽略。我們可以看到當前堆棧段寄存器地址是SS=0x8。段頂指針寄存器SP=0x400fffc,段基址是0x1fff0000。我們通過x命令打印堆棧裏面的20個字節用16進製表示,這個20字節從段頂開始偏向段基址。

我們可以看到從段頂開始兩個字節是0x0042,這個就是我們壓入堆棧的‘B,它被存在偏移地址0x400fffc-0x400fffd這個地方。之後數據都是0

3. 繼續單步調式,我們進入調用的函數,通過u我們察看當前代碼後面的10條反彙編指令,可以看到我們已經進入到打印函數內部第一條語句。這條語句MOV CLbyte ptr ss:[esp+4]就是將我們剛剛壓入堆棧的參數得到,然後賦值給cl寄存器。Esp+4的目的就是掉過調用call指令自動壓入堆棧的CSIP的值。接着來看這些值。

 

 

4.       接着我們繼續打印當前寄存器,結果如下圖。從這個圖我們可以發現,段頂指針已經從0x400fffc移動到了0x400fff8,然後通過x命令,查看當前堆棧裏面的數據,我們可以發現有四個字節的新數據被壓入了進來分別是0x65d0 0x0000.對了,這兩個值就是我們調用CALL指令的時候自動壓入的IPCS,它們分別是CALL後面一條指令的程序偏移地址,和代碼段地址。從前面的截圖我們可以推算到這個0x65d0就是偏移地址IP,但是爲什麼這個代碼段地址是0呢,當前代碼段明明是CS=0x60,那是因爲調用的是本段函數,所以這裏的CS=0,表示不進行段間調轉。所以只需要偏移地址。到目前爲止我們可以看到我們一共有8個字節的數據被壓入堆棧:4個字節參數,2個字節IP2個字節CS。到這裏就可以理解那個ESP+4就是要掉過4個字節的IPCS,取得參數。

 

 

5.       設置斷點直接掉到函數結尾ret 0x4。這條指令表示返回時候返回IP,並且清除4個字節壓入堆棧的數據。這樣說明堆棧內數據要被相當於pop8個字節。

 

6.       執行ret 4.代碼走到下一個CALL,這時候我們可以看到當前指令地址是0x60:0xb5d0,這個0xb5d0就是我們剛剛壓入堆棧的下一條指令的IP。接下來我們再來看看堆棧。

 

7.       我們可以看到,這個時候Sp已經移動到了0x4010000,這樣看來比調用函數之前的0x400fffc要增加了4個字節,這個說明在函數返回的時候,不僅返回了IP,CS的四個字節,系統還清除了ret指定的4個字節,也就是說,調用函數之前允許最多押送4個字節進入堆棧作爲參數使用。這樣也相當於重新清空了堆棧,我們可以看到新的堆棧頂部以下都沒有東西了。全是0

 

 

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