《MFC 程序逆向分析 消息篇.pdf》由会员分享,可在线阅读,更多相关《MFC 程序逆向分析 消息篇.pdf(18页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、1/18 MFC 程序逆向 消息篇(上)作者:szdbg Email: 前言:记得前一段时间,我刚接触软件破解和逆向这一行时,对于一些软件不知从何处跟踪按钮消息,试了好多方法,就是断不下来,在系统模块中经常转得晕头转向,而一无所获。MFC 程序是一种常见类型的程序,我静下心来,潜心研究了一下 MFC 消息流程。弄清原委之后,一切豁然开朗,发现跟踪 MFC 程序和消息处理原来是如此。,跟踪按钮事件处理也由此变得特别简单。于是,我将这些研究整理成文,以备后忘。并希望大家有所帮助,失误之处,请高手指正。本文目的就是以一个 MFC 的标准对话框程序为例,同时从源码和反汇编代码两方面来研究 MFC 消息
2、的流程走向,弄清 MFC消息路径的所有站点,这样就可以任意定位 MFC 的所有消息事件,可以从任一站点切入,进行跟踪分析 MFC 的处理过程。甚至可以从 PumpMessage 大本营出发,一直全程跟踪,做到心中有数,不慌不乱。关于对话框的启动过程,其过程很简单,程序进入 WinMain 函数之后,会调用对话框的 DoModal 函数,然后就进入RunModalLoop 函数,消息循环在这里就开始了,限于篇幅,本文不作多说,有兴趣者可看看 MFC 源码。本篇重点在于分析MFC 的消息分发处理的过程。先看一下 RunModalLoop 函数部分源码:int CWnd:RunModalLoopRu
3、nModalLoop(DWORD dwFlags).for(;).do if(!AfxGetThread()-PumpMessage()/pump message,but quit on WM_QUIT AfxPostQuitMessage(0);return-1;.while(:PeekMessage(pMsg,NULL,NULL,NULL,PM_NOREMOVE);.这里,AfxGetThread()-PumpMessage()是 MFC 消息处理的大本营,MFC 程序的所有消息就是从这里开始,经过重重路径转换,翻山越岭,中途直达 Windows 系统内核,再返回到 MFC 地界,又途经不
4、少周折,才找到最终目的地 消息函数地址。可谓是山重水复疑无路,柳暗花明又一村。一个按钮点击事件的过程如下:CWinThread:PumpMessageCWinThread:PumpMessage-CWnd:PretranslateMessage-CWnd:WWalkPreTranslateMessate CWnd:WWalkPreTranslateMessate-CD1Dlg:PreTranslateMessage-CDialog:PreTranslateMessage-CWnd:PreTranslateInput -CWnd:IsDialogMessageA-USER32 内核USER32
5、内核-AfxWndProcBase-AfxWndProc-AfxCallWndProc-CWnd:WindowProcCWnd:WindowProc-CWnd:OnWndMsg-CWnd:OnCommand-CDialog:OnCmdMsg-CCmdTarget:OnCmdMsg-_AfxDispatchCmdMsg-CD1Dlg:OnButton1()CD1Dlg:OnButton1()VC 下,可以随手写一个标准的对话框程序,上面放一个按钮,点击按钮后,弹出一个消息框。我们现在就从 PumpMessage()开始,踏上消息之旅,来分析这中间的消息流程。2/18 1.CWinThread:P
6、umpMessage 函数(消息泵)1.CWinThread:PumpMessage 函数(消息泵)BOOL CWinThread:PumpMessagePumpMessage()/GetMessage 当消息为 WM_QUIT 时,返回 0,其它消息时,返回 TRUE,有错误时,返回-1 if(!:GetMessage(&m_msgCur,NULL,NULL,NULL)return FALSE;if(m_msgCur.message!=WM_KICKIDLE&!PreTranslateMessage(&m_msgCur):TranslateMessage(&m_msgCur);:Dispat
7、chMessage(&m_msgCur);return TRUE;PumpMessage 只有在接收到 WM_QUIT 消息时,才返回 FALSE,其它情况,返回 TRUE。由于 CWinThread:PumpMessage()函数负责从消息队列中获取消息、翻译消息以及分发消息等,因此习惯将此函数称之为“消息泵”。消息进入 PumpMessage 后,先流进了 PreTranslateMessage()函数,此函数至关重要,正是由于 PreTranslateMessage(),才使得 MFC 具有灵活的控制消息的分发模式,可以说,PreTranslateMessage()就是 MFC 的实现消
8、息分发模式的工具。PumpMessage 函数反汇编代码:73D31194 56 PUSH ESI.73D311A1 FF15 B0B6DC73 CALL DWORD PTR DS:73D311A7 85C0 TEST EAX,EAX 73D311A9 74 26 JE SHORT MFC42.73D311D1 ;收到 WM_QUIT,退出程序 73D311AB 817E 38 6A030000 CMP DWORD PTR DS:ESI+38,36A 73D311B2 74 1A JE SHORT MFC42.73D311CE 73D311B4 8B06 MOV EAX,DWORD PTR D
9、S:ESI 73D311B6 57 PUSH EDI 73D311B7 8BCE MOV ECX,ESI 73D311B9 FF50 60 CALL DWORD PTR DS:EAX+60 ;PreTranslateMessage(消息预处理)73D311BC 85C0 TEST EAX,EAX 73D311BE 75 0E JNZ SHORT MFC42.73D311CE 73D311C0 57 PUSH EDI ;消息预处理返回 FALSE 73D311C1 FF15 ACB6DC73 CALL DWORD PTR DS:73D311C7 57 PUSH EDI 73D311C8 FF15
10、 30B6DC73 CALL DWORD PTR DS:;73D311CE 6A 01 PUSH 1 ;返回 TRUE 73D311D0 58 POP EAX 73D311D1 5F POP EDI 73D311D2 5E POP ESI 73D311D3 C3 RETN 提示:a.OD 加载程序后,调出 MFC42.dll 模块,定位到 PumpMessage 代码入口处。b.在 CALL DWORD PTR DS:EAX+60这一条语句上设置条件断点esp+4=202,即可设置鼠标左键释放断点。说明:call eax+60是调用 PreTranslateMessage 函数,入口参数为:M
11、SG*pMsg,所以:esp就是 pMsg,而esp就是 pMsg-hWnd ,esp+4就是 pMsg-Message c.esp=002407B4&esp+4=202 可以为指定按钮设置点击断点。这里 002407B4 是目标按钮的句柄.3/18 2.CWinThread:PreTranslateMessage 函数2.CWinThread:PreTranslateMessage 函数 BOOL CWinThread:PreTranslateMessagePreTranslateMessage(MSG*pMsg)/if this is a thread-message,short-circ
12、uit this function if(pMsg-hwnd=NULL&DispatchThreadMessageEx(pMsg)return TRUE;CWnd*pMainWnd=AfxGetMainWnd();/通过 WalkPreTranslateTree 进行消息分发 if(CWnd:WalkPreTranslateTree(pMainWnd-GetSafeHwnd(),pMsg)return TRUE;/消息分发处理关键 if(pMainWnd!=NULL)CWnd*pWnd=CWnd:FromHandle(pMsg-hwnd);if(pWnd-GetTopLevelParent()
13、!=pMainWnd)return pMainWnd-PreTranslateMessage(pMsg);/程序主框架处理消息 return FALSE;/no special processing PreTranslateMessage 函数反汇编部分代码 PreTranslateMessage 函数反汇编部分代码 73D313D0 PUSH ESI 73D313D1 PUSH EDI 73D313D2 MOV EDI,DWORD PTR SS:ESP+C 73D313D6 CMP DWORD PTR DS:EDI,0 73D313D9 JE MFC42.73D8E9A1 73D313DF
14、CALL MFC42.#6575_?AfxGetMainWndYGPAVCW 73D313E4 MOV ESI,EAX 73D313E6 TEST ESI,ESI 73D313E8 JE SHORT MFC42.73D313ED 73D313EA MOV EAX,DWORD PTR DS:ESI+20 73D313ED PUSH EDI 73D313EE PUSH EAX 73D313EF CALL MFC42.#6367_?WalkPreTranslateTreeC 73D313F4 TEST EAX,EAX 73D313F6 JNZ SHORT MFC42.73D31415 73D313F
15、8 TEST ESI,ESI 73D313FA JE SHORT MFC42.73D3140E 73D313FC PUSH DWORD PTR DS:EDI 73D313FE CALL MFC42.#2864_?FromHandleCWndSGPAV 73D31403 MOV ECX,EAX 73D31405 CALL MFC42.#3815_?GetTopLevelParentCWnd 73D3140A CMP EAX,ESI 73D3140C JNZ SHORT MFC42.73D3141A 73D3140E XOR EAX,EAX 73D31410 POP EDI 73D31411 PO
16、P ESI 73D31412 RETN 4 提示:a.OD 加载程序后,调出 MFC42.dll 模块,定位到 PreTranslateMessagePreTranslateMessage 代码入口处。4/18 b.在函数入口处设置条件断点esp+4+4=202,即可设置鼠标左键释放断点。说明:此函数的入口参数为:MSG*pMsg,在入口处时,esp是函数返回地址,所以:esp+4就是 pMsg,而esp+4就是 pMsg-hWnd ,esp+4+4就是 pMsg-Message c.esp+4=002407B4&esp+4+4=202 可以为指定按钮设置点击断点。这里 002407B4 是目
17、标按钮的句柄.3.CWnd:WalkPreTranslateTree 函数3.CWnd:WalkPreTranslateTree 函数 CWnd:WalkPreTranslateTree()的所使用的策略很简单,拥有该消息窗口最先获得该消息的处理权,如果它不想对该消息进行处理(该窗口对象的 PreTranslateMessage()函数返回 FALSE),就将处理权交给它的父亲窗口,如此向树的根部遍历,直到遇到 hWndStop(在 CWinThread:PreTranslateMessage()中,hWndStop 表示的是线程主窗口的句柄)。记住这个消息处理权的传递方向,是由树的某个一般节
18、点或叶子节点向树的根部传递!BOOL PASCAL CWnd:WalkPreTranslateTreeWalkPreTranslateTree(HWND hWndStop,MSG*pMsg)for(HWND hWnd=pMsg-hwnd;hWnd!=NULL;hWnd=:GetParent(hWnd)/从当前窗口到父窗口,逐层往上 CWnd*pWnd=CWnd:FromHandlePermanent(hWnd);if(pWnd!=NULL)/target window is a C+window if(pWnd-PreTranslateMessage(pMsg)return TRUE;/消息被
19、某一窗口处理了,返回 if(hWnd=hWndStop)break;/got to hWndStop window without interest return FALSE;/no special processing 正是这个 if(pWnd-PreTranslateMessage(pMsg)return TRUE;才实现了 MFC 灵活的消息分发处理机制。MFC 程序各个窗口类中重载的 PreTranslateMessage 虚函数,都是从这里进来的。MFC 从当前消息窗口类逐级向上搜索执行各个类的 PreTranslateMessage 函数,只要有一个 PreTranslateMes
20、sage 函数 返回 TRUE,WalkPreTranslateTree 就中止搜索,并返回 TRUE,否则返回 FALSE。在PumpMessage函数中最终就是根据WalkPreTranslateTree函数返回值决定是否要进行下一步的Tanslate和Dispatch。WalkPreTranslateTree 函数反汇编代码如下:73D31389 MOV EDI,EDI ;D1.0040308C 73D3138B PUSH ESI 73D3138C PUSH EDI 73D3138D MOV EDI,DWORD PTR SS:ESP+10 73D31391 MOV ESI,DWORD P
21、TR DS:EDI 73D31393 JMP SHORT MFC42.73D313BD 73D31395 /PUSH ESI 73D31396|CALL MFC42.#2867_?FromHandlePermanentCWndSGPAV ;取得 CWnd 指针值 73D3139B|TEST EAX,EAX ;CWnd*值不为 NULL 时,则调用 CWnd:PreTranslateMessage()73D3139D|JE SHORT MFC42.73D313AE 73D3139F|MOV EDX,DWORD PTR DS:EAX 73D313A1|PUSH EDI 73D313A2|MOV E
22、CX,EAX 73D313A4|CALL DWORD PTR DS:EDX+98 ;通过虚函数方式调用 73D313AA|TEST EAX,EAX 73D313AC|JNZ SHORT MFC42.73D313C8 ;消息被处理了,返回 TRUE 73D313AE|CMP ESI,DWORD PTR SS:ESP+C 5/18 73D313B2|JE SHORT MFC42.73D313C1 73D313B4|PUSH ESI ;/hWnd 73D313B5|CALL DWORD PTR DS:;GetParent 73D313BB|MOV ESI,EAX 73D313BD TEST ESI,
23、ESI 73D313BF JNZ SHORT MFC42.73D31395 73D313C1 XOR EAX,EAX ;返回 FALSE 73D313C3 POP EDI 73D313C4 POP ESI 73D313C5 RETN 8 73D313C8 XOR EAX,EAX 73D313CA INC EAX ;返回 TRUE 73D313CB JMP SHORT MFC42.73D313C3 跟踪说明:在上面一句 73D313A4 CALL DWORD PTR DS:EDX+98 设置按钮点击条件断点:esp=002407B4&esp+4=202 可以发现:当点击按钮后,按钮点击事件函数代
24、码就会在这条语句后执行,当按钮事件函数代码执行完毕后,CALL 才会返回 TRUE。4.CD1Dlg:PreTranslateMessage 函数 4.CD1Dlg:PreTranslateMessage 函数 BOOL CDialog:PreTranslateMessage(MSG*pMsg).return CDialog:PreTranslateMessage(pMsg);若接收消息的窗口类重载了 PreTranslateMessage 函数,则此时会调用它,否则就进入第 5 步。实际应用中,这里很有可能是消息流程的一个分水岭,可能走向两条不同的道路。这完全取决于应用程序新增的代码,若应用
25、程序在这里返回 TRUE,消息流程就返回去了。否则,就会继续往下执行。在跟踪按钮消息时,此处应作为一个注意点,而设置断点的最佳位置是在上一步 WalkPreTranslateTree 函数中所说的位置,跟踪下来,注意消息流程的走向。5.CDialog:PreTranslateMessage 函数 5.CDialog:PreTranslateMessage 函数 BOOL CDialog:PreTranslateMessage(MSG*pMsg)if(CWnd:PreTranslateMessage(pMsg)return TRUE;CFrameWnd*pFrameWnd=GetTopLevel
26、Frame();if(pFrameWnd!=NULL&pFrameWnd-m_bHelpMode)return FALSE;if(pMsg-message=WM_KEYDOWN&(pMsg-wParam=VK_ESCAPE|pMsg-wParam=VK_CANCEL)&(:GetWindowLong(pMsg-hwnd,GWL_STYLE)&ES_MULTILINE)&_AfxCompareClassName(pMsg-hwnd,_T(Edit)/可以看出,对于编辑框,某些按键消息在这里采取了一些特殊处理 HWND hItem=:GetDlgItem(m_hWnd,IDCANCEL);if(h
27、Item=NULL|:IsWindowEnabled(hItem)SendMessage(WM_COMMAND,IDCANCEL,0);return TRUE;return PreTranslateInput(pMsg);/消息流入此处 6/18 CDialog:PreTranslateMessage()反汇编代码如下:73D468A4 PUSH ESI 73D468A5 PUSH EDI 73D468A6 MOV EDI,DWORD PTR SS:ESP+C 73D468AA MOV ESI,ECX 73D468AC PUSH EDI 73D468AD CALL MFC42.#5290_?P
28、reTranslateMessageCWndUAEHPAUtagMSG 73D468B2 TEST EAX,EAX 73D468B4 JNZ MFC42.73D8D490 73D468BA MOV ECX,ESI 73D468BC CALL MFC42.#3813_?GetTopLevelFrameCWndQBEPAVCFrameWnd 73D468C1 TEST EAX,EAX 73D468C3 JNZ MFC42.73D8D429 73D468C9 CMP DWORD PTR DS:EDI+4,100 73D468D0 JE SHORT MFC42.73D468DF 73D468D2 PU
29、SH EDI 73D468D3 MOV ECX,ESI 73D468D5 CALL MFC42.#5278_?PreTranslateInputCWndQAEHPAUtagMSG ;消息从流入此处 73D468DA POP EDI 73D468DB POP ESI 73D468DC RETN 4 6.CWnd:PreTranslateInput 函数 6.CWnd:PreTranslateInput 函数 BOOL CWnd:PreTranslateInput(LPMSG lpMsg)if(lpMsg-message message WM_KEYLAST)&(lpMsg-message mes
30、sage WM_MOUSELAST)/过滤消息 return FALSE;return IsDialogMessage(lpMsg);从源码中可以看出,这个函数是对消息进行过滤,对于按键消息和鼠标消息,直接返回 FALSE,然后再返回到 PumpMessge函数中,调用 TranslageMessage()和 DispatchMessage()函数,进行消息转换和分发,再进入 MFC。对于其它消息,则调用CWnd:IsDialogMessage()函数进行下一步处理。CWnd:PreTranslateInput()函数反汇编代码如下:73D34009 MOV EDX,DWORD PTR SS:
31、ESP+4 73D3400D MOV EAX,DWORD PTR DS:EDX+4 73D34010 CMP EAX,100 73D34015 JNB SHORT MFC42.73D34023 73D34017 CMP EAX,200 73D3401C JNB SHORT MFC42.73D34032 73D3401E XOR EAX,EAX 73D34020 RETN 4 73D34023 CMP EAX,108 73D34028 JA SHORT MFC42.73D34017 73D3402A PUSH EDX 7/18 73D3402B CALL MFC42.#4047_?IsDialo
32、gMessageACWndQAEHPAUtagMSGZ 73D34030 JMP SHORT MFC42.73D34020 73D34032 CMP EAX,209 73D34037 JBE SHORT MFC42.73D3402A 73D34039 JMP SHORT MFC42.73D3401E 7.CWnd:IsDialogMessageA 函数 7.CWnd:IsDialogMessageA 函数 BOOL CWnd:IsDialogMessage(LPMSG lpMsg)if(m_nFlags&WF_OLECTLCONTAINER)return afxOccManager-IsDia
33、logMessage(this,lpMsg);else return:IsDialogMessage(m_hWnd,lpMsg);/从此处流进 User32 内核 这里会转进 User32.IsDialogMessageA 函数,从而转入系统内核,由 Windows 系统再来负责将消息的分发传送到各个目标窗口。注:User32.IsDialogMessage 并不是象它的名字那样用来检查对话框消息的,而是用来解释或转换消息的。更贴切的名字应该是 TranslateDialogMessage。CWnd:IsDialogMessage 实际上是一个以 LPMSG 作为参数,再加上内部的 m_hWn
34、d 参数来调用 User32.IsDialogMessage 的打包函数。这样,MFC 中每一个对话框都会解释自己的输入。所以,若同时运行五个对话框,每一个对话框的 PreTranslateMessage 都会自动调用 User32.IsDialogMessage,而且运转良好,完全可以不用我们编程处理,MFC 真是太牛了。CWnd:IsDialogMessageA 函数反汇编代码:73D468F5 PUSH ESI 73D468F6 MOV ESI,ECX 73D468F8 TEST BYTE PTR DS:ESI+29,1 73D468FC JNZ MFC42.73D8E273 73D46
35、902 PUSH DWORD PTR SS:ESP+8 73D46906 PUSH DWORD PTR DS:ESI+20 73D46909 CALL DWORD PTR DS:;进入系统内核 73D4690F POP ESI 73D46910 RETN 4 提示:提示:1?.OD 中设断:bp IsDialogMessageA MSG=202,则当鼠标左键释放时,会中断在 User32.IsDialogMessageA 函数入口上。2.若已知按钮的句柄,且要求当点击该按钮时,程序中断在 IsDialogMessageA 上,则可以作如下设断:bp IsDialogMessageA esp+8
36、=00060350&MSG=202.3.中断后,可以通过堆栈返回到 CWnd:IsDialogMessageA 函数代码处。=8.User32 内核处理8.User32 内核处理,不分析 这里面的过程,我们就当作一个黑匣子吧,不管它,一般情况下,也无需管它。因为我们百分之百相信它不会忽悠我们。=当消息到达此处时,又进入了 MFC 地界?.第 8 步之前,可以说是经常峰回路转,山重水复。第 8 步之后,是柳暗花明,可以一路高歌,直奔目的地了。8/18 MFC 程序逆向 消息篇(下)上篇啰里啰嗦地说了一大堆,其实所说的消息都是 PostMessage 方式的。MFC 中还有另外一种很常见的消息发送
37、方式,就是 SendMessage 函数。这个消息起始路径和上篇所讲的完全不一样。这种方式下,前面的 7 个站点均不执行,而是直接进入第 8 站点:User32 内核,从第 8 站点出来后,这两种消息方式走上了同一条道路,进入第 9 个站点或第 10 个站点了,真是殊道同归。可以作如下测试:在 Button1 事件代码中加入:SendMessage(WM_COMMAND,IDC_BUTTON2,0);这是往 Button2 发送点击消息,当点击 Button1 时,跟进 Button1 的事件代码流程,再跟进 SendMessage 函数的内部代码,可以发现,和上面所讲是完全一样的。对于 MF
38、C 窗口程序,所有窗口都使用同一窗口过程:AfxWndProcBase(第 9 个站点)或 AfxWndProc(第 10 个站点)。如果程序是动态链接到 MFC DLL(定义了_AFXDLL),则 AfxWndProcBase 被用作窗口过程,否则 AfxWndProc 被用作窗口过程。而在 AfxWndProcBase 中,最终也是调用 AfxWndProc 函数。所以,可以说,第 10 个站点:AfxWndProc 函数是 MFC 的所有消息必经之点。各位可能有疑问了,消息从 User32 内核出来之后,应该是由 Windows 系统自动发往各个窗口的消息处理函数,但这里怎么会全部进入了
39、 AfxWndProc()函数呢?这涉及到了钩子函数,有兴趣者,请看本文附录,正文不作多说。现在继续进入消息之旅:9.AfxWndProcBase 函数 9.AfxWndProcBase 函数 LRESULT CALLBACK AfxWndProcBase(HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)AFX_MANAGE_STATE(_afxBaseModuleState.GetData();return AfxWndProc(hWnd,nMsg,wParam,lParam);AfxWndProcBase 首先使用宏 AFX_MANAGE_S
40、TATE 设置正确的模块状态,然后调用 AfxWndProc。说明:如果程序是动态链接到 MFC DLL(定义了_AFXDLL),则 AfxWndProcBase 被用作窗口过程,否则 AfxWndProc 被用作窗口过程。从源码可以知道,在 AfxWndProcBase 中,最终也是调用 AfxWndProc 函数。AfxWndProcBase 反汇编代码:73D31B81 MOV EAX,MFC42.73DC2CFE 73D31B86 CALL MFC42._EH_prolog ;JMP 到 MSVCRT._EH_prolog 73D31B8B PUSH ECX 73D31B8C PUSH
41、 ECX 73D31B8D PUSH MFC42.#2188_?CreateObject?$CProcessLocalV_AFX_BASE_ 73D31B92 MOV ECX,OFFSET MFC42._afxBaseModuleState 73D31B97 CALL MFC42.#3028_?GetDataCProcessLocalObjectQAEPAVCNoTr 73D31B9C PUSH EAX 73D31B9D LEA ECX,DWORD PTR SS:EBP-14 73D31BA0 CALL MFC42.#6467_?0AFX_MAINTAIN_STATE2QAEPAVAFX_MO
42、DULE 73D31BA5 PUSH DWORD PTR SS:EBP+14 73D31BA8 AND DWORD PTR SS:EBP-4,0 73D31BAC PUSH DWORD PTR SS:EBP+10 73D31BAF PUSH DWORD PTR SS:EBP+C 73D31BB2 PUSH DWORD PTR SS:EBP+8 73D31BB5 CALL MFC42.#1578_?AfxWndProcYGJPAUHWND_IIJZ 73D31BBA MOV ECX,DWORD PTR SS:EBP-10 9/18 73D31BBD MOV EDX,DWORD PTR SS:EB
43、P-14 73D31BC0 MOV DWORD PTR DS:ECX+4,EDX 73D31BC3 MOV ECX,DWORD PTR SS:EBP-C 73D31BC6 MOV DWORD PTR FS:0,ECX 73D31BCD LEAVE 73D31BCE RETN 10 10.AfxWndProc 函数10.AfxWndProc 函数-是所有的 CWnd 类及其派生类的 WndProc LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)if(nMsg=WM_QUERYAFXWNDPR
44、OC)return 1;CWnd*pWnd=CWnd:FromHandlePermanent(hWnd);return AfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);AfxWndProc()要做的第一件事是找到目标窗口的 CWnd 对象。一旦找到 CWnd 对象,就会立刻调用 AfxCallWndProc()。这样,AfxWndProc 就成为 CWnd 或其派生类的窗口过程。不论队列消息,还是非队列消息,都送到 AfxWndProc 窗口过程来处理(如果使用 MFC DLL,则 AfxWndProcBase 被调用,然后是 AfxWndProc)。
45、Windows 消息送给 AfxWndProc 窗口过程之后,AfxWndProc 得到 HWND 窗口对应的 MFC 窗口对象,然后,调用AfxCallWndProc 函数进行下一步处理。AfxWndProc 函数反汇编代码:73D31BD1 PUSH EBP 73D31BD2 MOV EBP,ESP 73D31BD4 CMP DWORD PTR SS:EBP+C,360 73D31BDB JE MFC42.73D8BF8A 73D31BE1 PUSH DWORD PTR SS:EBP+8 73D31BE4 CALL MFC42.#2867_?FromHandlePermanentCWndS
46、GPAV1PAUHWND 73D31BE9 PUSH DWORD PTR SS:EBP+14 73D31BEC PUSH DWORD PTR SS:EBP+10 73D31BEF PUSH DWORD PTR SS:EBP+C 73D31BF2 PUSH DWORD PTR SS:EBP+8 73D31BF5 PUSH EAX 73D31BF6 CALL MFC42.#1109_?AfxCallWndProcYGJPAVCWndPAUHWND_I 73D31BFB POP EBP 73D31BFC RETN 10 提示:a.OD 加载程序后,调出 MFC42.dll 模块,定位到 AfxWnd
47、Proc 代码入口处。b.在入口 PUSH EBP 处设置条件断点esp+8=111,即可设置按钮点击事件断点。c.esp+4=002407B4&esp+8=202 可以为指定按钮设置点击断点(002407B4 是按钮的句柄值)。说明:此时设置条件断点就更方便了,esp是返回地址,esp+4是接收消息的窗口句柄,esp+8就是消息代码值 11.AfxCallWndProc 函数 11.AfxCallWndProc 函数 LRESULT AFXAPI AfxCallWndProc(CWnd*pWnd,HWND hWnd,UINT nMsg,WPARAM wParam=0,LPARAM lPara
48、m=0)_AFX_THREAD_STATE*pThreadState=_afxThreadState.GetData();/存储标志符和参数,因为 MFC 内部需要这些参数和信息,但用户不需关心 MSG oldState=pThreadState-m_lastSentMsg;10/18 pThreadState-m_lastSentMsg.hwnd=hWnd;pThreadState-m_lastSentMsg.message=nMsg;pThreadState-m_lastSentMsg.wParam=wParam;pThreadState-m_lastSentMsg.lParam=lPar
49、am;/委派到窗口的 WindowProc lResult=pWnd-WindowProc(nMsg,wParam,lParam);return lResult;AfxCallWndProc 函数把消息送给 CWnd 类或其派生类的对象。该函数主要是把消息和消息参数(nMsg、wParam、lParam)传递给 MFC 窗口对象的成员函数 WindowProc(pWnd-WindowProc)作进一步处理。如果是 WM_INITDIALOG 消息,则在调用WindowProc 前后要作一些处理。AfxCallWndProc 函数反汇编代码:73D31BFF MOV EAX,MFC42.73DC
50、2D82 73D31C04 CALL MFC42._EH_prolog ;JMP 到 MSVCRT._EH_prolog 73D31C09 SUB ESP,34 73D31C0C PUSH EBX 73D31C0D PUSH ESI 73D31C0E PUSH EDI 73D31C0F MOV ECX,OFFSET MFC42._afxThreadState 73D31C14 MOV DWORD PTR SS:EBP-10,ESP 73D31C17 PUSH MFC42.#2202_?CreateObject?$CThreadLocalV_AFX_THREAD 73D31C1C CALL MF