MFC源码实战分析(二)——消息响应与处理机制初探

逆向实战:MFC程序控件消息映射的破解之道 本文深入探讨了MFC程序逆向工程的核心技术——破解控件消息映射。通过实战演示,详细介绍了如何使用SPY++定位窗口句柄,并利用XSPY工具探查MFC内部消息映射表,最终通过编写代码发送特定消息来触发隐藏逻辑。该方法对于安全研究、漏洞挖掘及CTF竞赛(如攻防世界)中的MFC程序分析具有重要价值,提供了一套高效的“黑盒”变“白盒”的逆向实战思路。 阅读详情

在上一节中,我们借助MFC框架中的两个类,编写了一个Windows窗口应用程序。本节我们来探究MFC的消息映射机制,在上一节程序的基础上增加响应鼠标WM_LBUTTONDOWN的消息。

增加消息处理能力

Win32原始消息机制

在此之前,我们先来回忆一下Win32SDK程序中与消息处理相关的部分:

  1. WinMain函数中,注册窗口类时,传入了窗口过程WndProc的地址。然后创建窗口、显示窗口、更新窗口。
  2. 完成上述步骤后,进入消息循环。调用user32!GetMessage或user32!PeekMessage、user32!TranslateMessage完成消息的获取和整理后,调用user32!DispatchMessage,将控制权交还给系统,由系统回调第一步注册窗口类时传入的WndProc函数。
  3. WndProc被系统调用,根据传入的消息不同,分别加以处理,形式上是一个switch-case或多级的if-else。对于我们不关心的消息,再交调用DefWindowProc交给系统处理。(另外,在创建窗口、显示窗口、更新窗口的时候,虽然尚未进入消息循环,但系统会直接调用我们的窗口过程处理一些与窗口创建相关的消息)
  4. 当关闭窗口时,调用DestroyWindow销毁窗口,然后调用PostQuitMessage退出消息循环,程序结束。

MFC消息映射机制初探

而在MFC中,处理消息的机制被称为消息映射机制,通过一组宏来完成,下面热烈欢迎它们登场:

DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CChild, CBase)
END_MESSAGE_MAP()
ON_COMMAND()
ON_WM_XXX()

其中:
1. DECLARE_MESSAGE_MAP用于声明该类具有处理消息的资格和能力,放在该类的类声明中。
2. BEGIN_MESSAGE_MAP和END_MESSAGE_MAP成对出现,自成一体。BEGIN_MESSAGE_MAP有两个参数,分别是本类名称和父类名称。
在二者中间用ON_WM_XXX或ON_COMMAND将要处理的消息和消息处理函数绑定。
3. 这些消息映射的宏和函数的名字是固定的,因为这些是常用的系统消息。有这样的名字的函数和宏会自己关联。
4. 任何CCmdTarget及其派生类都可以通过添加上述的宏从而进行消息处理。但CCmdTarget类能处理的消息类型有限,大多数消息都需要由CWnd及其派生类来处理。

增补代码

在上节代码基础上,增加以下部分:

class CMyWnd : public CFrameWnd
{
public:
     //类原有内容   
    void OnLButtonDown(UINT nFlags, CPoint point)
    {
        MessageBox(TEXT("In CMyWnd::OnLButtonDown"), TEXT("In CMyWnd::OnLButtonDown"), MB_OK);
    }

    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

需要注意的是上述所有宏都要紧跟一对括号。

一探究竟

DECLARE_MESSAGE_MAP()宏

下面看看DECLARE_MESSAGE_MAP()宏的定义:

#define DECLARE_MESSAGE_MAP() \
protected: \
    static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
    virtual const AFX_MSGMAP* GetMessageMap() const; \

这个宏的作用就是在类声明中加入了两个函数,其中一个名为GetThisMessageMap的函数是静态函数,另一个名为GetMessageMap的函数为虚函数,两个函数都没有参数。注意到其返回值都是AFX_MSGMAP的指针,那这又是什么鬼呢?

struct AFX_MSGMAP
{
    const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
    const AFX_MSGMAP_ENTRY* lpEntries;
};

该结构包含两个成员,第一个成员是一个函数指针,由名称猜出应该是得到基类中该结构的指针。
第二个成员又是一个结构指针,看名称中lpEntries用了复数,估计是指向了一个结构数组。先看看该结构:

#ifndef AFX_MSG_CALL
#define AFX_MSG_CALL
#endif
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);

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)
};

