【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向
虛函數(shù)存在是為了克服類型域解決方案的缺陷,以使程序員可以在基類里聲明一些能夠在各個(gè)派生類里重新定義的函數(shù)。
1 識(shí)別簡(jiǎn)單的虛函數(shù)
代碼示例:
#include "stdafx.h" #include <Windows.h>class CObj { public:CObj():m_Obj_1(0xAAAAAAAA),m_Obj_2(0xBBBB){printf("CObj() Constructor...\r\n");}~CObj(){printf("CObj() Destructor...\r\n");}virtual void Show(int nID) // 注意這里{m_Obj_1 = 1;printf("ID:%d Who is your God? I am!\r\n",nID);} private:int m_Obj_1;WORD m_Obj_2; };class CPeople : public CObj { public:CPeople():m_People_1(0xCCCCCCCC),m_People_2(0xDDDD){printf("CPeople() Constructor...\r\n");}~CPeople(){printf("CPeople() Destructor...\r\n");}void Show(int nID){printf("ID:%d People!\r\n",nID);} private:int m_People_1;WORD m_People_2; };int _tmain(int argc, _TCHAR* argv[]) {CObj obj;CPeople people;CObj *pobj;pobj = &obj;pobj->Show(0);pobj = &people;pobj->Show(1);return 0; } // ---------- 輸出結(jié)果 ---------- // CObj() Constructor... // CObj() Constructor... // CPeople() Constructor... // ID:0 Who is your God? I am! // ID:1 People! // CPeople() Destructor... // CObj() Destructor... // CObj() Destructor... // ----------------------------反匯編代碼:
int _tmain(int argc, _TCHAR* argv[]) { 001273B0 push ebp 001273B1 mov ebp,esp 001273B3 push 0FFFFFFFFh 001273B5 push 1B3730h 001273BA mov eax,dword ptr fs:[00000000h] 001273C0 push eax 001273C1 sub esp,108h 001273C7 push ebx 001273C8 push esi 001273C9 push edi 001273CA lea edi,[ebp+FFFFFEECh] 001273D0 mov ecx,42h 001273D5 mov eax,0CCCCCCCCh 001273DA rep stos dword ptr es:[edi] 001273DC mov eax,dword ptr ds:[001D9004h] 001273E1 xor eax,ebp 001273E3 push eax 001273E4 lea eax,[ebp-0Ch] 001273E7 mov dword ptr fs:[00000000h],eax ; 棧保護(hù)基址相關(guān)代碼CObj obj; 001273ED lea ecx,[ebp-1Ch] ; this 指針 001273F0 call 00123D87 ; CObj::CObj (0123D87h) 001273F5 mov dword ptr [ebp-4],0 ; 異常處理的輔助標(biāo)志,以-1為結(jié)尾CPeople people; 001273FC lea ecx,[ebp-38h] ; this指針 001273FF call 001211DB ; CPeople::CPeople (01211DBh) 00127404 mov byte ptr [ebp-4],1 CObj *pobj;pobj = &obj; 00127408 lea eax,[ebp-1Ch] ; 將obj的this指針給eax 0012740B mov dword ptr [ebp-44h],eax ; 將this指針給pobj的指針pobj->Show(0); 0012740E mov esi,esp 00127410 push 0 ; 參數(shù)壓棧 00127412 mov eax,dword ptr [ebp-44h] 00127415 mov edx,dword ptr [eax] 00127417 mov ecx,dword ptr [ebp-44h] ; 將Obj的指針(指向的是 Obj的this指針)給ecx 0012741A mov eax,dword ptr [edx] ; 將Obj的this指針?biāo)赶虻牡谝豁?xiàng)的內(nèi)容(即Vtbl的第一個(gè)元素)給eax 0012741C call eax 0012741C ; 在調(diào)用完CPeople的構(gòu)造后,程序采用如下步驟實(shí)現(xiàn) 0012741C ; pobj = &obj; 0012741C ; pobj ->Show(0); 0012741C ; 0012741C ; 1、將創(chuàng)建完的Obj對(duì)象的this指針傳遞給pobj 0012741C ; 2、將指this指針給eax 0012741C ; 3、將this指針第一項(xiàng)(即虛函數(shù)表指針)傳遞給edx 0012741C ; 4、將pobj的值傳遞給ecx(注意此步) 0012741C ; 5、將既虛函數(shù)表數(shù)組的地址傳遞給eax 0012741C ; 6、調(diào)用eax 0012741E cmp esi,esp 00127420 call 00122329 pobj = &people; 00127425 lea eax,[ebp-38h] ; 將People的this指針傳給eax 00127428 mov dword ptr [ebp-44h],eax ; 將People的this指針給Objpobj->Show(1); 0012742B mov esi,esp 0012742D push 1 ; 參數(shù)壓棧 0012742F mov eax,dword ptr [ebp-44h] ; 將People的this指針給eax 00127432 mov edx,dword ptr [eax] ; 將this指針中的第一項(xiàng),即Vptr給edx 00127434 mov ecx,dword ptr [ebp-44h] ; 將People的this指針給ecx 00127437 mov eax,dword ptr [edx] ; 將Vptr指向的Vtbl給eax 00127439 call eax ; 調(diào)用eax 0012743B cmp esi,esp 0012743D call 00122329 ; __RTC_CheckEspreturn 0; 00127442 mov dword ptr [ebp+FFFFFEF0h],0 0012744C mov byte ptr [ebp-4],0 00127450 lea ecx,[ebp-38h] 00127453 call 00121E10 00127458 mov dword ptr [ebp-4],0FFFFFFFFh 0012745F lea ecx,[ebp-1Ch] 00127462 call 00123BC0 00127467 mov eax,dword ptr [ebp+FFFFFEF0h] }如果沒(méi)有Debug的符號(hào)文件,或者逆向過(guò)程中代碼不是我們自己寫(xiě)的,那就要先判斷它是否是一個(gè)類的應(yīng)用。
跟進(jìn)函數(shù)內(nèi)部情況:
class CObj { 00126FD0 push ebp 00126FD1 mov ebp,esp 00126FD3 sub esp,0CCh 00126FD9 push ebx 00126FDA push esi 00126FDB push edi 00126FDC push ecx 00126FDD lea edi,[ebp-0CCh] 00126FE3 mov ecx,33h 00126FE8 mov eax,0CCCCCCCCh 00126FED rep stos dword ptr es:[edi] 00126FEF pop ecx 00126FF0 mov dword ptr [this],ecx ; 取this指針 this == [ebp-8] 00126FF3 mov eax,dword ptr [this] ; 取this指針 00126FF6 mov dword ptr [eax],offset CObj::`vftable' (01B5E54h) public:CObj():m_Obj_1(0xAAAAAAAA),m_Obj_2(0xBBBB) 00126FFC mov eax,dword ptr [this] 00126FFF mov dword ptr [eax+4],0AAAAAAAAh ; 初始化m_Obj_1為0xAAAAAAAA 00127006 mov eax,0BBBBh ; 初始化m_Obj_2為0xBBBB 0012700B mov ecx,dword ptr [this] ; this指針 this == ecx-8 0012700E mov word ptr [ecx+8],ax printf("CObj() Constructor...\r\n"); 00127012 push offset string "CObj() Constructor...\r\n" (01B5E5Ch) printf("CObj() Constructor...\r\n"); 00127017 call _printf (0123D00h) 0012701C add esp,4 } 0012701F mov eax,dword ptr [this] ; 將this指針作為返回值 this == ebp-8 00127022 pop edi 00127023 pop esi 00127024 pop ebx 00127025 add esp,0CCh 0012702B cmp ebp,esp 0012702D call __RTC_CheckEsp (0122329h) 00127032 mov esp,ebp 00127034 pop ebp 00127035 ret通過(guò)閱讀以上代碼可以得出以下過(guò)程:
1)找出虛表位置,以及操作的流程
- 代碼里的例子操作了虛表 00126FF6 mov dword ptr [eax],offset CObj::`vftable' (01B5E54h)
這是一個(gè)保存函數(shù)地址的指針,再通過(guò)匯編上下文的猜測(cè),則可大致確定這就是一個(gè)虛表,且將值傳到了寄存器參數(shù)ecx記錄地址的第一項(xiàng)。
以寄存器參數(shù)ecx為首地址,分別給其4偏移與8偏移處賦值
寄存器參數(shù)ecx又作為返回值傳了回去。
通過(guò)調(diào)用函數(shù)的分析,ecx里保存的是this指針,并且根據(jù)類的內(nèi)存結(jié)構(gòu)可知,this里的第一項(xiàng)是Vptr。
2)識(shí)別構(gòu)造函數(shù)
- 由于此成員函數(shù)是第一個(gè)被調(diào)用的,通過(guò)代碼看出匯編函數(shù)中的第二件事是初始化數(shù)據(jù)成員。最后一件事是將this指針當(dāng)做返回值返回,所以推測(cè)該函數(shù)為構(gòu)造函數(shù)。
3)逐步分析函數(shù)
- 構(gòu)造函數(shù)與析構(gòu)函數(shù)會(huì)對(duì)Vptr操作。
- 在VS默認(rèn)設(shè)置下,構(gòu)造與析構(gòu)前都會(huì)有相應(yīng)的異常處理標(biāo)記置位操作。
- 虛函數(shù)的調(diào)用一般采用eax。
2 識(shí)別較復(fù)雜的虛函數(shù)
經(jīng)驗(yàn)小結(jié):
- new出來(lái)的對(duì)象會(huì)以其在堆中申請(qǐng)空間的指針作為this指針傳入?yún)⑴c構(gòu)造。
- new出來(lái)的對(duì)象其虛函數(shù)調(diào)用的尋址方式與普通構(gòu)造出來(lái)的不同。
- delete對(duì)象時(shí)會(huì)先析構(gòu)自己,再析構(gòu)父類,最后再執(zhí)行delete。
- new出來(lái)的對(duì)象如果其成員函數(shù)派生于純虛函數(shù),在delete時(shí)只調(diào)用父類的析構(gòu)。
- 如果此類為抽象類(包含純虛函數(shù)),那么其虛表的對(duì)應(yīng)項(xiàng)會(huì)填充指向庫(kù)函數(shù)__purecall的函數(shù)指針。
虛函數(shù)調(diào)用的固定模式,緊盯對(duì)各個(gè)虛表的操作。從而根據(jù)上下文即可大致確定虛函數(shù)的調(diào)用與類的析構(gòu)與構(gòu)造。
3 識(shí)別類的繼承關(guān)系
- 根據(jù)構(gòu)造函數(shù)內(nèi)的構(gòu)造順序分辨此函數(shù)所屬類的繼承情況
- 總結(jié)并記錄分析結(jié)果
- VS的release版中存在同時(shí)使用ecx、esi寄存器傳遞this指針的情況。
4 逆向MFC程序
MFC程序關(guān)鍵特征點(diǎn)
| 4.0 | mfc40.dll | call [ebp+0x14] | call [ebp+0x14] |
| 6.0 | mfc42.dll | call [ebp+0x14] | call [ebp+0x14] |
| 7.1 | mfc71.dll | call [ebp+0x14] | call [ebp+0x14] |
| 10.0 | mfc100.dll | call [ebp+0x14] | mov edx,[ebp+0x14] |
分析核心重點(diǎn)
1)判斷目標(biāo)程序是不是MFC程序,如果是,判斷其MFC版本
OD快捷鍵:Ctrl+E 打開(kāi)模塊窗口,并在模塊窗口尋找類似于mfc*.dll這樣的模塊。
如果找到了就可以根據(jù)DLL的名稱判定程序所用的MFC版本,如果找不到則證明這是一個(gè)在靜態(tài)庫(kù)中使用MFC的程序。
2)根據(jù)目標(biāo)程序調(diào)用MFC方式的不同而采取不同的方式搜索特征
OD快捷鍵:Ctrl+F 搜索特征 call [ebp+0x14]
由于搜索的特征位于消息分發(fā)函數(shù)里,因此特征指令所在的位置應(yīng)該是一個(gè)非常大的switch-case。
3)在合適的地方下斷點(diǎn),并跟進(jìn)到相應(yīng)消息的函數(shù)中。
設(shè)置按鈕點(diǎn)擊事件下斷點(diǎn),即可跟進(jìn)到達(dá)相應(yīng)消息的函數(shù)中。
這里可以參考:
看雪《MFC程序逆向》
https://bbs.pediy.com/thread-54150.htm
轉(zhuǎn)載于:https://www.cnblogs.com/17bdw/p/8851049.html
總結(jié)
以上是生活随笔為你收集整理的【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 驻马店看无精子症最好的医院推荐
- 下一篇: 20155320《网络对抗》Exp4 恶