C++反汇编代码分析
生活随笔
收集整理的這篇文章主要介紹了
C++反汇编代码分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C++反匯編代碼分析--函數調用
代碼如下:
#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");
}
有四個參數的sum函數,接著在main方法中調用sum函數。在debug環境下,單步調試如下:
11: ? void main()
12: ? {
00401060 ? push ? ? ? ?ebp
;保存ebp,執行這句之前,ESP = 0012FF4C EBP = 0012FF88
;執行后,ESP = 0012FF48 EBP = 0012FF88,ESP減小,EBP不變
00401061 ? mov ? ? ? ? ebp,esp
;將esp放入ebp中,此時ebp和esp相同,即執行后ESP = 0012FF48 EBP = 0012FF48
;原EBP值已經被壓棧(位于棧頂),而新的EBP又恰恰指向棧頂。
;此時EBP寄存器就已經處于一個非常重要的地位,該寄存器中存儲著棧中的一個地址(原EBP入棧后的棧頂),
;從該地址為基準,向上(棧底方向)能獲取返回地址、參數值(假如main中有參數,“獲取參數值”會比較容易理解,
;不過在看下邊的sum函數調用時會有體會的),向下(棧頂方向)能獲取函數局部變量值,
;而該地址處又存儲著上一層函數調用時的EBP值!
00401063 ? sub ? ? ? ? esp,44h
;把esp往上移動一個范圍
;等于在棧中空出一片空間來存局部變量
;執行這句后ESP = 0012FF04 EBP = 0012FF48
00401066 ? push ? ? ? ?ebx
00401067 ? push ? ? ? ?esi
00401068 ? push ? ? ? ?edi
;保存三個寄存器的值
00401069 ? lea ? ? ? ? edi,[ebp-44h]
;把ebp-44h加載到edi中,目的是保存局部變量的區域
0040106C ? mov ? ? ? ? ecx,11h
00401071 ? mov ? ? ? ? eax,0CCCCCCCCh
00401076 ? rep stos ? ?dword ptr [edi]
;從ebp-44h開始的區域初始化成全部0CCCCCCCCh,就是int3斷點,初始化局部變量空間
;REP ? ? ? ? ? ;CX不等于0 ,則重復執行字符串指令
;格式: STOS OPRD
;功能: 把AL(字節)或AX(字)中的數據存儲到DI為目的串地址指針所尋址的存儲器單元中去.指針DI將根據DF的值進行自動
;調整. 其中OPRD為目的串符號地址.
?
;以上的語句就是在棧中開辟一塊空間放局部變量
;然后把這塊空間都初始化為0CCCCCCCCh,就是int3斷點,一個中斷指令。
;因為局部變量不可能被執行,執行了就會出錯,這時候發生中斷提示開發者。
13: ? ? ? int result = sum(1,2,3,4);
00401078 ? push ? ? ? ?4
0040107A ? push ? ? ? ?3
0040107C ? push ? ? ? ?2
0040107E ? push ? ? ? ?1
;各個參數入棧,注意查看寄存器ESP值的變化
;亦可以看到參數入棧的順序,從右到左
;變化為:ESP = 0012FEF8-->ESP = 0012FEF4-->ESP = 0012FEF0-->ESP = 0012FEEC-->ESP = 0012FEE8
00401080 ? call ? ? ? ?@ILT+15(boxer) (00401014)
;調用sum函數,可以按F11跟進
;注:f10(step over),單步調試,遇到函數調用,直接執行,不會進入函數內部
;f11(step into),單步調試,遇到函數調用,會進入函數內部
;shift+f11(step out),進入函數內部后,想從函數內部跳出,用此快捷方式
;ctrl+f10(run to cursor),呵呵,看英語注釋就應該知道是什么意思了,不再解釋
00401085 ? add ? ? ? ? esp,10h
;調用完函數后恢復/釋放棧,執行后ESP = 0012FEF8,與sum函數的參數入棧前的數值一致
00401088 ? mov ? ? ? ? dword ptr [ebp-4],eax
;將結果存放在result中,原因詳看最后有關ss的注釋
14: ? ? ? system("pause");
0040108B ? push ? ? ? ?offset string "pause" (00422f6c)
00401090 ? call ? ? ? ?system (0040eed0)
00401095 ? add ? esp ,4
;有關system(“pause”)的處理,此處不討論
15: ? }
00401098 ? pop ? ? ? ? edi
00401099 ? pop ? ? ? ? esi
0040109A ? pop ? ? ? ? ebx
;恢復原來寄存器的值,怎么“吃”進去,怎么“吐”出來
0040109B ? add ? ? ? ? esp,44h
;恢復ESP,對應上邊的sub esp,44h
0040109E ? cmp ? ? ? ? ebp,esp
;檢查esp是否正常,不正常就進入下邊的call里面debug
004010A0 ? call ? ? ? ?__chkesp (004010b0)
;處理可能出現的堆棧異常,如果有的話,就會陷入debug
004010A5 ? mov ? ? ? ? esp,ebp
004010A7 ? pop ? ? ? ? ebp
;恢復原來的esp和ebp,讓上一個調用函數正常使用
004010A8 ? ret
;將返回地址存入eip,轉移流程
?
;如果函數有返回值,返回值將放在eax返回(這就是很多軟件給秒殺爆破的原因了,因為eax的返回值是可以改的)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
;以上即是主函數調用的反匯編過程,下邊來看調用sum函數的過程:
;上邊有說在00401080 ? call ? ? ? ?@ILT+15(boxer) (00401014)這一句處,用f11單步調試,f11后如下句:
00401014 ? jmp ? ? ? ? sum (00401020)
;即跳轉到sum函數的代碼段中,再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]
;可見,上邊幾乎與主函數調用相同,每一步不再贅述,可對照上邊主函數調用的注釋
8: ? ? ? ?return a+b;
00401038 ? mov ? ? ? ? eax,dword ptr [ebp+8]
;取第一個參數放在eax
0040103B ? add ? ? ? ? eax,dword ptr [ebp+0Ch]
;取第二個參數,與eax中的數值相加并存在eax中
9: ? ?}
0040103E ? pop ? ? ? ? edi
0040103F ? pop ? ? ? ? esi
00401040 ? pop ? ? ? ? ebx
00401041 ? mov ? ? ? ? esp,ebp
00401043 ? pop ? ? ? ? ebp
00401044 ? ret
;收尾操作,比前邊只是少了檢查esp操作罷了
?
有關ss部分的注釋:
;一般而言,ss:[ebp+4]處為返回地址
;ss:[ebp+8]處為第一個參數值(這里是a),ss:[ebp+0Ch]處為第二個參數(這里是b,這里8+4=12=0Ch)
;ss:[ebp-4]處為第一個局部變量(如main中的result),ss:[ebp]處為上一層EBP值
;ebp和函數返回值是32位,所以占4個字節
========
c++反匯編代碼分析--循環結構
在此主要討論或者驗證三點:
1、循環結構的反匯編代碼分析
2、函數中,局部變量的保存位置
3、方法的返回值保存位置驗證
一個沒有找到答案的疑問:
00401029 ? lea ? ? ? ? edi,[ebp-48h]
0040102C ? mov ? ? ? ? ecx,12h
00401031 ? mov ? ? ? ? eax,0CCCCCCCCh
;這段代碼是在棧中開辟一個48字節大小的區域來存放局部變量,但是如果函數內
沒有局部變量,則是lea ? ? ? ? edi,[ebp-40h]
一個局部變量,則是lea ? ? ? ? edi,[ebp-44h]
兩個局部變量,則是lea ? ? ? ? edi,[ebp-48h]
也就是沒有局部變量時開辟的40個字節,我用F11追蹤過,單步調試時,這一部分區域并沒有用到,這一區域的作用是什么?
代碼如下:
?1int sum()
?2{
?3 ? ?int subResult=0;
?4 ? ?for (int i=0;i<3;i++)
?5 ? ?{
?6 ? ? ? ?subResult+=1;
?7 ? ?}
?8 ? ?return subResult;
?9}
10
11void main()
12{
13 ? ?int result = sum();
14 ? ?printf("%\d\n",result);
15}
由于方法的調用已經在上一篇中說過,這里直接分析內部有循環結構的sum()方法
反匯編代碼及分析:
5: ? ?int sum()
6: ? ?{
00401020 ? push ? ? ? ?ebp
;ESP = 0012FEF0 EBP = 0012FF48
00401021 ? mov ? ? ? ? ebp,esp
;ESP = 0012FEF0 EBP = 0012FEF0
00401023 ? sub ? ? ? ? esp,48h
;ESP = 0012FEA8 EBP = 0012FEF0
00401026 ? push ? ? ? ?ebx
00401027 ? push ? ? ? ?esi
00401028 ? push ? ? ? ?edi
00401029 ? lea ? ? ? ? edi,[ebp-48h]
0040102C ? mov ? ? ? ? ecx,12h
00401031 ? mov ? ? ? ? eax,0CCCCCCCCh
00401036 ? rep stos ? ?dword ptr [edi]
7: ? ? ? ?int subResult=0;
00401038 ? mov ? ? ? ? dword ptr [ebp-4],0
8: ? ? ? ?for (int i=0;i<3;i++)
0040103F ? mov ? ? ? ? dword ptr [ebp-8],0
;[ebp-4]=[0012FEEC]處存放的即是局部變量subResult的位置
;[ebp-8]=[0012FEE8]處存放的即是局部變量i的位置
;參看下圖可知
;subResult和i的初值均為0
00401046 ? jmp ? ? ? ? sum+31h (00401051)
;轉到地址00401051(下方藍字)處去判斷循環條件是否滿足(請從00401051處接著往下看)
00401048 ? mov ? ? ? ? eax,dword ptr [ebp-8]
;將循環條件即i的值復制給eax
0040104B ? add ? ? ? ? eax,1
;循環條件修正
0040104E ? mov ? ? ? ? dword ptr [ebp-8],eax
;保存修正后的循環條件
00401051 ? cmp ? ? ? ? dword ptr [ebp-8],3
00401055 ? jge ? ? ? ? sum+42h (00401062)
;比較dword ptr [ebp-8]處的值,即局部變量i的值與3的大小,如果小于3,則往下執行;如果大于等于3,則跳轉到00401062處執行
;jge 指令 如果大于或等于則轉移
9: ? ? ? ?{
10: ? ? ? ? ? subResult+=1;
00401057 ? mov ? ? ? ? ecx,dword ptr [ebp-4]
;將dword ptr [ebp-4]處的值即subResult的值傳個寄存器ecx
0040105A ? add ? ? ? ? ecx,1
;通過寄存器ecx實現循環加1操作
0040105D ? mov ? ? ? ? dword ptr [ebp-4],ecx
;將加1后的值復制給dword ptr [ebp-4]處
11: ? ? ? }
00401060 ? jmp ? ? ? ? sum+28h (00401048)
;轉移到00401048處,去進行下一輪的循環變量修正和判斷
12: ? ? ? return subResult;
00401062 ? mov ? ? ? ? eax,dword ptr [ebp-4]
;將最終的結果復制給eax,由此可以驗證,函數的返回值保存在寄存器eax中
;為了更好的說明這一點,看下邊main函數中sum函數的返回值的傳遞情況:
13: ? }
00401065 ? pop ? ? ? ? edi
00401066 ? pop ? ? ? ? esi
00401067 ? pop ? ? ? ? ebx
00401068 ? mov ? ? ? ? esp,ebp
0040106A ? pop ? ? ? ? ebp
0040106B ? ret
========
c++反匯編代碼分析--偷調函數
注:不知道說“偷調函數”說法合不合適,在此也就這樣一說了~
主要有兩點:
一、再說C++反匯編函數調用,重點是怎樣通過堆棧實現由被調用函數轉到調用者
二、在 1 的基礎上,在WinDbg下通過修改EIP實現如下一個功能:
有兩個函數foo()和hack(),在main函數中調用foo,但是在foo執行過程中,通過修改EIP來調用hack函數,最后再回到main中foo函數的下一條語句
一、再說C++反匯編函數調用,重點是怎樣通過堆棧實現由被調用函數轉到調用者
程序如下(很簡單):
復制代碼
1 #include "stdafx.h"
2?
3 ?int MyAdd(int a,int b)
4 {
5 return a+b;
6 }
7?
8 ?void main()
9 {
10 MyAdd(1,2);
11 }
復制代碼
?
反匯編后如下:
void main()
11: ? {
00401080 ? push ? ? ? ?ebp
00401081 ? mov ? ? ? ? ebp,esp
00401083 ? sub ? ? ? ? esp,40h
00401086 ? push ? ? ? ?ebx
00401087 ? push ? ? ? ?esi
00401088 ? push ? ? ? ?edi
00401089 ? lea ? ? ? ? edi,[ebp-40h]
0040108C ? mov ? ? ? ? ecx,10h
00401091 ? mov ? ? ? ? eax,0CCCCCCCCh
00401096 ? rep stos ? ?dword ptr [edi]
12: ? ? ? MyAdd(1,2);
00401098 ? push ? ? ? ?2
0040109A ? push ? ? ? ?1?
;程序執行到這,堆棧內容如下(至于為什么是這,請參看《c++反匯編代碼分析--函數調用》)
?
?
0040109C ? call ? ? ? ?@ILT+15(hook) (00401014);
--------------------------------開始轉入MyAdd函數去執行--------------------------
;在執行0040109C ? call ? ? ? ?@ILT+15(hook) (00401014)到這句時,F11單步調試,會依次執行下邊的反匯編代碼:
00401014 ? jmp ? ? ? ? MyAdd (00401030)?
;執行到此句時,ESP和EBP還是原來的值嗎?
;我們可能會覺得,現在也沒有push操作,ESP和EBP應該還是應該如上圖一樣沒有變化吧
;非也,其實執行到這一句時,已經有一個自動的入棧操作,入棧的是0040109C ? call ? ? ? ?@ILT+15(hook) (00401014)
;這條指令的下一條指令的地址,具體如下圖所示:
;執行到0040109C ? call ? ? ? ?@ILT+15(hook) (00401014)這條語句時,如圖:
;執行到0040109C ? call ? ? ? ?@ILT+15(hook) (00401014)這條語句,按F11后,如下圖:
;此時的堆棧情況如下圖
;之后,轉入下邊的程序執行
5: ? ?int MyAdd(int a,int b)
6: ? ?{
00401030 ? push ? ? ? ?ebp
?
……
}
……
?
?
?
;執行過ret后,會自動將堆棧中retAddr的值彈給EIP,從而完成從被調用函數MyAdd轉到main函數中去執行。
;這一點十分重要,也就是 二 的理論基礎了吧。
00401053 ? pop ? ? ? ? ebp
00401054 ? ret
--------------------------MyAdd子函數執行完畢,在此進入main函數執行------------------------------------
004010A1 ? add ? ? ? ? esp,8
13: ? }
004010A4 ? pop ? ? ? ? edi
......
二、在 一 的基礎上,在WinDbg下通過修改EIP實現如下一個功能:......
程序如下:
?
復制代碼
#include <iostream>
usingnamespace std;
void foo()
{
printf("--foo--\n");
}
void hook()
{
printf("--hook--\n");
}
void main()
{
foo();
hook();
}
復制代碼
理論如圖:
輸出為:
--foo--
--hack--
--hack--
(具體實現參考 第一部分 結合這里給出的圖示,應該很快可以出來了,嘿嘿,困了,偷懶~有時間會將如何看反匯編代碼,如何查看寄存器,以及如何在windbg這個終極利器下調試程序等等做下總結,都是很基本的,包括今天費了老大勁,寫到半夜的東西,也沒有什么用,只是幫助理解,大俠們莫嘲笑,我只是初學階段,正在努力!)?
========
總結
以上是生活随笔為你收集整理的C++反汇编代码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows系统漏洞学习总结
- 下一篇: 图解VC++ opengl环境配置和几个