堆棧是連續(xù)的地址空間,且向低地址端生長冰蘑。
esp 是堆棧指針
ebp 是基址指針
那兩條指令的意思是 將棧頂指向 ebp 的地址
以下摘自網(wǎng)上一篇文章:
push ebp ; ebp 入椂詈伲
mov ebp, esp ; 因為 esp 是堆棧指針地啰,無法暫借使用送粱,所以得用 ebp 來存取堆棧
sub esp, 4*5 ; 下面的 wsprintf 一共使用了 5 個參數(shù)时捌,每個參數(shù)占用 4 個字節(jié)怒医,所以要入棧 4*5 個字節(jié)
push 1111
push 2222
push 3333
push offset szFormat
push offset szOut
call wsprintf ; 調(diào)用 wsprintf
add esp, 4*5 ; 堆棧使用完畢,“還” 回 4*5 個字節(jié)給系統(tǒng)
...
mov esp, ebp ; 恢復(fù) esp 的值
pop ebp ; ebp 出椛萏郑
ret
明白了嗎稚叹?主要是用來保存 / 恢復(fù)堆棧,以便傳遞參數(shù)給函數(shù)拿诸∪肼迹 在 MASM 里面,有一條更方便的語句佳镜,就是 invoke 使用它后僚稿,你就不用自己做這些事情了◇吧欤
---------------------------------------------------------------
esp 始終指向棧頂蚀同,ebp 是在堆棧中尋址用的
我的理解:
調(diào)用一個函數(shù)時缅刽,先將堆棧原先的基址(EBP)入棧,以保存之前任務(wù)的信息蠢络。然后將棧頂指針的值賦給 EBP衰猛,將之前的棧頂作為新的基址(棧底),然后再這個基址上開辟相應(yīng)的空間用作被調(diào)用函數(shù)的堆棧刹孔。函數(shù)返回后啡省,從 EBP 中可取出之前的 ESP 值,使棧頂恢復(fù)函數(shù)調(diào)用前的位置髓霞;再從恢復(fù)后的棧頂可彈出之前的 EBP 值卦睹,因為這個值在函數(shù)調(diào)用前一步被壓入堆棧。這樣方库,EBP 和 ESP 就都恢復(fù)了調(diào)用前的位置结序,堆棧恢復(fù)函數(shù)調(diào)用前的狀態(tài)纵潦。
二. 通過 ollydbg 跟蹤 esp 和 ebp
發(fā)現(xiàn)文字描述還是太沒有快感徐鹤。上幾幅圖,來說明這個調(diào)試過程更好邀层。此文對于深刻理解 ebp返敬,esp 是具有長遠意義的
然后我們讓它執(zhí)行前兩句,push ebp,mov ebp,esp
接著上圖不解釋:
所以重點就在 pop 這個語句了侦锯。pop ebp 究竟表達神馬意思驼鞭?ebp 的值起初存在了棧中,出棧以后尺碰,它的值就恢復(fù)了原樣挣棕。所一句灰常重要啊。pop 的意思也許就是把彈出的值賦給我們的變量亲桥,pop ebp洛心,也就是把存在棧中的值彈出來賦給 ebp。
所以我在這里總結(jié)幾句:
1题篷、兩句的 mov ebp,esp 實際上是把 ebp 進棧后的棧頂?shù)刂方o了 ebp词身。
2、在 ebp 沒有出棧錢番枚,它會一直保存 ebp 進棧以后的棧頂值法严,也就是 1 的值璧瞬。
3、在 ebp 出棧前渐夸,需要把 esp 恢復(fù)到只有 ebp 在棧中時的值嗤锉。
4、出棧后墓塌,esp 自然恢復(fù)到 ebp 進棧以前的初始值瘟忱,而 pop ebp 則恢復(fù)了 ebp 的初始值。
5苫幢、pop 的語義很重要, pop ebp 的意思是把當(dāng)前棧頂?shù)脑爻鰲7糜眨腿?ebp 中,而不是讓 ebp 出棧韩肝,這點必須明確触菜!
這下應(yīng)該明白了吧~~~~
參考網(wǎng)上資源:
http://blog.csdn.net/running_noodle/article/details/2838679
http://hi.baidu.com/anheizzq/item/1c0899622926c81e7ddecca3
ebp-- 棧底指針
esp-- 棧頂指針
如圖所示,簡化后的代碼調(diào)用過程如下:
void Layer02()
{
int b = 2;
}
void Layer01()
{
int a = 1;
Layer02();
}
那么函數(shù)執(zhí)行過程中 ebp 和 esp 是如何變化的呢哀峻?如下是反匯編后的代碼:
void Layer02()
{
00413700 push ebp
00413701 mov ebp,esp
00413703 sub esp,0CCh
00413709 push ebx
0041370A push esi
0041370B push edi
0041370C lea edi,[ebp-0CCh]
00413712 mov ecx,33h
00413717 mov eax,0CCCCCCCCh
0041371C rep stos dword ptr es:[edi]
int b = 2;
0041371E mov dword ptr [b],2
}
00413725 pop edi
00413726 pop esi
00413727 pop ebx
00413728 mov esp,ebp
0041372A pop ebp
0041372B ret
我們看到函數(shù)調(diào)用開始執(zhí)行如下的兩行代碼:
00413700 push ebp
00413701 mov ebp,esp
返回前執(zhí)行如下代碼:
00413728 mov esp,ebp
0041372A pop ebp
0041372B ret
那么這幾行代碼到底是什么意思呢涡相?首先,如圖上所示:
開始兩行代碼的意思是先將 ebp1 壓棧剩蟀,然后將現(xiàn)在的棧頂 esp1 作為函數(shù)調(diào)用時的棧底催蝗,所以會執(zhí)行如下語句:
00413701 mov ebp,esp
那么,返回前的幾條語句又是什么意思呢育特?
我想大家已經(jīng)猜到了丙号,當(dāng)函數(shù)調(diào)用執(zhí)行結(jié)束,我們要執(zhí)行相反的過程:
00413728 mov esp,ebp
還原棧頂指針
0041372A pop ebp
還原棧底指針
0041372B ret
返回到函數(shù)調(diào)用前的指令繼續(xù)執(zhí)行缰冤。待續(xù)…
ESP,EBP, 椚В回溯基本原理
我們看到,盡管可以使用相對于棧頂(ESP 寄存器)的偏移來引用局部變量棉浸,但是因為 ESP寄存器經(jīng)常變化怀薛,所以用這種方法引用同一個局部變量的偏移值是不固定的。這種不確定性對于 CPU 來說不成什么問題涮拗,但在調(diào)試時乾戏,如果要跟蹤這樣的代碼迂苛,那么很容易就被轉(zhuǎn)得頭暈眼花三热,因為現(xiàn)實的函數(shù)大多有多個局部變量,可能還有層層嵌套的循環(huán)三幻,棧指針變化非常頻繁就漾。
為了解決以上問題,x86 CPU 設(shè)計了另一個寄存器念搬,這就是 EBP 寄存器抑堡。EBP 的全稱是 Extended Base Pointer摆出,即拓展的基址指針。使用 EBP 寄存器首妖,函數(shù)可以把自己將要使用的椯寺空間的基準地址記錄下來,然后使用這個基準地址來引用局部變量和參數(shù)有缆。在同一函數(shù)內(nèi)象踊,EBP 寄存器的值是保持不變的,這樣函數(shù)內(nèi)的局部變量便有了一個固定的參照物棚壁。
通常杯矩,一個函數(shù)在入口處將當(dāng)時的 EBP 值壓入堆棧,然后把 ESP 值(棧頂)賦給 EBP袖外,這樣 EBP 中的地址就是進入本函數(shù)時的棧頂?shù)刂肥仿。@一地址上面(地址值遞減方向)的空間便是這個函數(shù)將要使用棧空間曼验,它下面(地址值遞增方向)是父函數(shù)使用的空間泌射。如此設(shè)置 EBP 后,便可以使用 EBP 加正數(shù)偏移來引用父函數(shù)的內(nèi)容鬓照,使用 EBP 加負數(shù)便宜來引用本函數(shù)的局部變量魄幕,比如 EBP+4 指向的是 CALL 指令壓入的函數(shù)返回地址;EBP+8 是父函數(shù)壓在棧上的第一個參數(shù)颖杏,EBP+0xC 是第二個參數(shù)纯陨,一次類推;EBP-n 是第一個局部變量的起始地址(n 為變量的長度)。
因為在將棧頂?shù)刂罚‥SP)賦給 EBP 寄存器之前先把舊的 EBP 值保存在棧中留储,所以 EBP 寄存器所指向的棧單元中保存的是前一個 EBP 寄存器的值翼抠,這通常也就是父函數(shù)的 EBP 的值。類似的父函數(shù)的 EBP 所指向的棧單元中保存的是更上一層函數(shù)的 EBP 值获讳,以此類推阴颖,直到當(dāng)前線程的最頂層函數(shù)。這也正是椮はィ回溯的基本原理量愧。