原文:http://bbs.pediy.com/showthread.php?t=80439
Guidelines to MFC reversing
MFC逆向指南
Contents 内容 · 1 Guidelines to MFC reversing · 1 MFC逆向指南 o 1.1 工具 – 参考 o 1.2 前言: 什么是MFC o 1.3 引言 o 1.4 正文 § 1.4.1 MFC主程序 § 1.4.2 获取MESSAGE_MAP § 1.4.2.1 IDC脚本 § 1.4.3 提取WM_COMMAND o 1.5 后记 |
Infos | |
Author: | |
Email: | pnluck@virgilio.it |
Website: | |
Date: | 25/08/2008 (dd/mm/yyyy) |
Level: |
|
Language: | |
Comments: | It's reversing, it isn't cracking! |
工具 – 参考
IDA
Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI
Crackme (本文的目标就是这个Crack me,压缩包内有一个idb文件,应该是只能使用5.3版本打开。不过第一个连接中给出的IDA 5.3 Demo是不能打开idb文件的,还有各种限制,请考虑下载。)
前言: 什么是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对象以及预定义的窗口和通用对话框。
引言
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.dll(MFC80U.dll是本文编写时的最后版本dll的名字),它依赖于编译的类型:作为一个静态的类库呢,还是一个共享的动态链接库。
我会分析一个引入dll以及包含调试信息的软件,只是为了让工作简单一点。
一旦你懂得了MFC,你也可以静态分析MFC软件,只要将MFC和Visual C的签名加入到IDA。
正文
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主函数
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在什么地方获取这个地址?点一下IDA的DATA 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
获取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脚本
// 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
提取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
后记
Thanks to Ntoskrnl (Daniel Pistelli), EvilCry, Quequero, Zairon, emdel, DrWatson, Brnocrist, ocean and quequero forum members.
Pnluck