在其中,我们发现了nMessage和pfn,这应该对应的是一种映射关系,将消息与处理消息函数的入口地址建立一个映射关系。且pfn是CCmdTarget的成员函数。这也从侧面应证了CCmdTarget及其子类具有消息处理的能力。
在该宏中,我们没有找到GetThisMessageMap以及GetMessageMap的定义,预知后事如何,且看BEGIN_MESSAGE_MAP()宏的解析。

BEGIN_MESSAGE_MAP()宏与END_MESSAGE_MAP()宏

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
    PTM_WARNING_DISABLE \
    const AFX_MSGMAP* theClass::GetMessageMap() const \
        { return GetThisMessageMap(); } \
    const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
    { \
        typedef theClass ThisClass;                           \
        typedef baseClass TheBaseClass;                       \
        static const AFX_MSGMAP_ENTRY _messageEntries[] =  \
        {

#define END_MESSAGE_MAP() \
        {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
    }; \
        static const AFX_MSGMAP messageMap = \
        { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
        return &messageMap; \
    }                                  \
    PTM_WARNING_RESTORE

哈,这两个宏真有意识。原来在两个宏中间定义的代码全部在GetThisMessageMap函数体内,准确的说是在GetThisMessageMap函数体内定义的AFX_MSGMAP_ENTRY类型的结构数组_messageEntries的条目中。那么ON_WM_LBUTTONDOWN应该是在这个数组中插入了一个新条目喽,查看其宏定义加以验证:

enum AfxSig
{
    //省略
    AfxSig_v_u_p,                // void (UINT, CPoint)
    AfxSig_vwp = AfxSig_v_u_p,            // void (UINT, CPoint)
    //省略
}

#define ON_WM_LBUTTONDOWN() \
    { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, CPoint) > ( &ThisClass :: OnLButtonDown)) },

到这里我们终于知道了为什么添加了ON_WM_LBUTTONDOWN()宏后就能与CMyWnd类中的OnLButtonDown函数建立起联系了。原来这名字是MFC框架的编写者们早就定好的了。

并且我们还注意到,WM_LBUTTONDOWN消息对应的处理函数必须在CWnd类中加以定义(或其子类中),也就是说,只有CWnd及其子类才能处理WM_LBUTTONDOWN消息。翻看afxmsg_.h中的定义,绝大多数Windows消息都只能由CWnd类或其子类来处理。

只有WM_COMMAND消息和WM_NOTIFY消息可由CCmdTarget或其子类处理。此外,我们还看到了MFC定义的WM_REFLECT_BASE消息,即所谓的反射类消息。关于这些内容我们将在后续章节加以介绍。

在END_MESSAGE_MAP中,将AFX_MSGMAP结构中的pfnGetBaseMap函数指针设定为了父类的GetThisMessageMap函数。

因此,虚函数GetThisMessageMap中定了该类的消息-处理函数映射表并返回其指针,而GetMessageMap就是该类的虚函数(意味着可以用其基类的指针调用),在其中调用了该类的静态函数GetThisMessageMap。

小结

总结一下:
* DECLARE_MSG_MAP宏在类声明中添加了两个成员函数:GetThisMessageMap(静态函数)与GetMessageMap(虚函数)的声明
* BEGIN_MSG_MAP宏中实现了GetMessageMap,就是调用GetThisMessageMap(静态函数);部分实现了GetThisMessageMap函数:创建了一个AFX_MSGMAP_ENTRY的结构数组,该数组用于将消息和处理消息的函数一一对应。
* ON_WM_XXX宏和ON_COMMAND等宏其实就是在AFX_MSGMAP_ENTRY结构数组中增加了一个条目,将目标消息和已经定义好名称和函数原型的函数(该函数需要在类中实现)对应起来
* END_MSG_MAP宏在AFX_MSGMAP结构数组的末尾增加了一个全0的条目作为结尾,然后构造了一个静态的AFX_MSGMAP结构,将其中的pfnGetBaseMap字段填入父类的GetThisMessageMap函数地址,在lpEntries字段中填入刚构建的AFX_MSGMAP_ENTRY结构数组的地址并返回。

全面解析MFC源码:深入学习与应用 本文还有配套的精品资源,点击获取 简介:《深入浅出MFC源代码》是为程序员深入理解并掌握Microsoft Foundation Classes (MFC)编写的书籍。MFC是微软提供的C++类库,主要用于Windows应用开发,尤其在桌面领域应用广泛。本书通过详尽的源码分析,使读者能够理解MFC核心原理,提升开发效率和程序质量。MFC的架构包括框架类、文档/视图架构、控件... 阅读详情

相关推荐

MFC Windows程序设计源代码深度剖析

