首页
论坛
专栏
课程

[翻译]浅析MFC逆向(Basic MFC Reversing)

2010-9-8 19:58 20436

[翻译]浅析MFC逆向(Basic MFC Reversing)

2010-9-8 19:58
20436
最近在看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;
                        
                        // ...
                }
        }
}

相反,这是一段MFC下的源代码:
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软件的反汇编是更难理解的。

MFC Main

这是目标程序的主要反汇编:
.text:00401CBB                 public start
.text:00401CBB                 call    ___security_init_cookie
.text:00401CC0                 jmp     ___tmainCRTStartup

.text:004019FB ___tmainCRTStartup proc near            ; CODE XREF: start+5�j
.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

如你所见WinMain调用了AfxWinMain。
如果你安装了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;
}

这是AfxWinMain函数的反汇编:
.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

在上面的代码中有一些调用如call [eax + XXh]:事实上对AfxGetApp(和AfxGetThread)的调用将返回一个指向结构的指针,这个结构记录了所有MFC框架所使用的函数的偏移。
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
;.......................................................

现在您应该会有个疑问,MFC在哪里得到的地址?让我们快来看一下它的引用…
.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

虚拟地址(VA)004023B0包含在一个结构中。
.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

WinMain运行之前由__initterm压入。
.text:00401AAC                 push    offset unk_403314
.text:00401AB1                 push    offset unk_403304 
.text:00401AB6                 call    _initterm

现在,让我们回到AfxWinMain中:
call dword ptr [eax+98h] (40349C + 98 = 00403534) 调用...
.text:00403534                 dd offset ?InitApplication@CWinApp@@UAEHXZ ; CWinApp::InitApplication(void)

...而 call dword ptr [eax+58h], 就是 pThread->InitInstance, 调用了这个函数:
.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在哪呢? 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

从78312E95 的调用进入到了这里:
;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

在403580 处就是这个对话框的 MESSAGE_MAP
我们可以通过下面的方式快速获取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


下面是该资源所对应的MESSAGE_MAP结构体
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
...

每一个时间都有一个结构体,用于保存窗口ID和调用的函数。

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:0040362C�o
.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


利用 WM_COMMAND

函数 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

[公告][征集寄语] 看雪20周年年会(12.28上海) | 感恩有你,一路同行

上传的附件:
最新回复 (16)
ImHolly 1 2010-9-8 21:36
2
0
支持一个LZ
wxxw 6 2010-9-8 21:38
3
0
谢谢分享。。
踏雪流云 1 2010-9-9 13:59
4
0
很不错,顶一个~~~
wujimaa 1 2010-9-9 15:09
5
0
逆向MFC太难了.要了解MFC框架.
pencil 5 2010-9-10 17:19
6
0
是啊,以前还觉得自己MFC掌握的不错,等到逆东西的时候才发现了解的太少了。
yijun8354 12 2010-9-10 19:40
7
0
http://bbs.pediy.com/showthread.php?t=80439

这个也是你?
lixupeng 2010-9-12 14:06
8
0
好资料收下了!!
guiss 2010-9-12 17:18
9
0
谢谢~~~~~~~~~~!
pencil 5 2010-9-12 18:09
10
0
我晕,这个~~~我没看到~~尴尬了~
pencil 5 2010-9-12 18:10
11
0
这以后干什么事还是要先看看有没有前辈干过~
justto 2010-10-9 17:07
12
0
没有关系 这样才能扩大资源的共享面积 让大家都很容易就能分享到好东西
快雪时晴 4 2010-10-9 21:11
13
0
翻译辛苦,不懂自己怎么会漏过本帖
much thx
lyju 2011-2-6 16:06
14
0
唉我一直不会C++
木棉 2011-6-2 09:12
15
0
谢谢分享,
imule 2011-7-16 02:12
16
0
写VC MFC的时候专门看过MFC的内部机制简析,当时就感觉很考验理解能力。LZ的这个从汇编的层面来分析,对调试工作很有指导意义。
XPoy 3 2011-7-24 04:59
17
0
辛苦! 我以前就是觉得逆向程序里,常见的难度依次是 其他的类型<MFC<delphi<VB,但VB和delphi有大量的玩家参与,使得它们的逆向有很多工具可以直接借用,但MFC似乎是因为有源码,反而很多时候只能靠自己的经验来琢磨,因为想指出来一个LZ翻译的文章里一点点可以改进的地方,索性就把逆向MFC的笔记也一起共享了:
首先,ida的话,不应该弄一个idc脚本出来的,先把ida默认带但不自动加进去的枚举类型MACRO_WM添进来,add standand enum即可,接着创建我们的MessageMap结构  :P
00000000 AFX_MSGMAP_ENTRY struc ; (sizeof=0x18)
00000000 nMessage        dd ?                    ; enum MACRO_WM
00000004 nCode           dd ?
00000008 nID             dd ?
0000000C nLastID         dd ?
00000010 nSig            dd ?
00000014 pfn             dd ?                    ; offset (00000000)
00000018 AFX_MSGMAP_ENTRY ends
ida的结构好就好在可以给每个元素都设置类型,选着我们的nMessage 然后, M->枚举类型选 MACRO_WM
接着就可以给每一个messagemap都alt+Q直接设置结构类型,再*来让ida判断它这个数组里有几个messagemap了

