2.16 EBP尋址
為什麼要介紹EBP尋址?因為EBP為棧底指針,EBP的值在尋址時是不會發生變化的。
本節必須掌握的知識點:
EBP尋址
在上節中我們留下了一個疑問,“面對ESP尋址的壞處,我們有什麼好的解決方案”,有的,可以選擇用EBP尋址。接下來我們介紹EBP尋址。
2.16.1【什麼是EBP尋址】
EBP就是棧底指針。EBP尋址就是通過EBP尋找堆棧內存的地址。說的再明白點就是利用與EBP的相對位置來確定數據所在位置。
我們借用例題的形式給大家介紹EBP尋址。
例:編寫一個函數,使它能實現任意兩個整數相加。
PUSH 1
PUSH 2
CALL 0x12345678(假設函數體開始的位置為0x12345678)
PUSH EBP (保存EBP的值)
MOV EBP,ESP (將棧頂ESP的值複製一份給EBP)
SUB ESP,10 (提升棧頂ESP的值,重新劃分一塊堆棧空間給程序使用)
MOV EAX,DWORD PTR SS:[EBP+C] //加第一個參數
ADD EAX,DWORD PTR SS:[EBP+8] //加第二個參數:
MOV ESP,EBP //恢復堆棧
POP EBP
RETN 8
我們借用DTDebug.exe軟件,操作上面的例題,並一步步解釋說明。
第一步:用DTDebug.exe軟件打開飛鴿軟件,如圖2-16-1所示。
第二步:輸入彙編指令,如圖2-16-2所示。
ESP
0x0018FF0
EBP
0x0018FFC
初始堆棧
第三步:將參數壓入棧,並執行CALL指令,如圖2-16-3所示。
ESP
CALL 返回地址
2
1
原ESP
0x0018FF0
EBP
0x0018FFC
傳入參數
傳遞參數
第四步:將ESP存入EBP中,提升堆棧,如圖2-16-4所示。
PUSH EBP這一步是保存EBP存儲的數據;
MOV EBP,ESP 這一步是將ESP存儲的數據移動到EBP,執行後EBP存儲的數據為0x0018FFE0,為我們以後EBP尋址做鋪墊;
SUB ESP,10 這一步提升堆棧空間,為什麼提升堆棧空間,因為一個函數在執行時會讀取臨時數據用到這段空間,在我們一般應用程序中,一段函數會預留一段空間,至於預留多少,這是編譯器決定的。
ESP
SUB ESP,10
PUSH EBP
CALL 返回地址
2
1
原ESP
0x0018FF0
EBP
0x0018FFC
提升堆棧
第五步:按F8執行,實現兩個參數相加,圖2-16-5所示。
MOV EAX,DWORD PTR DS:[EBP+0xC]
ADD EAX,DWORD PTR DS:[EBP+0x8]
這兩步用EBP尋址的方式,實現兩個參數相加,並把得到的結果保存在EAX中。由於MOV指令和ADD指令並沒有使堆棧發生變話,所以就不再畫堆棧的變化。
第七步:恢復現場,EBP存儲的數據是在沒提升堆棧之前的ESP的數據,我們執行完了函數,這裡要恢復堆棧,執行MOV ESP,EBP,由於在執行函數時保存EBP的數據,我們還要恢復原EBP的數據,執行POP EBP,如圖2-16-6。
ESP
CALL 返回地址
2
1
原ESP
0x0018FF0
EBP
0x0018FFC
恢復現場
第八步:平衡堆棧,執行RETN 8,如圖2-16-7所示。
上步驟是用EBP尋址執行函數的完整的步驟。
總結:
使用EBP尋址的步驟如下:
第一步:保存EBP的值;
第二步:將EBP的值等於ESP;
第三步:提升棧頂ESP的值,重新劃分一塊堆棧空間給程序使用。
執行一個函數,在運行時會產生讀取數據,改變ESP的變化,使用EBP尋址時,EBP和參數的位置不會有變動,通過EBP+偏移來尋址,快速、簡單、穩定。
下節介紹JCC指令。
練習:
1、使用EBP尋址,編寫一個函數,使它能實現任意2個整數相加、2個整數相減,並單步執行,觀察堆棧窗口變化。
2、自己總結ESP尋址和EBP尋址,並自己通過實驗總結出ESP尋址和EBP尋址的不同。
閱讀更多 愛達人編程達人 的文章