本文还有配套的精品资源,点击获取 简介:《MFC Windows程序设计(第二版)(中文版)源代码》为开发者提供了深入了解MFC框架的宝贵资源。MFC简化了Windows应用程序的构建过程,通过面向对象的编程模式封装了Windows API。源代码涉及MFC核心类的使用,事件驱动编程模型,数据库访问,文件操作,多线程编程以及文档/视图架构等关键特性。学习这些代码有助于开发者...

weixin_34618526的博客 1121

【MFC】06.MFC六大机制:窗口创建机制-笔记

接上文【MFC】05.MFC第一大机制:程序启动机制-笔记,这一篇文章来带领大家逆向分析MFC第二大机制:窗口创建机制的源码。 我们知道,在Win32编程中,如果我们要创建一个窗口,基本步骤为: 注册窗口 创建一个窗口,必须要给一个类名称 消息处理回调函数

WdIg-2023的博客 1417

深入解析MFC类源代码项目

MFC(Microsoft Foundation Classes)类库是微软提供的一套用于简化Windows应用程序开发的C++类库。它将Windows API的复杂性封装在面向对象的接口中,从而让开发者能够更加专注于业务逻辑的实现。本章将对MFC类库进行一个基础概述,从它的起源与设计哲学谈起,概述核心类及它们在应用程序中的作用,为后续章节的深入学习打下坚实的基础。在探讨MFC之前,理解Windows应用程序的工作原理是很有必要的。

weixin_31659095的博客 765

MFC源码实战分析(一)——从一个最简MFC程序说开去

偶然翻到以前学MFC时做的笔记,又回想起那段激情燃烧的岁月。当初为了学MFC还是下了一番功夫的,正好最近写文章有些拖延,就把这些笔记整理了一下发上来炒炒冷饭。如果能帮到一两位同好也算是皆大欢喜的事情了。 编译与运行 这是一个最简单的MFC应用程序,我们通过它来学习一些MFC的基础知识。首先新建一个Win32 Application,代码如下: 源代码: #include afxwi

LPWSTR的博客 5941

MFC图形绘制项目实战与源码分析

MFC库,即Microsoft Foundation Classes,为开发者提供了一个封装Windows API的框架,极大地简化了Windows应用程序的开发过程。它允许开发者使用C++进行面向对象编程,同时提供了一套标准的GUI组件,如窗口、按钮、编辑框等,以及文档/视图架构,以便构建结构化的应用程序。OnDraw()是CView类中的一个虚函数,用于定义视图绘制的逻辑。每当视图需要被重绘或更新时,MFC框架会调用OnDraw()函数。

weixin_42327217的博客 1021

Dialog Bar在子框架中消息响应问题解析与实战

经过这一番深入探究,你应该明白了:CDialogBar里的消息处理失败,很少是因为单一原因造成的。它往往是资源、样式、焦点、路由等多个因素共同作用的结果。解决问题的关键在于建立系统性的排查思路,而不是盲目试错。记住这个诊断流程:1. 确认资源是否存在2. 检查消息映射是否完整3. 验证窗口样式是否正确4. 跟踪消息实际流向5. 分析焦点和Z-order状态掌握这套方法论,不仅能让正常工作,更能让你对整个MFC消息体系有透彻理解。

weixin_32456485的博客 1056

MFC源码实战分析(三)——消息映射原理与消息路由机制初探

