日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向

發(fā)布時(shí)間:2023/11/30 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

虛函數(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] }

如果沒有Debug的符號(hào)文件,或者逆向過程中代碼不是我們自己寫的,那就要先判斷它是否是一個(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

通過閱讀以上代碼可以得出以下過程:

1)找出虛表位置,以及操作的流程

  • 代碼里的例子操作了虛表 00126FF6 mov dword ptr [eax],offset CObj::`vftable' (01B5E54h)

這是一個(gè)保存函數(shù)地址的指針,再通過匯編上下文的猜測(cè),則可大致確定這就是一個(gè)虛表,且將值傳到了寄存器參數(shù)ecx記錄地址的第一項(xiàng)。

  • 以寄存器參數(shù)ecx為首地址,分別給其4偏移與8偏移處賦值

  • 寄存器參數(shù)ecx又作為返回值傳了回去。

  • 通過調(diào)用函數(shù)的分析,ecx里保存的是this指針,并且根據(jù)類的內(nèi)存結(jié)構(gòu)可知,this里的第一項(xiàng)是Vptr。

2)識(shí)別構(gòu)造函數(shù)

  • 由于此成員函數(shù)是第一個(gè)被調(diào)用的,通過代碼看出匯編函數(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出來的對(duì)象會(huì)以其在堆中申請(qǐng)空間的指針作為this指針傳入?yún)⑴c構(gòu)造。
  • new出來的對(duì)象其虛函數(shù)調(diào)用的尋址方式與普通構(gòu)造出來的不同。
  • delete對(duì)象時(shí)會(huì)先析構(gòu)自己,再析構(gòu)父類,最后再執(zhí)行delete。
  • new出來的對(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)

版本對(duì)應(yīng)動(dòng)態(tài)庫(kù)靜態(tài)庫(kù)中使用MFC時(shí)的特征動(dòng)態(tài)庫(kù)中使用MFC的特征
4.0mfc40.dllcall [ebp+0x14]call [ebp+0x14]
6.0mfc42.dllcall [ebp+0x14]call [ebp+0x14]
7.1mfc71.dllcall [ebp+0x14]call [ebp+0x14]
10.0mfc100.dllcall [ebp+0x14]mov edx,[ebp+0x14]

分析核心重點(diǎn)

1)判斷目標(biāo)程序是不是MFC程序,如果是,判斷其MFC版本

OD快捷鍵:Ctrl+E 打開模塊窗口,并在模塊窗口尋找類似于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ǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。