首页 MFC+消息逆向分析

MFC+消息逆向分析

举报
开通vip

MFC+消息逆向分析 1 / 18 MFC 程序逆向 – 消息篇(上) 作者:szdbg Email:szdbg@sina.com 前言: 记得前一段时间,我刚接触软件破解和逆向这一行时,对于一些软件不知从何处跟踪按钮消息,试了好多方法,就是断 不下来,在系统模块中经常转得晕头转向,而一无所获。 MFC 程序是一种常见类型的程序,我静下心来,潜心研究了一下 MFC 消息流程。弄清原委之后,一切豁然开朗,发现跟 踪 MFC 程序和消息处理原来是如此。。。,跟踪按钮事件处理也由此变得特别简单。 于是,我将这些研究整...

MFC+消息逆向分析
1 / 18 MFC 程序逆向 – 消息篇(上) 作者:szdbg Email:szdbg@sina.com 前言: 记得前一段时间,我刚接触软件破解和逆向这一行时,对于一些软件不知从何处跟踪按钮消息,试了好多方法,就是断 不下来,在系统模块中经常转得晕头转向,而一无所获。 MFC 程序是一种常见类型的程序,我静下心来,潜心研究了一下 MFC 消息 流程 快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计 。弄清原委之后,一切豁然开朗,发现跟 踪 MFC 程序和消息处理原来是如此。。。,跟踪按钮事件处理也由此变得特别简单。 于是,我将这些研究整理成文,以备后忘。并希望大家有所帮助,失误之处,请高手指正。 本文目的就是以一个MFC的 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 对话框程序为例,同时从源码和反汇编代码两方面来研究MFC消息的流程走向,弄清MFC 消息路径的所有站点,这样就可以任意定位 MFC 的所有消息事件,可以从任一站点切入,进行跟踪 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 MFC 的处理过程。甚 至可以从 PumpMessage 大本营出发,一直全程跟踪,做到心中有数,不慌不乱。 关于对话框的启动过程,其过程很简单,程序进入 WinMain 函数之后,会调用对话框的 DoModal 函数,然后就进入 RunModalLoop 函数,消息循环在这里就开始了,限于篇幅,本文不作多说,有兴趣者可看看 MFC 源码。本篇重点在于分析 MFC 的消息分发处理的过程。先看一下 RunModalLoop 函数部分源码: int CWnd::RunModalLoop(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 地界,又途经不少周折,才找到最终目的地 – 消息函数地 址。可谓是山重水复疑无路,柳暗花明又一村。 一个按钮点击事件的过程如下: CWinThread::PumpMessage -> CWnd::PretranslateMessage -> CWnd::WWalkPreTranslateMessate -> CD1Dlg::PreTranslateMessage -> CDialog::PreTranslateMessage -> CWnd::PreTranslateInput -> CWnd::IsDialogMessageA -> USER32 内核 -> AfxWndProcBase -> AfxWndProc -> AfxCallWndProc -> CWnd::WindowProc -> CWnd::OnWndMsg -> CWnd::OnCommand -> CDialog::OnCmdMsg -> CCmdTarget::OnCmdMsg -> _AfxDispatchCmdMsg -> CD1Dlg::OnButton1() VC 下,可以随手写一个标准的对话框程序,上面放一个按钮,点击按钮后,弹出一个消息框。我们现在就从 PumpMessage() 开始,踏上消息之旅,来分析这中间的消息流程。 2 / 18 1. CWinThread::PumpMessage 函数 (消息泵) BOOL CWinThread::PumpMessage() { //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); ::DispatchMessage(&m_msgCur); } return TRUE; } PumpMessage 只有在接收到 WM_QUIT 消息时,才返回 FALSE,其它情况,返回 TRUE。由于 CWinThread::PumpMessage() 函数负责从消息队列中获取消息、翻译消息以及分发消息等,因此习惯将此函数称之为“消息泵”。 消息进入 PumpMessage 后,先流进了 PreTranslateMessage()函数,此函数至关重要,正是由于 PreTranslateMessage(), 才使得 MFC 具有灵活的控制消息的分发模式,可以说,PreTranslateMessage()就是 MFC 的实现消息分发模式的工具。 PumpMessage 函数反汇编代码: 73D31194 > 56 PUSH ESI ... 73D311A1 FF15 B0B6DC73 CALL DWORD PTR DS:[<&USER32.GetMessageA>] 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 DS:[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:[<&USER32.TranslateMessage>] 73D311C7 57 PUSH EDI 73D311C8 FF15 30B6DC73 CALL DWORD PTR DS:[<&USER32.DispatchMessageA>] ; 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 函数,入口参数为:MSG* pMsg,所以: [esp]就是 pMsg,而[[esp]]就是 pMsg->hWnd , [[esp]+4]就是 pMsg->Message c. [[esp]]==002407B4 && [[esp]+4]==202 可以为指定按钮设置点击断点。这里 002407B4 是目标按钮的句柄. 3 / 18 2. CWinThread::PreTranslateMessage 函数 BOOL CWinThread::PreTranslateMessage(MSG* pMsg) { // if this is a thread-message, short-circuit 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() != pMainWnd) return pMainWnd->PreTranslateMessage(pMsg); //程序主框架处理消息 } return FALSE; // no special processing } 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 CALL MFC42.#6575_?AfxGetMainWnd@@YGPAVCW> 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_?WalkPreTranslateTree@C> 73D313F4 TEST EAX,EAX 73D313F6 JNZ SHORT MFC42.73D31415 73D313F8 > TEST ESI,ESI 73D313FA JE SHORT MFC42.73D3140E 73D313FC PUSH DWORD PTR DS:[EDI] 73D313FE CALL MFC42.#2864_?FromHandle@CWnd@@SGPAV> 73D31403 MOV ECX,EAX 73D31405 CALL MFC42.#3815_?GetTopLevelParent@CWnd> 73D3140A CMP EAX,ESI 73D3140C JNZ SHORT MFC42.73D3141A 73D3140E XOR EAX,EAX 73D31410 POP EDI 73D31411 POP ESI 73D31412 RETN 4 提示: a. OD 加载程序后,调出 MFC42.dll 模块,定位到 PreTranslateMessage 代码入口处。 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 是目标按钮的句柄. 3. CWnd::WalkPreTranslateTree 函数 CWnd::WalkPreTranslateTree()的所使用的策略很简单,拥有该消息窗口最先获得该消息的处理权,如果它不想对该消 息进行处理(该窗口对象的 PreTranslateMessage()函数返回 FALSE),就将处理权交给它的父亲窗口,如此向树的根部遍历, 直到遇到 hWndStop(在 CWinThread::PreTranslateMessage()中,hWndStop 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 示的是线程主窗口的句柄)。 记住这个消息处理权的传递方向,是由树的某个一般节点或叶子节点向树的根部传递! BOOL PASCAL CWnd::WalkPreTranslateTree(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; //消息被某一窗口处理了,返回 } 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 函数,只要有一个 PreTranslateMessage 函数 返回 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 PTR DS:[EDI] 73D31393 JMP SHORT MFC42.73D313BD 73D31395 /PUSH ESI 73D31396 |CALL MFC42.#2867_?FromHandlePermanent@CWnd@@SGPAV> ;取得 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 ECX,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:[<&USER32.GetParent>] ; \GetParent 73D313BB |MOV ESI,EAX 73D313BD TEST ESI,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 可以发现:当点击按钮后,按钮点击事件函数代码就会在这条语句后执行,当按钮事件函数代码执行完毕后,CALL 才会返 回 TRUE。 4. CD1Dlg::PreTranslateMessage 函数 BOOL CDialog::PreTranslateMessage(MSG* pMsg) { ... return CDialog::PreTranslateMessage(pMsg); } 若接收消息的窗口类重载了 PreTranslateMessage 函数,则此时会调用它,否则就进入第 5步。实际应用中,这里很有 可能是消息流程的一个分水岭,可能走向两条不同的道路。这完全取决于应用程序新增的代码,若应用程序在这里返回 TRUE, 消息流程就返回去了。否则,就会继续往下执行。 在跟踪按钮消息时,此处应作为一个注意点,而设置断点的最佳位置是在上一步 WalkPreTranslateTree 函数中所说的 位置,跟踪下来,注意消息流程的走向。 5. CDialog::PreTranslateMessage 函数 BOOL CDialog::PreTranslateMessage(MSG* pMsg) { if (CWnd::PreTranslateMessage(pMsg)) return TRUE; CFrameWnd* pFrameWnd = GetTopLevelFrame(); 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 (hItem == 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_?PreTranslateMessage@CWnd@@UAEHPAUtagMSG@> 73D468B2 TEST EAX,EAX 73D468B4 JNZ MFC42.73D8D490 73D468BA MOV ECX,ESI 73D468BC CALL MFC42.#3813_?GetTopLevelFrame@CWnd@@QBEPAVCFrameWnd@@> 73D468C1 TEST EAX,EAX 73D468C3 JNZ MFC42.73D8D429 73D468C9 CMP DWORD PTR DS:[EDI+4],100 73D468D0 JE SHORT MFC42.73D468DF 73D468D2 PUSH EDI 73D468D3 MOV ECX,ESI 73D468D5 CALL MFC42.#5278_?PreTranslateInput@CWnd@@QAEHPAUtagMSG@@@> ;消息从流入此处 73D468DA POP EDI 73D468DB POP ESI 73D468DC RETN 4 6. CWnd::PreTranslateInput 函数 BOOL CWnd::PreTranslateInput(LPMSG lpMsg) { if ((lpMsg->message < WM_KEYFIRST || lpMsg->message > WM_KEYLAST) && (lpMsg->message < WM_MOUSEFIRST || lpMsg->message > WM_MOUSELAST)) // 过滤消息 return FALSE; return IsDialogMessage(lpMsg); } 从源码中可以看出,这个函数是对消息进行过滤,对于按键消息和鼠标消息,直接返回 FALSE,然后再返回到 PumpMessge 函数中,调用 TranslageMessage()和 DispatchMessage()函数,进行消息转换和分发,再进入 MFC。对于其它消息,则调用 CWnd::IsDialogMessage()函数进行下一步处理。 CWnd::PreTranslateInput()函数反汇编代码如下: 73D34009 > MOV EDX,DWORD PTR SS:[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_?IsDialogMessageA@CWnd@@QAEHPAUtagMSG@@@Z 73D34030 ^ JMP SHORT MFC42.73D34020 73D34032 CMP EAX,209 73D34037 ^ JBE SHORT MFC42.73D3402A 73D34039 ^ JMP SHORT MFC42.73D3401E 7. CWnd::IsDialogMessageA 函数 BOOL CWnd::IsDialogMessage(LPMSG lpMsg) { if (m_nFlags & WF_OLECTLCONTAINER) return afxOccManager->IsDialogMessage(this, lpMsg); else return ::IsDialogMessage(m_hWnd, lpMsg); // 从此处流进 User32 内核 } 这里会转进 User32.IsDialogMessageA 函数,从而转入系统内核,由 Windows 系统再来负责将消息的分发传送到各个目 标窗口。 注:User32.IsDialogMessage 并不是象它的名字那样用来检查对话框消息的,而是用来解释或转换消息的。更贴切的名 字应该是 TranslateDialogMessage。CWnd::IsDialogMessage 实际上是一个以 LPMSG 作为参数,再加上内部的 m_hWnd 参数 来调用 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 73D46902 PUSH DWORD PTR SS:[ESP+8] 73D46906 PUSH DWORD PTR DS:[ESI+20] 73D46909 CALL DWORD PTR DS:[<&USER32.IsDialogMessageA>] ;进入系统内核 73D4690F POP ESI 73D46910 RETN 4 提示: 1ٛ. OD 中设断: bp IsDialogMessageA MSG==202 , 则当鼠标左键释放时,会中断在 User32.IsDialogMessageA 函数 入口上。 2. 若已知按钮的句柄,且要求当点击该按钮时,程序中断在 IsDialogMessageA 上,则可以作如下设断: bp IsDialogMessageA [[esp+8]]==00060350 && MSG==202. 3. 中断后,可以通过堆栈返回到 CWnd::IsDialogMessageA 函数代码处。 ========================================================================================================= 8. User32 内核处理,不分析 这里面的过程,我们就当作一个黑匣子吧,不管它,一般情况下,也无需管它。因为我们百分之百相信它不会忽悠我们。 ========================================================================================================= 当消息到达此处时,又进入了 MFC 地界ٛ.第 8 步之前,可以说是经常峰回路转,山重水复。第 8 步之后,是柳暗花明, 可以一路高歌,直奔目的地了。 8 / 18 MFC 程序逆向 – 消息篇(下) 上篇啰里啰嗦地说了一大堆,其实所说的消息都是 PostMessage 方式的。MFC 中还有另外一种很常见的消息发送方式, 就是 SendMessage 函数。这个消息起始路径和上篇所讲的完全不一样。这种方式下,前面的 7个站点均不执行,而是直接进 入第 8 站点:User32 内核,从第 8 站点出来后,这两种消息方式走上了同一条道路,进入第 9 个站点或第 10 个站点了,真 是殊道同归。 可以作如下测试:在 Button1 事件代码中加入: SendMessage(WM_COMMAND,IDC_BUTTON2,0); 这是往 Button2 发送点 击消息,当点击 Button1 时,跟进 Button1 的事件代码流程,再跟进 SendMessage 函数的内部代码,可以发现,和上面所讲 是完全一样的。 对于 MFC 窗口程序,所有窗口都使用同一窗口过程 : AfxWndProcBase(第 9 个站点)或 AfxWndProc(第 10 个站点)。如 果程序是动态链接到 MFC DLL(定义了_AFXDLL),则 AfxWndProcBase 被用作窗口过程,否则 AfxWndProc 被用作窗口过程。 而在 AfxWndProcBase 中,最终也是调用 AfxWndProc 函数。 所以,可以说,第 10 个站点:AfxWndProc 函数是 MFC 的所有消息必经之点。 各位可能有疑问了,消息从 User32 内核出来之后,应该是由 Windows 系统自动发往各个窗口的消息处理函数,但这里 怎么会全部进入了 AfxWndProc()函数呢?这涉及到了钩子函数,有兴趣者,请看本文附录,正文不作多说。现在继续进入 消息之旅: 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_STATE 设置正确的模块状态,然后调用 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 ECX 73D31B8D PUSH MFC42.#2188_?CreateObject@?$CProcessLocal@V_AFX_BASE_> 73D31B92 MOV ECX,OFFSET MFC42._afxBaseModuleState 73D31B97 CALL MFC42.#3028_?GetData@CProcessLocalObject@@QAEPAVCNoTr> 73D31B9C PUSH EAX 73D31B9D LEA ECX,DWORD PTR SS:[EBP-14] 73D31BA0 CALL MFC42.#6467_??0AFX_MAINTAIN_STATE2@@QAE@PAVAFX_MODULE> 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_?AfxWndProc@@YGJPAUHWND__@@IIJ@Z 73D31BBA MOV ECX,DWORD PTR SS:[EBP-10] 9 / 18 73D31BBD MOV EDX,DWORD PTR SS:[EBP-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 函数 - 是所有的 CWnd 类及其派生类的 WndProc LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { if (nMsg == WM_QUERYAFXWNDPROC) 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)。 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_?FromHandlePermanent@CWnd@@SGPAV1@PAUHWND> 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_?AfxCallWndProc@@YGJPAVCWnd@@PAUHWND__@@I> 73D31BFB POP EBP 73D31BFC RETN 10 提示: a. OD 加载程序后,调出 MFC42.dll 模块,定位到 AfxWndProc 代码入口处。 b. 在入口 PUSH EBP 处设置条件断点[esp+8]==111,即可设置按钮点击事件断点。 c. [esp+4]==002407B4 && [esp+8]==202 可以为指定按钮设置点击断点(002407B4 是按钮的句柄值)。 说明:此时设置条件断点就更方便了,[esp]是返回地址,[esp+4]是接收消息的窗口句柄,[esp+8]就是消息代码值 11. AfxCallWndProc 函数 LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,WPARAM wParam = 0, LPARAM lParam = 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 = lParam; … //委派到窗口的 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.73DC2D82 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@?$CThreadLocal@V_AFX_THREAD> 73D31C1C CALL MFC42.#3030_?GetData@CThreadLocalObject@@QAEPAVCNoTra> ... 73D31C62 PUSH DWORD PTR SS:[EBP+18] 73D31C65 MOV EAX,DWORD PTR DS:[EDI] 73D31C67 MOV ECX,EDI 73D31C69 PUSH DWORD PTR SS:[EBP+14] 73D31C6C PUSH ESI 73D31C6D CALL DWORD PTR DS:[EAX+A0] ... 73D31C9A C2 1400 RETN 14 12. CWnd::WindowProc 函数 AfxWndProc 和 AfxCallWndProc 都是 AFX 的 API 函数。而 WindowProc 已经是 CWnd 的一个方法。所以可以注意到在 WindowProc 中已经没有关于句柄或者是 CWnd 的参数了。至此,消息已经正式登堂入室,步入 MFC 的大厅了。真是辛苦啊! 其源码如下: LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { LRESULT lResult = 0; if (!OnWndMsg(message, wParam, lParam, &lResult)) // OnWndMsg 做了大部分工作 lResult = DefWindowProc(message, wParam, lParam); return lResult; } 11 / 18 CWnd::WindowProc 先发送消息到 OnWndMsg()函数,它试图在类中为该消息寻找一个处理函数;如果未被处理,则调用 DefWindowProc()函数。DefWindowProc()是缺省的窗口过程,所有不能或者没有被 OnWndMsg 处理的函数都将交由它处理。 CWnd::WindowProc 是一个虚拟函数,程序员可以在 CWnd 的派生类中覆盖它,改变 MFC 分发消息的方式。例如,MFC 的 CControlBar 就覆盖了 WindowProc,对某些消息作了自己的特别处理,其他消息处理由基类的 WindowProc 函数完成。 CWnd::WindowProc()函数反汇编代码: 73D31CC8 > PUSH EBP 73D31CC9 MOV EBP,ESP 73D31CCB PUSH ECX 73D31CCC PUSH ESI 73D31CCD MOV ESI,ECX 73D31CCF LEA ECX,DWORD PTR SS:[EBP-4] 73D31CD2 AND DWORD PTR SS:[EBP-4],0 73D31CD6 MOV EAX,DWORD PTR DS:[ESI] 73D31CD8 PUSH ECX 73D31CD9 PUSH DWORD PTR SS:[EBP+10] 73D31CDC MOV ECX,ESI 73D31CDE PUSH DWORD PTR SS:[EBP+C] 73D31CE1 PUSH DWORD PTR SS:[EBP+8] 73D31CE4 CALL DWORD PTR DS:[EAX+A4] ; 73D31CEA TEST EAX,EAX 73D31CEC JNZ SHORT MFC42.73D31D04 73D31CEE PUSH DWORD PTR SS:[EBP+10] 73D31CF1 MOV EAX,DWORD PTR DS:[ESI] 73D31CF3 MOV ECX,ESI 73D31CF5 PUSH DWORD PTR SS:[EBP+C] 73D31CF8 PUSH DWORD PTR SS:[EBP+8] 73D31CFB CALL DWORD PTR DS:[EAX+A8] ; 73D31CFB CALL DWORD PTR DS:[EAX+A8] 73D31D01 MOV DWORD PTR SS:[EBP-4],EAX 73D31D04 MOV EAX,DWORD PTR SS:[EBP-4] 73D31D07 POP ESI 73D31D08 LEAVE 73D31D09 RETN 0C 提示: a. OD 加载程序后,调出 MFC42.dll 模块,定位到 WindowProc 代码入口处。 b. 在入口 PUSH EBP 处设置条件断点[esp+4]==111,即可设置按钮点击事件断点。 说明: 1. 此时[esp]是返回地址,[esp+4]是消息代码值。
本文档为【MFC+消息逆向分析】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_033712
暂无简介~
格式:pdf
大小:220KB
软件:PDF阅读器
页数:18
分类:互联网
上传时间:2011-05-18
浏览量:35