如果在看完上一篇文章后觉得有点晕,不要害怕。本节我们就不用这些宏,而是用其中的内容重新完成开头那个程序,进而探究MFC消息映射的本来面目。 MFC消息映射机制初探 还我本来面目 class CMyWnd : public CFrameWnd { public: //原有代码 void OnLButtonDown(UINT nFlags, CPoint point)

LPWSTR的博客 1299

深入浅出MFC源码实战分析(简体中文第二版)

Microsoft Foundation Classes(MFC)是微软公司推出的一套面向对象的C++库,它封装了Windows API,并提供了一种更为简洁和高效的方式来创建图形用户界面(GUI)程序。MFC不仅简化了窗口创建、消息循环等复杂操作,还支持多文档界面(MDI)和单文档界面(SDI)应用程序的开发。在Windows平台下进行MFC开发时,CWinApp类扮演着至关重要的角色。CWinApp是所有MFC应用程序的基类,它负责应用程序的初始化、消息循环、以及终止程序时的清理工作。

weixin_42361478的博客 769

透过宏定义了解MFC的消息响应机制

  消息系统是MFC的重要组成部分。MFC的消息响应机制并不复杂,而且MFC的开发环境 Visual Studio来供了非常好的自动化工具,自动生成代码。MFC也定义了丰富的宏来简化消息响应的代码。这使得很多初学者都能快速开发出基于消息响应机制的应用程序。然后也正是这些带来方便的宏,使很多人摸不着头脑。当不小心的代码操作使自动化工具不好用的时候,看着那一组更像是一堆的宏,很多人只好一头雾水。

4076

掌握MFC核心技术与实战源码分析

MFC(Microsoft Foundation Classes)是微软公司为了简化Windows应用程序的开发而推出的一套C++类库。自1992年推出以来,MFC已成为Windows平台上最受欢迎和最常用的开发框架之一。它封装了Windows API(应用程序编程接口),将复杂的底层通信简化为对象操作,极大地提高了开发效率和可维护性。MFC提供了一系列的控件类,用于处理用户界面的各个方面。以下是一些最常用的控件类:CButton:用于创建按钮控件,可以是命令按钮、复选框、单选按钮等。

weixin_35756690的博客 836

《深入浅出MFC源码》实践指南

MFC(Microsoft Foundation Classes)是微软公司提供的一套类库,用于简化Windows平台下的应用程序开发。它封装了Windows API,为开发者提供了一组面向对象的编程接口。MFC将应用程序分解为文档、视图和框架三个主要部分,极大地提高了开发效率并降低了学习成本。在MFC中,文档/视图架构是一大特色,核心在于将数据的存储与显示分离。文档类(Document)负责管理数据,视图类(View)负责展示数据。

weixin_42348783的博客 787

[MFC] MFC消息机制的补充

对于 MFC消息循环和消息队列的记录。

zlllc的博客 1657

深入探索MFC:Windows桌面应用程序开发实战及源码分析

MFC是一个为Windows API提供封装的C++库,它把底层的Windows编程接口转换成了更易理解和使用的类层次结构。MFC的应用程序框架减少了重复编码的工作量,同时提供了一些标准用户界面元素和功能,比如窗口、控件和绘图工具,这使得开发人员可以专注于业务逻辑的实现,而非底层的细节。虽然标准控件可以满足大部分需求,但在某些情况下可能需要自定义控件以达到特定的用户交互效果。MFC允许开发者通过继承和扩展现有控件或从CWnd派生新控件来实现自定义控件。继承现有控件。

weixin_42594427的博客 558

MFC源码实战分析(四)——hWnd与CWnd之千里情缘一线牵

深入MFC的消息响应机制 对于Win32下的应用程序框架来说,如何建立起窗口句柄和窗口类间的对应关系是其在封装窗口类时必须考虑的,而这个过程和男女谈恋爱很类似:从CWnd找到对应的hWnd是相对容易的(m_hWnd就是CWnd的成员变量),就好像“女追男,隔层纱”;而要从hWnd找到对应的CWnd就难了,正所谓“男追女,隔座山”。因此,为了建立起hWnd到CWnd的联系,各大框架的设计者也是绞尽

LPWSTR的博客 1147

MFC源码剖析之--MFC来龙去脉

这个是本人在xue学习vc时研究了很长时间的MFC掩码剖析

MFC项目开发实例

本资源适合MFC初学者,如果你学了一些MFC知识,却苦于没有一个项目指导的话,本资源就是最适合的 本资源适合MFC初学者,如果你学了一些MFC知识,却苦于没有一个项目指导的话,本资源就是最适合的

C++ MFC框架结构的分析

上一节我们基于MFC的框架结构,创建了一个默认的基于对话框的应用程序。没有添加一行代码,但是运行程序却弹出了一个对话框界面。本节将要介绍上一节所生成的框架代码,来更多地了解MFC应用程序,能更好地在MFC框架上快速地开发软件。 框架代码文件的结构 基于MFC对话框程序的框架结构如图1-3-1所示: 图1-3-1 基于MFC对话框程序的框架代码结构示意图 主要有4个部分组成: 1) 资源...

Vermont_的博客 2370

VC++ MFC绘图类实战笔记源代码

摘要:VC/C++源码,系统相关,MFC,绘图  1 添加数据类    CShapeBase   2 添加绘制图形的命令   3 添加线色/填充色的命令   4 鼠标支持   5 多个图的支持   6 选择   7 橡皮条绘图    int nOld = dc.SetROP2( R2_NOTXORPEN );    pShape->Draw( &dc );//擦除    pShape->m_PtEnd = point;    pShape->Draw( &dc );//绘制    dc.SetROP2( nOld );       当前色 1

上一篇: MFC源码实战分析(一)——从一个最简MFC程序说开去
下一篇: MFC源码实战分析(三)——消息映射原理与消息路由机制初探
LPWSTR
博客等级 码龄10年 73粉丝 47原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值