MFC程序中的消息逆向

MFC中CWnd基类中包含了一个GetThisMessageMap 虚函数,用于获得指向messageMap 的指针。因此任何从CWnd派生的窗口类,都含有这个函数(在虚拟函数表中的位置好像是0x30附近)。它的代码通常形如:

CWnd::GetThisMessageMap

00511390 >/>>push    ebp
00511391 |.>mov     ebp, esp
00511393 |.>mov     eax, offset messageMap
00511398 |.>pop     ebp
00511399 /.>retn

MFC的消息处理过程基本是这样的:

AfxWndProc
调用
AfxCallWndProc 和<&USER32.DefWindowProcA>

AfxCallWndProc
调用
CWnd::WindowProc

CWnd::WindowProc
调用
CWnd::OnWndMsg 和CWnd::DefWindowProcA/CWnd::DefWindowProcW

CWnd::OnWndMsg调用CWnd::GetThisMessageMapAfxFindMessageEntry 来找到

具体的消息处理例程,并交与其处理。

因此只要能定位到CWnd::OnWndMsg就能找到CWnd::GetThisMessageMap,进而得到messageMap。

messageMap是一个很重要的结构:

它的第一个字段是指向其基类的messageMap指针,第二个字段就是_messageEntries结构数组,以一个

全零的结构结束 。_messageEntries结构数组中就包含了当前窗口的消息映射情况,其对应着源码中的

消息映射宏:

BEGIN_MESSAGE_MAP(CSundyButton, CButton)
ON_WM_MEASUREITEM()
ON_WM_DRAWITEM()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_MOUSELEAVE,&CSundyButton::OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER,&CSundyButton::OnMouseHover)
END_MESSAGE_MAP()

因此得到_messageEntries的地址,也就得到了用户代码中处理消息的地址了。

对一个基于MFC的程序,首先参考模块中的CWnd::OnWndMsg,如果这个参考不容易找到,可以
下断点:bp AfxCallWndProc。进而追踪CWnd::WindowProc->CWnd::OnWndMsg->CWnd::GetThisMessageMap->_messageEntries。

实战训练:
目标:特色按钮.exe
主程序有4 个消息处理程序,包含一个自定义的控件,它含有7 个消息处理程序,尝试找出它们。

OD载入,直接找到了:
地址=004F9A80
区段=.text
类型=库
名称=CWnd::OnWndMsg

切换断点,F9运行,中断后很快跟随到GetThisMessgeMap的调用:
004F9C8C   . FFD0          call    eax   ;GetThisMessgeMap
数据跟随GetMessgeMap的返回值:
00697C00 >004DC2C9 陕M.   入口地址
00697C04 00697B88 坽i.   offset 特色按钮._messageEntries

006977C0 >004D7D00 .}M. 入口地址
006977C4 00697700 .wi. offset 特色按钮._messageEntries


数据跟随00697B88
00697B88 >0000000F ...
00697B8C 00000000 ....
00697B90 00000000 ....
00697B94 00000000 ....
00697B98 00000013 ...
00697B9C 004D7FF8 ?M.   特色按钮.004D7FF8
00697BA0 00000037 7...
00697BA4 00000000 ....
00697BA8 00000000 ....
00697BAC 00000000 ....
00697BB0 00000028 (...
00697BB4 004D666C lfM. 特色按钮.004D666C
00697BB8 00000111 ..
00697BBC 00000000 ....
00697BC0 000003EB ?..
00697BC4 000003EB ?..
00697BC8 00000038 8...
00697BCC 004D560E VM. 特色按钮.004D560E
00697BD0 00000111 ..
00697BD4 00000000 ....
00697BD8 000003E9 ?..
00697BDC 000003E9 ?..
00697BE0 00000038 8...
00697BE4 004DD8D6 重M.   特色按钮.004DD8D6
00697BE8 00000000 ....
00697BEC 00000000 ....
00697BF0 00000000 ....
00697BF4 00000000 ....
00697BF8 00000000 ....
00697BFC 00000000 ....


刚好是四个消息处理例程,消息ID和例程地址都很明显了。

接下来是自定义控件的几个消息处理例程。

跟随006977C0
006977C0 >004D7D00 .}M. 入口地址
006977C4 00697700 .wi. offset 特色按钮._messageEntries
00697700 >0000002C ,...
00697704 00000000 ....
00697708 00000000 ....
0069770C 00000000 ....
00697710 0000002E ....
00697714 004DB419 碝.   特色按钮.004DB419
00697718 0000002B +...
0069771C 00000000 ....
00697720 00000000 ....
00697724 00000000 ....
00697728 0000002E ....
0069772C 004DAEA6 ΞM.   特色按钮.004DAEA6
00697730 00000201 ..
00697734 00000000 ....
00697738 00000000 ....
0069773C 00000000 ....
00697740 00000035 5...
00697744 004E05C7 ?N.   特色按钮.004E05C7
00697748 00000202 ..
0069774C 00000000 ....
00697750 00000000 ....
00697754 00000000 ....
00697758 00000035 5...
0069775C 004DA2EE 睥M.   特色按钮.004DA2EE
00697760 00000200 . ..
00697764 00000000 ....
00697768 00000000 ....
0069776C 00000000 ....
00697770 00000035 5...
00697774 004DB45A Z碝.   特色按钮.004DB45A
00697778 000002A3 ?..
0069777C 00000000 ....
00697780 00000000 ....
00697784 00000000 ....
00697788 0000000E ...
0069778C 004DA8BB 花M.   特色按钮.004DA8BB
00697790 000002A1 ?..
00697794 00000000 ....
00697798 00000000 ....
0069779C 00000000 ....
006977A0 0000000E ...
006977A4 004DD368 h覯.   特色按钮.004DD368
006977A8 00000000 ....
006977AC 00000000 ....
006977B0 00000000 ....
006977B4 00000000 ....
006977B8 00000000 ....
006977BC 00000000 ....

