重在動(dòng)手,重在總結(jié)!
代碼如下:
#include "stdlib.h"
int sum(int a,int b,int m,int n)
{
return a+b;
}
void main()
{
int result = sum(1,2,3,4);
system("pause");
}
有四個(gè)參數(shù)的sum函數(shù),接著在main方法中調(diào)用sum函數(shù)。在debug環(huán)境下,單步調(diào)試如下:
11: void main()
12: {
00401060 push ebp
;保存ebp,執(zhí)行這句之前,ESP = 0012FF4C EBP = 0012FF88
;執(zhí)行后,ESP = 0012FF48 EBP = 0012FF88,ESP減小,EBP不變
00401061 mov ebp,esp
;將esp放入ebp中,此時(shí)ebp和esp相同,即執(zhí)行后ESP = 0012FF48 EBP = 0012FF48
;原EBP值已經(jīng)被壓棧(位于棧頂),而新的EBP又恰恰指向棧頂。
;此時(shí)EBP寄存器就已經(jīng)處于一個(gè)非常重要的地位,該寄存器中存儲(chǔ)著棧中的一個(gè)地址(原EBP入棧后的棧頂),
;從該地址為基準(zhǔn),向上(棧底方向)能獲取返回地址、參數(shù)值(假如main中有參數(shù),“獲取參數(shù)值”會(huì)比較容易理解,
;不過(guò)在看下邊的sum函數(shù)調(diào)用時(shí)會(huì)有體會(huì)的),向下(棧頂方向)能獲取函數(shù)局部變量值,
;而該地址處又存儲(chǔ)著上一層函數(shù)調(diào)用時(shí)的EBP值!
00401063 sub esp,44h
;把esp往上移動(dòng)一個(gè)范圍
;等于在棧中空出一片空間來(lái)存局部變量
;執(zhí)行這句后ESP = 0012FF04 EBP = 0012FF48
00401066 push ebx
00401067 push esi
00401068 push edi
;保存三個(gè)寄存器的值
00401069 lea edi,[ebp-44h]
;把ebp-44h加載到edi中,目的是保存局部變量的區(qū)域
0040106C mov ecx,11h
00401071 mov eax,0CCCCCCCCh
00401076 rep stos dword ptr [edi]
;從ebp-44h開(kāi)始的區(qū)域初始化成全部0CCCCCCCCh,就是int3斷點(diǎn),初始化局部變量空間
;REP ;CX不等于0 ,則重復(fù)執(zhí)行字符串指令
;格式: STOS OPRD
;功能: 把AL(字節(jié))或AX(字)中的數(shù)據(jù)存儲(chǔ)到DI為目的串地址指針?biāo)鶎ぶ返拇鎯?chǔ)器單元中去.指針DI將根據(jù)DF的值進(jìn)行自動(dòng)
;調(diào)整. 其中OPRD為目的串符號(hào)地址.
;以上的語(yǔ)句就是在棧中開(kāi)辟一塊空間放局部變量
;然后把這塊空間都初始化為0CCCCCCCCh,就是int3斷點(diǎn),一個(gè)中斷指令。
;因?yàn)榫植孔兞坎豢赡鼙粓?zhí)行,執(zhí)行了就會(huì)出錯(cuò),這時(shí)候發(fā)生中斷提示開(kāi)發(fā)者。
13: int result = sum(1,2,3,4);
00401078 push 4
0040107A push 3
0040107C push 2
0040107E push 1
;各個(gè)參數(shù)入棧,注意查看寄存器ESP值的變化
;亦可以看到參數(shù)入棧的順序,從右到左
;變化為:ESP = 0012FEF8-->ESP = 0012FEF4-->ESP = 0012FEF0-->ESP = 0012FEEC-->ESP = 0012FEE8
00401080 call @ILT+15(boxer) (00401014)
;調(diào)用sum函數(shù),可以按F11跟進(jìn)
;注:f10(step over),單步調(diào)試,遇到函數(shù)調(diào)用,直接執(zhí)行,不會(huì)進(jìn)入函數(shù)內(nèi)部
;f11(step into),單步調(diào)試,遇到函數(shù)調(diào)用,會(huì)進(jìn)入函數(shù)內(nèi)部
;shift+f11(step out),進(jìn)入函數(shù)內(nèi)部后,想從函數(shù)內(nèi)部跳出,用此快捷方式
;ctrl+f10(run to cursor),呵呵,看英語(yǔ)注釋就應(yīng)該知道是什么意思了,不再解釋
00401085 add esp,10h
;調(diào)用完函數(shù)后恢復(fù)/釋放棧,執(zhí)行后ESP = 0012FEF8,與sum函數(shù)的參數(shù)入棧前的數(shù)值一致
00401088 mov dword ptr [ebp-4],eax
;將結(jié)果存放在result中,原因詳看最后有關(guān)ss的注釋
14: system("pause");
0040108B push offset string "pause" (00422f6c)
00401090 call system (0040eed0)
00401095 add esp ,4
;有關(guān)system(“pause”)的處理,此處不討論
15: }
00401098 pop edi
00401099 pop esi
0040109A pop ebx
;恢復(fù)原來(lái)寄存器的值,怎么“吃”進(jìn)去,怎么“吐”出來(lái)
0040109B add esp,44h
;恢復(fù)ESP,對(duì)應(yīng)上邊的sub esp,44h
0040109E cmp ebp,esp
;檢查esp是否正常,不正常就進(jìn)入下邊的call里面debug
004010A0 call __chkesp (004010b0)
;處理可能出現(xiàn)的堆棧異常,如果有的話,就會(huì)陷入debug
004010A5 mov esp,ebp
004010A7 pop ebp
;恢復(fù)原來(lái)的esp和ebp,讓上一個(gè)調(diào)用函數(shù)正常使用
004010A8 ret
;將返回地址存入eip,轉(zhuǎn)移流程
;如果函數(shù)有返回值,返回值將放在eax返回(這就是很多軟件給秒殺爆破的原因了,因?yàn)閑ax的返回值是可以改的)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
;以上即是主函數(shù)調(diào)用的反匯編過(guò)程,下邊來(lái)看調(diào)用sum函數(shù)的過(guò)程:
;上邊有說(shuō)在00401080 call @ILT+15(boxer) (00401014)這一句處,用f11單步調(diào)試,f11后如下句:
00401014 jmp sum (00401020)
;即跳轉(zhuǎn)到sum函數(shù)的代碼段中,再f11如下:
6: int sum(int a,int b,int m,int n)
7: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
;可見(jiàn),上邊幾乎與主函數(shù)調(diào)用相同,每一步不再贅述,可對(duì)照上邊主函數(shù)調(diào)用的注釋
8: return a+b;
00401038 mov eax,dword ptr [ebp+8]
;取第一個(gè)參數(shù)放在eax
0040103B add eax,dword ptr [ebp+0Ch]
;取第二個(gè)參數(shù),與eax中的數(shù)值相加并存在eax中
9: }
0040103E pop edi
0040103F pop esi
00401040 pop ebx
00401041 mov esp,ebp
00401043 pop ebp
00401044 ret
;收尾操作,比前邊只是少了檢查esp操作罷了
有關(guān)ss部分的注釋:
;一般而言,ss:[ebp+4]處為返回地址
;ss:[ebp+8]處為第一個(gè)參數(shù)值(這里是a),ss:[ebp+0Ch]處為第二個(gè)參數(shù)(這里是b,這里8+4=12=0Ch)
;ss:[ebp-4]處為第一個(gè)局部變量(如main中的result),ss:[ebp]處為上一層EBP值
;ebp和函數(shù)返回值是32位,所以占4個(gè)字節(jié)
《完》