OD的debug->select import library里可以载入MFC的各个版本的DLL的lib,让那些#123变成可以识别的文字描述的符号名。

7.0~10.0 MFC常见类的常见被重载函数的在虚函数表里的位置:
消息表,MESSAGE_MAP这个消息表,是CCmdTarget中的函数,因为窗口类系列里,CCmdTarget是最深层的被继承者,所以它的函数总在虚函数表的最开始,可以很容易的找到:
.rdata:0040C150                 dd offset GetMessageMap
.rdata:0040C154                 CCmdTarget::GetCommandMap(void)
.rdata:0040C158                 CCmdTarget::GetDispatchMap(void)
.rdata:0040C15C                 CCmdTarget::GetConnectionMap(void)
.rdata:0040C160                 CWnd::GetInterfaceMap(void)
.rdata:0040C164                 CCmdTarget::GetEventSinkMap(void)

CWnd的 OnCommond和OnNotify是在GetSuperWndProcAddr的上边
0040C080                 dd OnCommond_unknowclass_
0040C084                 dd CWnd::OnNotify(uint,long,long *)
0040C088                 dd CWnd::GetSuperWndProcAddr(void)
PreTranslateMessage和WindowProc、OnWndMsg,是在DefWindowProcA上边的。
00405BF0  00401BD0  TA_试验?CWnd::BeginModalState
00405BF4  00401BC0  TA_试验?CWnd::EndModalState
00405BF8  00403B20  JMP to mfc100u.#?PreTranslateMessage@CWnd@@UAEHPAUtagMSG@@@Z_11210
0040C09C                 dd CWnd::OnAmbientProperty(COleControlSite *,long,tagVARIANT *)
0040C0A0                 dd CWnd::WindowProc(uint,uint,long)
0040C0A4                 dd CWnd::OnWndMsg(uint,uint,long,long *)
0040C0A8                 dd CWnd::DefWindowProcA(uint,uint,long)
常见的MFC类的区分,一个Cwnd重载出来的类,总会包含上面那些函数。
一个Frame类型的类,就总有一个LoadFrame的函数,它在虚函数表的位置是在Create这个函数的下边。
004059AC  004039EE  JMP to mfc100u.#?Create@CFrameWnd@@UAEHPB_W0KABUtagRECT@@PAVCWnd@@0KPAUCCreateContext@@@Z_2766
004059B0  004039F4  JMP to mfc100u.#?LoadFrame@CMDIChildWnd@@UAEHIKPAVCWnd@@PAUCCreateContext@@@Z_7561
一个View/CDialog这种作为临时创建类型的窗口类,就总会重载BeginModalState和EndModalState,在里面EnableWindow来设置自己是否显示。对话框的一个特殊之处在于,大部分的对话框都会重载一下DoDataExchange,这个函数的特征是里面调用DDX_Control来将某个数据交换给其他窗口。
.rdata:0040C21C                 dd offset DoDataExchange_CSkinDialogEx
.rdata:0040C220                 dd offset BeginModalState_CSkinDialogEx
.rdata:0040C224                 dd offset EndModalState_CSkinDialogEx
另外CDialog类型的窗口,也会包含对话框API特有的Create/CreateIndirect,和CDialog::DoModal,OnInitDialog
.rdata:0040C25C                 CDialog::Create(char const *,CWnd *)
.rdata:0040C260                 CDialog::CreateIndirect(void *,CWnd *)
.rdata:0040C264                 CDialog::CreateIndirect(DLGTEMPLATE const *,CWnd *,void *)
.rdata:0040C268                 CDialog::DoModal(void)
.rdata:0040C26C                 OnInitDialog_CSkinDialogEx
这几个函数,因为其包装总需要调用原始的MFC函数,所以根据对OnInitDialog的引用,就可以找到MFC的对话框虚函数表。

想知道一个程序的窗口类重载了哪些MFC函数,查找这个程序中对那个窗口类原始函数引用的地方就可以了,因为MFC大量的函数都只能写成包装的形式而非完全的覆盖。

要是需要查找某个窗口类所处理的对应消息,就需要先确定GetMessageMap ,CCmdTarget::GetCommandMap(void),CCmdTarget::GetDispatchMap(void),CCmdTarget::GetConnectionMap(void),CWnd::GetInterfaceMap(void),CCmdTarget::GetEventSinkMap(void)这几个获得MessageMap列表的函数,哪一个被目标类所重载了,即确定这几个函数哪个变成不是MFC库函数的了。接着根据查找到的函数中指定的MesssageMap,转换成MsgMap结构的数组即可得到对应的函数列表了:
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)
};

MFC的afxWinMain和普通的CRT里的WinMainCRTStartup是一样的,DLL和exe也是类似的,都有一个inititem用来初始化全局变量,全局变量WinApp的构造函数会在InitItem的调用表里被调用,其他的需要执行初始化代码的全局变量的初始化也会在这个表里,来让InitItem执行。

对于对话框程序,在OnCmdMsg()设置断点最好:
一、不用设置条件断点,只有在发生WM_COMMAND消息后,才运行到此。
二、在此函数中调用按钮消息函数AfxDispatchCmdMsg()时第4个参数为用户消息过程
mfc42中的名称为 MFC42.#?OnCommand@CWnd@@MAEHIJ@Z_4441
游客
登录 | 注册 方可回帖
返回