c++反汇编与逆向分析
1. _cdecl: c\c++默認調(diào)用方式,調(diào)用方平衡棧,不定參數(shù)的函數(shù)可以使用??ret;?
2. _stdcall: 被調(diào)用方平衡棧,不定參數(shù)的函數(shù)不可以使用?????????ret 4;
3. _fastcall:寄存器方式傳參,被調(diào)用方平衡棧,不定參數(shù)的函數(shù)不可以使用, cx,dx分別存儲1,2參數(shù),其余參數(shù)還是用棧
call 指令? 隱含push 函數(shù)返回下一條指令地址
_cdecl 函數(shù)的結(jié)束"}"?mov???????? esp,ebp?
???????????pop???????? ebp?
???????????ret?????//ret指令 會把當(dāng)前esp指向值更新到eip,同時esp-4, 此時一個棧幀調(diào)用完成。
???????????//如果函數(shù)有返回值會先賦值給eax,再執(zhí)行ret.
int _fastcall fun22(int a, int b, int c,int d)
{??????????????????F9???ecx = a, edx = b esp存儲函數(shù)返回下一條指令地址 esp+4 = c esp+8 = d?????
008613C0? push??????? ebp?
008613C1? mov???????? ebp,esp?
008613C3? sub???????? esp,0E4h????? F9???ecx = a, edx = b ebp+4存儲函數(shù)返回下一條指令地址 ebp+8 = c ebp+0xc = d?
第七章:變量在內(nèi)存中的位置和訪問方式
作用域:全局變量屬于進程作用域;靜態(tài)變量屬于文件作用域;局部變量屬于函數(shù)作用域;
在大多數(shù)情況下,在PE文件中的只讀數(shù)據(jù)節(jié)中,常量的節(jié)屬性被修飾為不可寫;而全局變量和靜態(tài)變量則在屬性為可讀寫的數(shù)據(jù)節(jié)中。
全局變量和常量類似,被寫入文件中,當(dāng)用戶執(zhí)行該文件,OS分析并加載各節(jié)到虛擬內(nèi)在地址,這是全局變量已經(jīng)存在了,加載完才開始執(zhí)行入口點代碼。
全局變量的內(nèi)在地址在全局數(shù)據(jù)區(qū)中,通過棧指針是無法訪問到,訪問也與常量類似,都是通過立即數(shù)來訪問。局部變量使用ebp或esp間接訪問。
全局變量在內(nèi)存中的地址順序是先定義的變量在低地址,后定義的在高地址,局部變量剛好相反。
靜態(tài)變量:全局靜態(tài)變量,局部靜態(tài)變量
?全局靜態(tài)變量和全局變量類似,只是編譯器限制外部源碼文件訪問的全局變量。
?局部靜態(tài)變量會預(yù)先被作為全局變量處理,和全局變量都保存在執(zhí)行文件中的數(shù)據(jù)區(qū)中,只是被定義在某個作用域內(nèi);
??局部靜態(tài)變量的初始化只是在做賦值操作而已,c++語法規(guī)定局部靜態(tài)變量只被初始化一次。
?
?在第一個局部靜態(tài)變量附近分配一個地址存放標(biāo)志。
void InNumber(int var)
{
??? static int nInt = var;
??? static int nInt2 = var+2;
??? static int nInt3 = var+3;
??? printf("%d,%d,%d",nInt, nInt2, nInt3);
}
static int nInt = var;
00AC1003? mov???????? eax,dword ptr ds:[00AC3370h]?
00AC1008? and???????? eax,1?
00AC100B? jne???????? InNumber+25h (0AC1025h)?
00AC100D? mov???????? ecx,dword ptr ds:[0AC3370h]?
00AC1013? or????????? ecx,1?
00AC1016? mov???????? dword ptr ds:[0AC3370h],ecx?
00AC101C? mov???????? edx,dword ptr [var]?
00AC101F? mov???????? dword ptr ds:[0AC337Ch],edx?
??? static int nInt2 = var+2;
00AC1025? mov???????? eax,dword ptr ds:[00AC3370h]?
00AC102A? and???????? eax,2?
00AC102D? jne???????? InNumber+4Ah (0AC104Ah)?
00AC102F? mov???????? ecx,dword ptr ds:[0AC3370h]?
00AC1035? or????????? ecx,2?
00AC1038? mov???????? dword ptr ds:[0AC3370h],ecx?
00AC103E? mov???????? edx,dword ptr [var]?
00AC1041? add???????? edx,2?
00AC1044? mov???????? dword ptr ds:[0AC3374h],edx?
??? static int nInt3 = var+3;
00AC104A? mov???????? eax,dword ptr ds:[00AC3370h]?
00AC104F? and???????? eax,4?
00AC1052? jne???????? InNumber+6Fh (0AC106Fh)?
00AC1054? mov???????? ecx,dword ptr ds:[0AC3370h]?
00AC105A? or????????? ecx,4?
00AC105D? mov???????? dword ptr ds:[0AC3370h],ecx?
00AC1063? mov???????? edx,dword ptr [var]?
00AC1066? add???????? edx,3?
00AC1069? mov???????? dword ptr ds:[0AC3378h],edx?
??? printf("%d,%d,%d",nInt, nInt2, nInt3);
00AC106F? mov???????? eax,dword ptr ds:[00AC3378h]
如果如下
void InNumber(int var)
{
??static int nn = 33;???//nn將不再使用標(biāo)志,直接當(dāng)作全局變量處理,但作用域還是局部。
??? static int nInt = var;
通過名字粉碎來區(qū)別作用域,UE打開.obj文件如下:
nInt3@?1??InNumber@@YAXH@Z@4HA
$?nInt@?1??InNumber@@YAXH@Z@4HA
void Show(char szBuffer[])?//數(shù)組傳參
{
??? int wrongSize = sizeof(szBuffer);?//此時szBuffer為指針類型,并非數(shù)組, 4
??? int rightSize = strlen(szBuffer);?//取決外部傳入
??? strcpy_s(szBuffer, 20, "hello, my sunny!");
}
局部靜態(tài)數(shù)組和局部靜態(tài)變量有些不同,無論局部靜態(tài)數(shù)組有多少個元素,也只會檢查一次初始標(biāo)志位。
數(shù)組的下標(biāo)尋址比指針尋址高效。數(shù)組名本身就是常量地址。而指針需要取其指向的地址值。
第九章:結(jié)構(gòu)體和類
空類實際長度為1字節(jié),如果不占內(nèi)存,則無法得到空類實例的地址,this指針失效,因此不能被實例化;沒有數(shù)據(jù)成員,還可以有成員函數(shù)。
結(jié)構(gòu)體和類中的數(shù)據(jù)成員分配內(nèi)存時,結(jié)構(gòu)體中的當(dāng)前數(shù)據(jù)成員類型長度為M,指定的對齊值為N,那么實際對齊值為q=min(M,N),其成員的地址安排在q的倍數(shù)上。
結(jié)構(gòu)體中數(shù)據(jù)成員類型最大值為M,指定的對齊值為N,那么實際對齊值為min(M,N). 結(jié)構(gòu)體整體大小為min(M,N)的整數(shù)倍。
#pragma pack(N) //調(diào)整對齊大小。
當(dāng)結(jié)構(gòu)體中以數(shù)組作為成員時,將根據(jù)數(shù)組元素的長度計算對齊值,而不是按數(shù)組的整體大小去計算。
struct STR{
?char cChar;
?char cArray[4];
?short sShort;
};???
??STR ss = {'y',"and",4};
????//79 61 6e 64 00 cc 04 00
????//y??a? n? d??????? 4
void *pp = &((STR*)NULL)->sShort;?//取相對地址。
類的成員函數(shù)默認是thiscall調(diào)用方式,與_stdcall相同,被調(diào)用方負責(zé)平衡棧,但傳參不同,第一參數(shù)(this)使用寄存器ecx傳遞,而非棧頂。
thiscall不是關(guān)鍵字,不能顯示調(diào)用。
靜態(tài)數(shù)據(jù)成員和靜態(tài)變量原理相同(有作用域的特殊全局變量),初值會寫入編譯鏈接后的執(zhí)行文件后。所以不參與對象size計算。
類對象作為參數(shù),如果只是簡單變量,真依序壓棧對象的成員變量,最先定義的最后壓棧。如果沒有構(gòu)造函數(shù),也不會調(diào)系統(tǒng)默認的構(gòu)造函數(shù)。
class CRet
{
public:
??? int a;
??? int b[8];
};
CRet GetReturn()
{
??? CRet temp;
??? temp.a = 3;
??? for (int i = 0; i < 8; ++i)
??? {
??????? temp.b[i] = i + 6;
??? }
??? return temp;
}
int _tmain(int argc, _TCHAR* argv[])
{
??? CRet mobj;
??//1.此時main函數(shù)已經(jīng)臨時對象temp
??? mobj = GetReturn();?//2.GetReturn函數(shù)結(jié)束,棧幀關(guān)閉,所以棧幀關(guān)閉前會復(fù)制到temp,3. 再次temp賦值給mobj,過了這條語句作用域,temp消失。//如果重載了“=”,這里會調(diào)用=
???
??? 如果為如下://定義時賦初值才會調(diào)用拷貝構(gòu)造
??? CRet mobj = GetReturn(); //不會有臨時對象,而是直接把mobj的地址作為隱含的參數(shù)傳遞給GetReturn(),在GetReturn()函數(shù)內(nèi)部完成拷貝構(gòu)造的過程。
???
//note:
1. mobj = GetReturn(); ";"后會析構(gòu)
2. mobj = GetReturn(),
?printf("hell");??則是在這里的";"后才會析構(gòu)這個temp對象。
3. 當(dāng)引用這個temp對象時,生命期與引用相同。
?
//下面兩個有錯誤,不要返回局部變量
CRet* GetAA()
{
??? CRet temp;
??? return &temp;
}
CRet& GetBB()
{
??? CRet temp;
??? return temp;
}
thiscall 使用ecx傳this指針, __fastcall使用ecx用來傳第一個參數(shù),所以不能作為唯一識別特征。
局部對象
堆對象??new成功后再會調(diào)構(gòu)造函數(shù)
參數(shù)對象?調(diào)用拷貝構(gòu)造函數(shù),該構(gòu)造函數(shù)只有一個參數(shù),類型為對象的引用
返回對象
全局對象??//main函數(shù)退出后調(diào)用析構(gòu)函數(shù)?
靜態(tài)對象??//main函數(shù)退出后調(diào)用析構(gòu)函數(shù)
定點斷點, tools->options->debugging->symbols select Microsoft Symbols Servers to download symbols.
Ctrl+B than put in function name.
Ctrl+Alt+B list and edit all breakpoint, Condition and Hit Count.
編譯器在以下兩種情況下會提供默認的構(gòu)造函數(shù):
1.本類、本類中定義的成員對象或者父類中有虛函數(shù)存在;
2.父類或本類中定義的成員對象帶有構(gòu)造函數(shù);
如果沒有這兩種情況,默認構(gòu)造函數(shù)已經(jīng)沒有存在的意義,只會影響效率。
delete p; delete []p; 釋放對象類型標(biāo)志,1為單個對象,3為釋放對象數(shù)組,0表示僅僅執(zhí)行析構(gòu)函數(shù)。
申請對象數(shù)組時,由于對象都在同一個堆空間中,編譯器使用了堆空間的前4字節(jié)數(shù)據(jù)來保存對象的總個數(shù)。
析構(gòu)順序,調(diào)用自身的析構(gòu)函數(shù)->聲明倒序調(diào)用成員對象的析構(gòu)函數(shù)->調(diào)用基類析構(gòu)函數(shù)
基類聲明為虛析構(gòu)
CBase *psub = new CSub;
delete psub;
?????mov???????? edx,dword ptr [ebp-0ECh]? //傳遞this
0039183E? mov???????? eax,dword ptr [edx]? ???//取得虛表指針
00391840? mov???????? ecx,dword ptr [ebp-0ECh]? //傳遞this
00391846? mov???????? edx,dword ptr [eax]? ???//間接調(diào)用虛析構(gòu)函數(shù)
00391848? call??????? edx?
基類聲明為虛析構(gòu)
CBase *psub = new CSub;
delete psub;
?????mov???????? ecx,dword ptr [ebp-0ECh]? ??????//傳遞this
012A183C? call??????? CBase::`scalar deleting destructor' (012A124Eh)??//直接調(diào)用基類析構(gòu)函數(shù)
要習(xí)慣給析構(gòu)函數(shù)加virtual
總結(jié)
以上是生活随笔為你收集整理的c++反汇编与逆向分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Window核心编程
- 下一篇: c与c++遗漏点