注:像这种自定义控件的消息处理例程和主窗口的消息处理例程都在“特色按钮.exe”的领空,用这种方法比较容易找到。如果控件定制成 DLL,OCX等以组件的形式注册,然后在主窗口中使用,按以上方法只能得到主窗口的消息处理例程地址,而封装好的组件的消息处理却在DLL,OCX中处 理的。

我的做法是对AfxWndProc 下断,然后返回到调用:
77D18731 call    dword ptr [ebp+8]
这一句指令是在user32领空的。

77D18731 call    dword ptr [ebp+8] 会调用

AfxWndProc和MFC42.#AfxWndProc

那么下次就对77D18731 call    dword ptr [ebp+8] 下断。
直到中断到对封装的控件消息的处理处,跟随一下发现是调用:
00C808D2   >jmp     dword ptr [<&MFC42.#AfxWndProc_1578>]
这条jmp指令就是在封装的控件领空中,而跳转的地址是MFC42领空。

相似地,mfc42.#AfxWndProc_1578会调用#AfxCallWndProc_1109
73D31A53   >call    #AfxCallWndProc_1109

#AfxCallWndProc_1109又会调用#CWnd::WindowProc_6374 :
73D31AFF   >call    dword ptr [eax+A0] ; <jmp.&MFC42.#CWnd::WindowProc_6374>
00C7F7D0   >jmp     dword ptr [<&MFC42.#CWnd::WindowProc_>; mfc42.#CWnd::WindowProc_6374

#CWnd::WindowProc_6374又会调用#CWnd::OnWndMsg_5163
73D31B95   >call    dword ptr [eax+A4]                    ; <jmp.&MFC42.#CWnd::OnWndMsg_5163>
00C7F7CA   >jmp     dword ptr [<&MFC42.#CWnd::OnWndMsg_51>; mfc42.#CWnd::OnWndMsg_5163

在mfc42.#CWnd::OnWndMsg_5163中就可以找到控件的GetThisMessgeMap 了:
73D31C5B   >mov     eax, dword ptr [edi]
73D31C5D   >mov     ecx, edi
73D31C5F   >call    dword ptr [eax+30]
73D31C62   >mov     ebx, eax                              ; CUQG.00C880F8

这两次的CWnd::OnWndMsg 调用不同是因为MFC库版本不同造成的,实际上都是通过CWnd::OnWndMsg 来找GetThisMessgeMap 的。

最好的方法是先用OD载入这个DLL或者OCX文件,找到CWnd::OnWndMsg 下好断点,

再用OD载入主程序分析。

首先关于 [评价可免费] 的严重声明: 一、评价=评论加评价(评星星); 二、评价必须是下载完了该资源后的评价,没下载就评论无效; 三、如果正确评价了,返还积分可能需要等等,系统需要反应下。呵呵 评论时记得要评分。然后会返回给你花费的分再加1分.理论上有十分就可以下载完所有的资源了。一般人我不告诉他。 MFC 程序逆向消息篇(上) 作者:szdbg Email:szdbg@sina.com 前言: 记得前一段时间,我刚接触软件破解和逆向这一行时,对于一些软件不知从何处跟踪按钮消息,试了好多方法,就是断 不下来,在系统模块中经常转得晕头转向,而一无所获。 MFC 程序是一种常见类型的程序,我静下心来,潜心研究了一下MFC 消息流程。弄清原委之后,一切豁然开朗,发现跟 踪MFC 程序消息处理原来是如此。。。,跟踪按钮事件处理也由此变得特别简单。 于是,我将这些研究整理成文,以备后忘。并希望大家有所帮助,失误之处,请高手指正。 的确, .Net之类的程序必定是大势所趋,不过,就目前来说,MFC程序在软件市场还是占有重要的一席之地,所以,了解它,对于逆向和破解此类程序还是很有必要的. MFC之所以显的复杂,就在于它隐藏了它的消息的处理机制,可以说, 程序员基本上不需要懂得它的消息处理过程,就可以写出一套满足应用的软件来.这有好有坏,好的是大大简化了程序员写程序的过程,坏的一方面是,给一般程序员留下了一个迷团: 我只知道这样做,而不知道为什么这样做.心里老是觉得不踏实.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值