搞懂函数调用前后堆栈恢复的过程
零、基本概念
1、ESP
棧指針寄存器(extended?stack?pointer),其內存放著一個指針,該指針永遠指向系統棧最上面一個棧幀的頂部。
2、EBP
基址指針寄存器(extended?base?pointer),其內存放著一個指針,該指針永遠指向系統棧最上面一個棧幀的底部。
一、栗子
以下摘自網上一篇文章:
明白了嗎?主要是用來保存 / 恢復堆棧,以便傳遞參數給函數。
在 MASM 里面,有一條更方便的語句,就是 invoke,使用它后,你就不用自己做這些事情了。
總的來說,esp 始終指向棧頂,ebp 是在堆棧中尋址用的。
我的理解:
調用一個函數時,先將堆棧原先的基址(EBP)入棧,以保存之前任務的信息。然后將棧頂指針的值賦給 EBP,將之前的棧頂作為新的基址(棧底),然后再這個基址上開辟相應的空間用作被調用函數的堆棧。
函數返回后,從 EBP 中可取出之前的 ESP 值,使棧頂恢復函數調用前的位置;再從恢復后的棧頂可彈出之前的 EBP 值,因為這個值在函數調用前一步被壓入堆棧。這樣,EBP 和 ESP 就都恢復了調用前的位置,堆棧恢復函數調用前的狀態。
二、通過 ollydbg 跟蹤 esp 和 ebp
可以看到,初始情況下,ebp 此時值為 0012FEDC,也就是棧幀的地址,而棧頂地址 esp 值為0012FDFC。可以看到兩個值有一定的關系。而幀指針的地址較高。
然后我們讓它執行前兩句,push ebp,mov ebp,esp
可以看到前兩句已經執行了,那么 ebp 跟 esp 的值也發生了變化。esp = 0012FDF8,ebp = 0012FDF8。為神馬?一句句解讀,push ebp,向棧里面壓入了一個東西,那么棧頂此時應該發生變化了,也就是地址 -4 字節。為什么是減法呢?因為是向低地址增長的,這點一定得注意。所以此時 esp 變化成了 0012FDFC - 4 = OO12FDF8。至于 ebp 也等于 0012FDF8 就不解釋了。
接著上圖不解釋:
此時呢,觀察現在的值。棧頂 esp = 0012FDF4,而 ebp = 0012FDF8;沒啥好說的,此時的棧頂已經又跑上去了,說明又有元素壓棧了。那么執行這句 mov esp, ebp 之后,不用說,esp 跟 ebp 都會變成 0012FDF8.
我們重點看下一幅,執行完 pop,讓 ebp 出棧,后會發生神馬。
此時 ebp 已經出棧了,來看看那他們的值,esp = 0012FDFC,ebp = 0012FEDC。首先,ebp 出棧了,這個時候棧空了,所以棧頂會變成初始時的值 0012FDFC。相當于上圖中的 esp = 0012FDF8 + 4 = 0012FDFC。注意出棧,則棧頂 + 4,然后呢。ebp 為啥變成了 0012FEDC 初始的值?ebp 不是一直保存著 esp 的初始地址么?
所以重點就在 pop 這個語句了。pop ebp 究竟表達神馬意思?ebp 的值起初存在了棧中,出棧以后,它的值就恢復了原樣。所以這一句灰常重要啊。pop 的意思也許就是把彈出的值賦給我們的變量,pop ebp,也就是把存在棧中的值彈出來賦給 ebp。
三、流程圖
EBP?和 ESP 后面的序號是兩個寄存器各自更新的次序。??
四、總結
1、兩句的 mov ebp,esp 實際上是把 ebp 進棧后的棧頂地址給了 ebp。
2、在 ebp 沒有出棧錢,它會一直保存 ebp 進棧以后的棧頂值,也就是 1 的值。
3、在 ebp 出棧前,需要把 esp 恢復到只有 ebp 在棧中時的值。
4、出棧后,esp 自然恢復到 ebp 進棧以前的初始值,而 pop ebp 則恢復了 ebp 的初始值。
5、pop 的語義很重要,pop ebp 的意思是把當前棧頂的元素出棧,送入 ebp 中,而不是讓 ebp 出棧,這點必須明確!
轉載于:EBP 和 ESP 詳解_測試開發小白變怪獸-CSDN博客_ebp
(SAW:Game Over!)
總結
以上是生活随笔為你收集整理的搞懂函数调用前后堆栈恢复的过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows10 下 vscode +
- 下一篇: Git / git clone 、git