【翻译】Basic MFC Reversing

来源:互联网 发布:中国房地产 知乎 编辑:程序博客网 时间:2024/05/16 19:32

原文:http://bbs.pediy.com/showthread.php?t=80439

Guidelines to MFC reversing

MFC逆向指南

Contents

内容

· 1 Guidelines to MFC reversing 

· 1 MFC逆向指南

o 1.1 Tools - References 

o 1.1 工具 – 参考

o 1.2 Prologue: What is MFC? 

o 1.2 前言什么是MFC

o 1.3 Introduction 

o 1.3 引言

o 1.4 Essay

o 1.4 正文 

§ 1.4.1 MFC Main 

§ 1.4.1 MFC主程序

§ 1.4.2 Get MESSAGE_MAP 

§ 1.4.2 获取MESSAGE_MAP

§ 1.4.2.1 IDC Script 

§ 1.4.2.1 IDC脚本

§ 1.4.3 Retrieve WM_COMMAND 

§ 1.4.3 提取WM_COMMAND

o 1.5 Final Note 

o 1.5 后记

o 1.6 Disclaimer 

 

Infos 

Author: 

Pnluck 

Email: 

pnluck@virgilio.it 

Website: 

http://pnluck.netsons.org 

Date: 

25/08/2008 (dd/mm/yyyy) 

Level: 

 

Language: 

English 

Comments: 

It's reversing, it isn't cracking! 

 

 

Tools – References

工具 – 参考

IDA 
Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI
Crackme  (本文的目标就是这个Crack me,压缩包内有一个idb文件,应该是只能使用5.3版本打开。不过第一个连接中给出的IDA 5.3 Demo是不能打开idb文件的,还有各种限制,请考虑下载。)

 

Prologue: What is MFC?

前言: 什么是MFC

The Microsoft Foundation Classes Library (also Microsoft Foundation Classes or MFC) is a library that wraps portions of the Windows API in C++ classes, including functionality that allows to use a default application framework. Classes are defined for many of the handle-managed Windows objects and also for predefined windows and common controls. 

MFC是一个封装了C++类中部分的Windows API的类库,包括可以使用默认的应用框架。这些类被定义成众多的句柄管理的Windows对象以及预定义的窗口和通用对话框。

 

Introduction

引言

Software developed with MFC may import MFC80U.dll (MFC80U is the name of the last version of the dll, as I'm writing), it depends on the type of compilation: as a static library or as a shared DLL.
I'll analyze a software which imports the dll and has debug infos, just to make the job easier.
Once you understand MFC in this way, you can analyze MFC software compiled statically just adding to IDA the signatures of MFC and VisualC. 

使用MFC开发的软件可能要引用MFC80U.dllMFC80U.dll是本文编写时的最后版本dll的名字),它依赖于编译的类型:作为一个静态的类库呢,还是一个共享的动态链接库。

我会分析一个引入dll以及包含调试信息的软件,只是为了让工作简单一点。

一旦你懂得了MFC,你也可以静态分析MFC软件,只要将MFCVisual C的签名加入到IDA

 

Essay

正文

This is a standard C source code for windows: 

这是一段标准的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;
                        
                        // ...
                }
        }
}

Instead this is source code that uses MFC: 

而这个是使用了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 //这是对话框ID         
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
        CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //Dialog Message Map: is like DialogProc 

                                        //对话框消息影射,就好像上面代码中的DialogProc做的事情。
END_MESSAGE_MAP()

// App command to run the dialog
void CProvaRevApp::OnAppAbout() 
{
        CAboutDlg aboutDlg; 
        aboutDlg.DoModal();
}

How you can imagine the disasm of MFC software is harder to understand. 

这样你就能想象反汇编MFC软件是多么难以理解。

MFC Main

MFC主函数

This is the Main disasm of our target: 

这是我们的目标软件的主函数反编译后的结果:

.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

As you can see WinMain calls AfxWinMain.
If you have VisualStudio you can see MFC source code, in this article I'll report only the functions we'll need. 

你可以看到WinMain调用了AfxWinMain

如果你有Visual Studio,你可以查看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;
}

This is the disasm of AfxWinMain: 

下面是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]              ;[edi] → eax
.text:7831D307                 mov     ecx, edi
.text:7831D309                 call    dword ptr [eax+98h]   ; pApp->InitApplication()
.text:7831D30F                 test    eax, eax
.text:7831D311                 jz      short loc_7831D33D
.text:7831D313
.text:7831D313 loc_7831D313: 
.text:7831D313                 mov     eax, [esi]              ; [esi] → eax
.text:7831D315                 mov     ecx, esi
.text:7831D317                 call    dword ptr [eax+58h]   pThread->InitInstance()
.text:7831D31A                 test    eax, eax
.text:7831D31C                 jnz     short loc_7831D334
.text:7831D31E                 cmp     [esi+20h], eax          ; pThread->m_pMainWnd
.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

In the code there are calls as call [eax+XXh]: actually the call to AfxGetApp (and AfxGetThread) gives back a pointer to a structure that has offsets of all functions used by MFC framework.
In this case edi (pApp) holds the offset of 405498, which value is 40349C VA, where the virtual functions table of CWinApp is stored: 

