原文: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: |
|
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