最近在看MFC逆向方面的资料,找了些国外的资料,第一次翻译东西,文章没有翻译难度,但这篇文章对我个人很有帮助,而分享才是最重要的,所以发了出来,希望对初识MFC逆向的朋友有所帮助。
目录
1 MFC逆向指导
1.1 相关工具
1.2 序言: 什么是MFC?
1.3 简介
1.4 实验
1.4.1 MFC 主过程
1.4.2 获取 MESSAGE_MAP
1.4.2.1 IDC 脚本
1.4.3 利用 WM_COMMAND
1.5 最后说明
相关工具
IDA
Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI
Crackme
前言:什么是MFC?
微软基础类库(也称为微软基础类 或 MFC)用C++类包装了部分Windows API,并包含了一个应用程序框架。类被定义为很多用句柄来管理的窗口对象,另外还有预定义窗口和各种通用控件。
介绍
使用MFC进行软件开发需要导入MFC80U.dll(MFC80U 是笔者写本文为止最近的一个版本的dll),视编译类型而定,可以使一个静态库或者共享DLL。
我将分析一个导入了这个dll并拥有调试信息的软件,这样会使我们的工作更容易一些。
一旦你通过这种方式而理解了MFC,你就可以通过在IDA中添加MFC和VisualC的签名来分析用MFC开发软件。
实验
这是windows下的一段标准的C源代码:
代码:
LRESULT CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_ABOUT: DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MainDialogProc, 0); break; // ... } } }
代码:
class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) //CAboutDlg::IDD is dialog ID { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //Dialog Message Map: is like DialogProc END_MESSAGE_MAP() // App command to run the dialog void CProvaRevApp::OnAppAbout() { CAboutDlg aboutDlg; aboutDlg.DoModal(); }
MFC Main
这是目标程序的主要反汇编:
代码:
.text:00401CBB public start .text:00401CBB call ___security_init_cookie .text:00401CC0 jmp ___tmainCRTStartup .text:004019FB ___tmainCRTStartup proc near ; CODE XREF: start+5j .text:004019FB .text:004019FB push 5Ch .text:004019FD push offset unk_403DD8 .text:00401A02 call __SEH_prolog4 ;... other initialization code .text:00401B3E push ecx ; nShowCmd .text:00401B3F push eax ; lpCmdLine .text:00401B40 push ebx ; hPrevInstance .text:00401B41 push 400000h ; hInstance .text:00401B46 call _wWinMain@16 ; wWinMain(x,x,x,x) ; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) _wWinMain@16 proc near jmp ?AfxWinMain@@YGHPAUHINSTANCE__@@0PA_WH@Z ; AfxWinMain(HINSTANCE__ *,HINSTANCE__ *,wchar_t *,int) _wWinMain@16 endp
如果你安装了VisualStudio你可以看下MFC的源代码,在这篇文章中我只会讲解我们要用到的函数。
代码:
[ int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL); int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); // AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure; // App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) goto InitFailure; // Perform specific initializations if (!pThread->InitInstance()) { if (pThread->m_pMainWnd != NULL) { TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n"); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } nReturnCode = pThread->Run(); InitFailure: AfxWinTerm(); return nReturnCode; }
代码:
.text:7831D2D2 public AfxWinMain .text:7831D2D2 AfxWinMain proc near .text:7831D2D2 push ebx .text:7831D2D3 push esi .text:7831D2D4 push edi .text:7831D2D5 or ebx, 0FFFFFFFFh .text:7831D2D8 call AfxGetModuleThreadState .text:7831D2DD mov esi, [eax+4] ;pThread .text:7831D2E0 call AfxGetModuleState .text:7831D2E5 push [esp+0Ch+arg_C] .text:7831D2E9 mov edi, [eax+4] ;pApp .text:7831D2EC push [esp+10h+arg_8] .text:7831D2F0 push [esp+14h+arg_4] .text:7831D2F4 push [esp+18h+arg_0] .text:7831D2F8 call AfxWinInit .text:7831D2FD test eax, eax .text:7831D2FF jz short loc_7831D33D .text:7831D301 test edi, edi .text:7831D303 jz short loc_7831D313 .text:7831D305 mov eax, [edi] .text:7831D307 mov ecx, edi .text:7831D309 call dword ptr [eax+98h] .text:7831D30F test eax, eax .text:7831D311 jz short loc_7831D33D .text:7831D313 .text:7831D313 loc_7831D313: .text:7831D313 mov eax, [esi] .text:7831D315 mov ecx, esi .text:7831D317 call dword ptr [eax+58h] .text:7831D31A test eax, eax .text:7831D31C jnz short loc_7831D334 .text:7831D31E cmp [esi+20h], eax .text:7831D321 jz short loc_7831D32B .text:7831D323 mov ecx, [esi+20h] .text:7831D326 mov eax, [ecx] .text:7831D328 call dword ptr [eax+68h] .text:7831D32B .text:7831D32B loc_7831D32B: .text:7831D32B mov eax, [esi] .text:7831D32D mov ecx, esi .text:7831D32F call dword ptr [eax+70h] .text:7831D332 jmp short loc_7831D33B .text:7831D334 .text:7831D334 loc_7831D334: .text:7831D334 mov eax, [esi] .text:7831D336 mov ecx, esi .text:7831D338 call dword ptr [eax+5Ch] .text:7831D33B .text:7831D33B loc_7831D33B: .text:7831D33B mov ebx, eax .text:7831D33D .text:7831D33D loc_7831D33D: .text:7831D33D call AfxWinTerm .text:7831D342 pop edi .text:7831D343 pop esi .text:7831D344 mov eax, ebx .text:7831D346 pop ebx .text:7831D347 retn 10h .text:7831D347 AfxWinMain endp
EDI(pApp) 的值为40349C VA,指向的位置是CwinApp中保存的虚函数表。
代码:
.rdata:0040349C off_40349C dd offset ?GetRuntimeClass@CWinApp@@UBEPAUCRuntimeClass@@XZ ;CWinApp::GetRuntimeClass(void) .rdata:004034A0 dd offset sub_401010 .rdata:004034A4 dd offset nullsub_1 .rdata:004034A8 dd offset nullsub_2 .rdata:004034AC dd offset nullsub_1 .rdata:004034B0 dd offset ?OnCmdMsg@CCmdTarget@@UAEHIHPAXPAUAFX_CMDHANDLERINFO@@@Z ; CCmdTarget::OnCmdMsg(uint,int,void *,AFX_CMDHANDLERINFO *) .rdata:004034B4 dd offset ?OnFinalRelease@CCmdTarget@@UAEXXZ ; CCmdTarget::OnFinalRelease(void) .rdata:004034B8 dd offset ?IsInvokeAllowed@CCmdTarget@@UAEHJ@Z ; CCmdTarget::IsInvokeAllowed(long) .rdata:004034BC dd offset ?GetDispatchIID@CCmdTarget@@UAEHPAU_GUID@@@Z ; CCmdTarget::GetDispatchIID(_GUID *) .rdata:004034C0 dd offset ?GetTypeInfoCount@CCmdTarget@@UAEIXZ ; CCmdTarget::GetTypeInfoCount(void) .rdata:004034C4 dd offset ?GetTypeLibCache@CCmdTarget@@UAEPAVCTypeLibCache@@XZ ; CCmdTarget::GetTypeLibCache(void) .rdata:004034C8 dd offset ?GetTypeLib@CCmdTarget@@UAEJKPAPAUITypeLib@@@Z ; CCmdTarget::GetTypeLib(ulong,ITypeLib * *) .rdata:004034CC dd offset sub_401000 ;.......................................................
代码:
.text:004023B0 sub_4023B0 proc near .text:004023B0 push 0 .text:004023B2 mov ecx, offset dword_405498 .text:004023B7 call ??0CWinApp@@QAE@PB_W@Z ; CWinApp::CWinApp(wchar_t const *) .text:004023BC push offset sub_4023F0 ; void (__cdecl *)() .text:004023C1 mov dword_405498, offset off_40349C ;<-- this is our offset .text:004023CB call _atexit .text:004023D0 pop ecx .text:004023D1 retn
代码:
.rdata:00403304 unk_403304 db 0 .rdata:00403305 db 0 .rdata:00403306 db 0 .rdata:00403307 db 0 .rdata:00403308 dd offset _pre_cpp_init .rdata:0040330C dd offset ??__E_afxInitAppState@@YAXXZ ; `dynamic initializer for '_afxInitAppState''(void) .rdata:00403310 dd offset sub_4023B0
代码:
.text:00401AAC push offset unk_403314 .text:00401AB1 push offset unk_403304 .text:00401AB6 call _initterm
call dword ptr [eax+98h] (40349C + 98 = 00403534) 调用...
代码:
.text:00403534 dd offset ?InitApplication@CWinApp@@UAEHXZ ; CWinApp::InitApplication(void)
代码:
.rdata:004034F4 dd offset sub_401030
.
代码:
text:00401030 sub_401030 proc near .text:00401030 push ebp .text:00401031 mov ebp, esp ;.......................................................................... .text:0040109F call sub_401130 ;-------------------------------------------------------------------------- ;entrato nella call .text:00401155 push 0 ; lpIconName .text:00401157 push 66h ; Dialog ID .text:00401159 mov ecx, esi .text:0040115B call ??0CDialog@@QAE@IPAVCWnd@@@Z ; CDialog::CDialog(uint,CWnd *) .text:00401160 mov [esp+14h+var_4], 0 .text:00401168 mov dword ptr [esi], offset off_403744 ;virtual functions table offset which is store ; in CDialog.DoModal -> CDialog__PreModal -> AfxHookWindowCreate .text:0040116E call ?AfxGetModuleState@@YGPAVAFX_MODULE_STATE@@XZ ; AfxGetModuleState(void) ;exit the call ;--------------------------------------------------------------------------- .text:004010A4 lea edx, [esp+8+arg_4] .text:004010A8 mov [esp+8+arg_88], 0 .text:004010B3 mov ecx, edx .text:004010B5 mov [esi+20h], edx .text:004010B8 call ?DoModal@CDialog@@UAEHXZ ; CDialog::DoModal(void) .text:004010BD lea ecx, [esp+8+arg_4] .text:004010C1 mov [esp+8+arg_88], 0FFFFFFFFh .text:004010CC call ??1CDialog@@UAE@XZ ; CDialog::~CDialog(void) ;.......................................................................... .text:004010E3 mov esp, ebp .text:004010E5 pop ebp .text:004010E6 retn
但MESSAGE_MAP在哪呢? Message Map 可以再这里找到:
代码:
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { // .... const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(); // .... if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL) // ... }
代码:
.text:78312E91 mov eax, [edi] ; eax = 403744 .text:78312E93 mov ecx, edi .text:78312E95 call dword ptr [eax+30h] ; eax+30h = 00403774 = GetMessageMap() ;.rdata:00403774 dd offset sub_4011E0 ;................................................................... .text:78312F1B push 0 .text:78312F1D push 0 .text:78312F1F jnb short loc_78312F67 .text:78312F21 push [ebp+arg_0] ;messagge .text:78312F24 push dword ptr [esi+4] ; lpEntries (0040362C) .text:78312F27 call AfxFindMessageEntry
代码:
;GetMessageMap() .text:004011E0 mov eax, offset off_403628 ;eax = pMessageMap .text:004011E5 retn ;---------------------------------------------------------------- ;pMessageMap .rdata:00403628 off_403628 dd offset ?GetThisMessageMap@CDialog@@KGPBUAFX_MSGMAP@@XZ .rdata:00403628 ; CDialog::GetThisMessageMap(void) .rdata:0040362C dd offset unk_403580 ;pMessageMap->lpEntries
我们可以通过下面的方式快速获取MessageMap:
1. 在CDialog:DoModal调用之前找一条指令,类似于: mov dword ptr [esi], offset off_XXXXXX (通常是用来定位虚函数表的)。
2. 在指令中的offset地址上加0x30就得到了GetMessageMap函数: 在函数中找到一条指令 mov eax, offset off_XXXXXX, eax 为 pMessageMap。
3. 在 pMessageMap上+4 就得到了对话框的MessageMap。
现在来看一个例子。 这是软件资源信息:
代码:
CONTROL "Register", 1006, BUTTON, //1006 = 0x3ee CONTROL "About", 1007, BUTTON, //1007 = 0x3ef CONTROL "Cancel", 1008, BUTTON, //1008 = 0x3f0
代码:
struct AFX_MSGMAP_ENTRY { UINT nMessage; // windows message UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages) UINT nLastID; // used for entries specifying a range of control id's UINT_PTR nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) }; .rdata:00403580 MESSAGE_MAP dd 112h .rdata:00403584 dd 0 .rdata:00403588 dd 0 .rdata:0040358C dd 0 .rdata:00403590 dd 1Eh .rdata:00403594 dd offset sub_4012D0 .rdata:00403598 dd 0Fh .rdata:0040359C dd 0 .rdata:004035A0 dd 0 .rdata:004035A4 dd 0 .rdata:004035A8 dd 13h .rdata:004035AC dd offset sub_401370 .rdata:004035B0 dd 37h .rdata:004035B4 dd 0 .rdata:004035B8 dd 0 .rdata:004035BC dd 0 .rdata:004035C0 dd 28h .rdata:004035C4 dd offset sub_401450 .rdata:004035C8 dd 111h .rdata:004035CC dd 0 .rdata:004035D0 dd 3EFh .rdata:004035D4 dd 3EFh .rdata:004035D8 dd 38h .rdata:004035DC dd offset sub_401460 .rdata:004035E0 dd 111h .rdata:004035E4 dd 0 .rdata:004035E8 dd 3F0h .rdata:004035EC dd 3F0h .rdata:004035F0 dd 38h .rdata:004035F4 dd offset sub_4014F0 .rdata:004035F8 dd 111h .rdata:004035FC dd 0 .rdata:00403600 dd 3EEh .rdata:00403604 dd 3EEh .rdata:00403608 dd 38h .rdata:0040360C dd offset sub_401510 .rdata:00403610 dd 0 ...
IDC 脚本
代码:
// mfc_message_map.idc version 0.2 by Pn 2008 #include <idc.idc> //Only some WM_ command are recognized static messageName(ptr, message) { if(message == 1) // WM_CREATE MakeComm(ptr, "WM_CREATE"); else if(message == 2) // WM_DESTROY MakeComm(ptr, "WM_DESTROY"); else if(message == 5) // WM_SIZE MakeComm(ptr, "WM_SIZE"); else if(message == 0x10) // WM_CLOSE MakeComm(ptr, "WM_CLOSE"); else if(message == 0x18) // WM_SHOWWINDOW MakeComm(ptr, "WM_SHOWWINDOW"); else if(message == 0x0100) // WM_KEYDOWN MakeComm(ptr, "WM_KEYDOWN"); else if(message == 0x0101) // WM_KEYUP MakeComm(ptr, "WM_KEYUP"); else if(message == 0x0102) // WM_CHAR MakeComm(ptr, "WM_KEYCHAR"); else if(message == 0x0110) // WM_INITDIALOG MakeComm(ptr, "WM_INITDIALOG"); else if(message == 0x0111) // WM_COMMAND MakeComm(ptr, "WM_COMMAND"); else if(message == 0x0112) // WM_SYSCOMMAND MakeComm(ptr, "WM_SYSCOMMAND"); else if(message == 0x0113) // WM_TIMER MakeComm(ptr, "WM_TIMER"); else if(message == 0x0116) // WM_INITMENU MakeComm(ptr, "WM_INITMENU"); else if(message == 0x0117) // WM_INITMENUPOPUP MakeComm(ptr, "WM_INITMENUPOPUP"); else if(message == 0x0126) // WM_MENUCOMMAND MakeComm(ptr, "WM_MENUCOMMAND"); } static DefineStruct() { auto idStruct; idStruct = AddStrucEx(-1,"AFX_MSGMAP_ENTRY",0); if(idStruct == 0) return 0; if(AddStrucMember(idStruct, "nMessage", 0, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning("\n1\n"); DelStruc(idStruct); return 0; } if(AddStrucMember(idStruct, "nCode", 4, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning("\n2\n"); DelStruc(idStruct); return 0; } if(AddStrucMember(idStruct, "nID", 8, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning("\n3\n"); DelStruc(idStruct); return 0; } if(AddStrucMember(idStruct, "nLastID", 12, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning("\n4\n"); DelStruc(idStruct); return 0; } if(AddStrucMember(idStruct, "nSignature", 16, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning("\n5\n"); DelStruc(idStruct); return 0; } if(AddStrucMember(idStruct, "pFunction", 20, FF_DWRD|FF_0OFF, -1, 4) != 0) { Warning("\n6\n"); DelStruc(idStruct); return 0; } return idStruct; } static GenerateMFCMap(addr) { auto idStruct, ptr, message, isOk; idStruct = GetStrucIdByName("AFX_MSGMAP_ENTRY"); if( idStruct == -1) { idStruct = DefineStruct(); if(idStruct == 0) { Warning("\nCannot declare the structure\n"); return; } } ptr = addr; isOk = 1; while( Dword(ptr) != 0) { if(MakeStructEx(ptr, 24, "AFX_MSGMAP_ENTRY") == 0) { isOk = 0; break; } messageName(ptr,Dword(ptr)); ptr = ptr + 24; } if(isOk == 0) { Warning("\nCannot set the structure at %x\n", addr); } else { Message("Completed"); } return; }
代码:
.rdata:00403580 stru_403580 AFX_MSGMAP_ENTRY <112h, 0, 0, 0, 1Eh, offset sub_4012D0> ; WM_SYSCOMMAND .rdata:00403580 ; DATA XREF: .rdata:0040362Co .rdata:00403598 AFX_MSGMAP_ENTRY <0Fh, 0, 0, 0, 13h, offset sub_401370> .rdata:004035B0 AFX_MSGMAP_ENTRY <37h, 0, 0, 0, 28h, offset sub_401450> .rdata:004035C8 AFX_MSGMAP_ENTRY <111h, 0, 3EFh, 3EFh, 38h, offset sub_401460> ; WM_COMMAND .rdata:004035E0 AFX_MSGMAP_ENTRY <111h, 0, 3F0h, 3F0h, 38h, offset sub_4014F0> ; WM_COMMAND .rdata:004035F8 AFX_MSGMAP_ENTRY <111h, 0, 3EEh, 3EEh, 38h, offset sub_401510> ; WM_COMMAND .rdata:00403610 db 0
函数 BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo), 准确的说是函数 _AfxDispatchCmdMsg, 来处理WM_COMMOND消息。
实际上如果你设置一个断点在它上面,当你点击按钮或者菜单的时候调试器将会停下。然后你就可以步入这个事件的处理函数,而不用检索 MESSAGE_MAP。
最后说明
感谢看到这里的所有朋友。
文章来源:http://quequero.org/Basic_MFC_Reversing(eng)#Guidelines_to_MFC_reversing