在那些形如call[eax+XXh]的调用: 实际上对AfxGetApp(和AfxGetThread)的调用返回一个指向某个结构指针,这个结构含有MFC架构中所有函数的偏移量。

在本例中,edi(就是源代码中的pApp)的偏移是405498[405498]40349C,这就是CwinApp的虚拟函数表之所在。

.rdata:0040349C off_40349C    dd offset ?GetRuntimeClass@CWinApp@@UBEPAUCRuntimeClass@@XZ

.rdata:0040349C                                         ; DATA XREF: .text:004023C1o

.rdata:0040349C                                         ; 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
;.......................................................

Now a question should pop up in your mind: where does MFC get the address? A quick glance at the reference with IDA... 

现在一个问题可能浮现在你的脑海中:MFC在什么地方获取这个地址?点一下IDADATA XREF。。。

.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
.text:004023D1 sub_4023B0    endp

This VA, 004023B0, is present in a structure 

这个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

which is pushed to __initterm, called before WinMain 

这个结构被压入__initterm,而这个函数在WinMain之前被调用。

.text:00401AAC                 push    offset unk_403314
.text:00401AB1                 push    offset unk_403304 
.text:00401AB6                 call    _initterm

After this excursus, let's go back to AfxWinMain:

看完了这个补充说明,让我们回到AfxWinMain
call dword ptr [eax+98h] (40349C + 98 = 00403534) calls... 

call dword ptr [eax+98h] (40349C + 98 = 00403534) 调用了…

.text:00403534                 dd offset ?InitApplication@CWinApp@@UAEHXZ ; CWinApp::InitApplication(void)

...while call dword ptr [eax+58h], that is pThread->InitInstance, calls the function: 

…call dword ptr [eax+58h], 这其实是pThread->InitInstance, 它调用了函数:

.rdata:004034F4                 dd offset sub_401030

This function shows the dialog window, here is the main part of the code: 

这个函数显示对话框窗口,这里是源代码的主要部分:

.text:00401030 sub_401030    proc near  
.text:00401030                 push    ebp
.text:00401031                 mov     ebp, esp
;..........................................................................
.text:0040109F                 call    sub_401130 
;--------------------------------------------------------------------------
;entrato nella call

进入这个函数(sub_401130
.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

Get MESSAGE_MAP

获取MESSAGE_MAP

But where is MESSAGE_MAP? : Message Map can be get from 

那么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)
     // ...
  
}

This is the disasm 

下面是反汇编后结果:

.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

The call in 78312E95 leads us to: 

位于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

At 403580 there's the MESSAGE_MAP of this dialog.

403580就是这个对话框的MESSAGE_MAP。

So we can get the MessageMap quickly this way:

1. Find before a call to CDialog:DoModal an instruction like this: mov dword ptr [esi], offset off_XXXXXX (it is used to load virtual functions table). 

2. Add 0x30 to that offset to get GetMessageMap function: into that function, look for the instruction mov eax, offset off_XXXXXX, where eax is pMessageMap 

3. Add 4 to pMessageMap to get Dialog MessageMap 

这样我们可以用这种方法快速的得到MESSAGE_MAP的地址:

1. 找到在CDialog:DoModal函数之前调用的函数中一条形如: mov dword ptr [esi], offset off_XXXXXX 的指令,它用来载入虚拟函数表。 

2. 把这个偏移量加上0x30就是函数GetMessageMap: 在这个函数里找形如: mov eax, offset off_XXXXXX的指令,eax就是pMessageMap。 

3. pMessageMap加上0x4就是对话框的MessageMap函数。 



Now an example. This is the software resource: 

现在来看一个例子,这是软件的资源:

CONTROL "Register", 1006, BUTTON,   //1006 = 0x3ee
CONTROL "About", 1007, BUTTON,      //1007 = 0x3ef
CONTROL "Cancel", 1008, BUTTON,     //1008 = 0x3f0


And this is its MESSAGE_MAP, which is an array of structures 

这个是它的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
...

Every event has a structure where window ID and the function to use are stored. 

所有的事件都由一个包含windows ID和调用的函数的结构保存。

IDC Script

IDC脚本

// mfc_message_map.idc version 0.2 by Pnluck 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;
}

This is the disasm after I used the script on it: 

下面是执行脚本后的反汇编结果:

.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

Retrieve WM_COMMAND

提取WM_COMMAND

The function BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo), precisely the function _AfxDispatchCmdMsg, handles WM_COMMAND event.
Actually if you set a bp on it you can see that after a button or a menu is clicked on, the debugger halts the execution. By stepping you can enter the function called for that event, without having to retrieve the MESSAGE_MAP. 

函数BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo), 确切地说函数_AfxDispatchCmdMsg, 处理WM_COMMAND事件。

现在如果你设置一个断点在这个函数上面,你可以看到当一个按钮或一个菜单被点击后,调试器会停止断下程序。不停的步入就可以进入这个事件对应的函数,完全不需要获取MESSAGE_MAP

Final Note

后记

Thanks to Ntoskrnl (Daniel Pistelli), EvilCry, Quequero, Zairon, emdel, DrWatson, Brnocrist, ocean and quequero forum members.

Pnluck 

 

0 0