MFC逆向-消息响应函数的定位


layout: post
title: MFC逆向-消息响应函数的定位
categories: Reverse_Engineering
description: MFC逆向-消息响应函数的定位
keywords: mfc
url: https://lichao890427.github.io/ https://github.com/lichao890427/


MFC逆向-消息响应函数的定位

背景

  MFC-Microsoft Foundation Class,微软基础类库,他封装了Windows API以便用户更快速的开发界面功能程序。然而该库及其庞大而复杂,需要有C++的功底否则很难解决bug,逆向起来也是需要一定技巧。Windows消息以如下:

#define WM_NULL                                                        0x0000                        0
 #define WM_CREATE                                                0x0001
 #define WM_DESTROY                                                0x0002
 #define WM_MOVE                                                        0x0003
 #define WM_SIZE                                                        0x0005
 #define WM_ACTIVATE                                                0x0006
 #define WM_SETFOCUS                                                0x0007
 #define WM_KILLFOCUS                                        0x0008
 #define WM_ENABLE                       0x000A
 #define WM_SETREDRAW                    0x000B
 #define WM_SETTEXT                      0x000C
 #define WM_GETTEXT                      0x000D
 #define WM_GETTEXTLENGTH                0x000E
 #define WM_PAINT                        0x000F
// more

尝试

  如果拿其他自己写的程序来说明显然没有说服力,我们就找一个MFC写的来看,超凡搜索BeyondSeacher ,下载下来以后主程序是P2P Searcher.exe,网上说他们抄袭了amule的,我们就来看看究竟。用peid看做初步判断,可以看到Microsoft Visual C++ 7.0 [Debug] IDA载入,先来找入口点,Start->WinMain->可以看到了IDA识别出了Afx系内部函数,且为静态调用。对于对话框类MFC程序,C??App和C???Dlg是关键类,在源码中可以看到这一点,总是有个全局对象,例如“CtestmfcApp theApp;”,那么就需要在c库_initc()中进行初始化(__xc_a -> __xc_z),另外C???App构造的时候会先构造父类CWinApp,且构造父类之后会设置设置虚表从而构造子类,C???Dlg过程类似。根据该原理可以定位到这2个类的位置。先找到CWinApp::CWinApp构造函数,发现索引位置:

void *__thiscall sub_401850(void *this)
 {
   void *v1; // esi@1

   v1 = this;
   CWinApp::CWinApp(this, 0);
   *(_DWORD *)v1 = &off_4315F8;
   return v1;
 }

显然是C???App的构造函数,先不急着看,往上层找:

int sub_42FF70()
 {
   sub_401850(&unk_43F0E0);
   return atexit(sub_42FFF0);
 }

  可见此处执行的是c库,程序启动时构造C???App myapp,同时注册退出时的析构函数,以便清理资源,在往上看已经是一堆要在启动时要初始化的函数了,接着看sub_401850,其虚表为off_4315F8:

.rdata:004315F8 off_4315F8      dd offset sub_42B312    ; DATA XREF: sub_401850+A o
 .rdata:004315FC                 dd offset sub_401970
 .rdata:00431600                 dd offset nullsub_4
 .rdata:00431604                 dd offset ?OnCmdMsg@CCmdTarget@@UAEHIHPAXPAUAFX_CMDHANDLERINFO@@@Z ; CCmdTarget::OnCmdMsg(uint,int,void *,AFX_CMDHANDLERINFO *)
 .rdata:00431608                 dd offset ?OnFinalRelease@CCmdTarget@@UAEXXZ ; CCmdTarget::OnFinalRelease(void)
 .rdata:0043160C                 dd offset sub_4229A6
 .rdata:00431610                 dd offset ?_Get_deleter@_Ref_count_base@std@@UBEPAXABVtype_info@@@Z_6 ; std::_Ref_count_base::_Get_deleter(type_info const &)
 .rdata:00431614                 dd offset sub_4229AC
 .rdata:00431618                 dd offset sub_4229AC
 .rdata:0043161C                 dd offset ?GetTypeLib@CCmdTarget@@UAEJKPAPAUITypeLib@@@Z ; CCmdTarget::GetTypeLib(ulong,ITypeLib * *)
 .rdata:00431620                 dd offset sub_401840
 .rdata:00431624                 dd offset sub_422A0C
 .rdata:00431628                 dd offset sub_4229BD
 .rdata:0043162C                 dd offset sub_422A06
 .rdata:00431630                 dd offset sub_4229C9
 .rdata:00431634                 dd offset sub_4229C3
 .rdata:00431638                 dd offset sub_4229FD
。。。。。。。。。。。。

  根据虚函数特点,如果子类重新定义了虚函数后会覆盖父类虚函数,因此未识别出来的均是经过修改的,那么我们只需要对照MFC源码中CWinApp的类布局,就可以知道未识别出的函数是哪些了,具体操作不再赘述,C???App最重要的是InitInstance函数,为虚表第21个函数,我们来看他做了些什么:

int __thiscall sub_401880(CWinApp *this)
 {
   CWinApp *v1; // esi@1
   void *v2; // eax@1
   int v3; // eax@2
   char v5; // [sp+8h] [bp-2C8h]@1
   int v6; // [sp+2CCh] [bp-4h]@1

   v1 = this;
   InitCommonControls();
   CWinApp::InitInstance(v1);
   AfxEnableControlContainer(0);
   sub_42B8C2("应用程序向导生成的本地应用程序");
   sub_40A150(&v5, 0);
   v6 = 0;
   *((_DWORD *)v1 + 7) = &v5;
   CDialog:oModal((CDialog *)&v5);
   v2 = operator new(1u);
   LOBYTE(v6) = 1;
   if ( v2 )
     v3 = sub_401000(v2);
   else
     v3 = 0;
   LOBYTE(v6) = 0;
   j_uninit(v3);
   v6 = -1;
   sub_409E20(&v5);
   return 0;
 }

  我们只来讨论和普通CWinApp构造函数不同的地方,可以得知v5是我们的C???Dlg,而下面v2 = operator new(1u)显然是某个构造函数:

BOOL CP2pSearcherApp::InitInstance()
 {
         InitCommonControls();
         CWinApp::InitInstance();
         SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
         CP2pSearcherDlg dlg;
         m_pMainWnd=&Dlg;
         int nResponse=dlg.DoModal();
         if (nResponse == IDOK)
         {

         }
         else if (nResponse == IDCANCEL)
         {

         }
         dispatch* mydispatch=new dispatch;        
         mydispatch->uninit();
 }

下面来看CP2pSearcherDlg:

void *__thiscall sub_40A150(void *this, int a2)
 {
   v2 = this;
   CDialog::CDialog(this, 0x66u, a2);
   v17 = 0;
   *(_DWORD *)v2 = &off_431C50;
   CWnd::CWnd((char *)v2 + 116);
   *((_DWORD *)v2 + 29) = &off_43326C;
   CWnd::CWnd((char *)v2 + 196);
   *((_DWORD *)v2 + 49) = &off_4334A4;
   CWnd::CWnd((char *)v2 + 276);
   *((_DWORD *)v2 + 69) = &off_43326C;
   CWnd::CWnd((char *)v2 + 356);
   *((_DWORD *)v2 + 89) = &off_43326C;
   CWnd::CWnd((char *)v2 + 436);
   *((_DWORD *)v2 + 109) = &off_43311C;
   CWnd::CWnd((char *)v2 + 516);
   *((_DWORD *)v2 + 129) = &off_4335F4;
   *((_DWORD *)v2 + 149) = 0;
   *((_DWORD *)v2 + 156) = 15;
   *((_DWORD *)v2 + 155) = 0;
   *((_BYTE *)v2 + 604) = 0;
   LOBYTE(v17) = 7;
   v3 = sub_402BB0((char *)v2 + 628);
   *((_DWORD *)v2 + 158) = v3;
   *(_BYTE *)(v3 + 45) = 1;
   *(_DWORD *)(*((_DWORD *)v2 + 158) + 4) = *((_DWORD *)v2 + 158);
   **((_DWORD **)v2 + 158) = *((_DWORD *)v2 + 158);
   *(_DWORD *)(*((_DWORD *)v2 + 158) + 8) = *((_DWORD *)v2 + 158);
   *((_DWORD *)v2 + 159) = 0;
   LOBYTE(v17) = 8;
   v4 = sub_402BF0((char *)v2 + 640);
   *((_DWORD *)v2 + 161) = v4;
   *(_BYTE *)(v4 + 57) = 1;
   *(_DWORD *)(*((_DWORD *)v2 + 161) + 4) = *((_DWORD *)v2 + 161);
   **((_DWORD **)v2 + 161) = *((_DWORD *)v2 + 161);
   *(_DWORD *)(*((_DWORD *)v2 + 161) + 8) = *((_DWORD *)v2 + 161);
   *((_DWORD *)v2 + 162) = 0;
   v5 = (int)((char *)v2 + 656);
   *(_DWORD *)(v5 + 4) = 0;
   *(_DWORD *)(v5 + 8) = 0;
   *(_DWORD *)(v5 + 12) = 0;
   LOBYTE(v17) = 10;
   *((_DWORD *)v2 + 168) = 0;
   *((_DWORD *)v2 + 169) = 0;
   *((_DWORD *)v2 + 170) = 0;
   AfxGetModuleState();
   v6 = AfxGetModuleState();
   *((_DWORD *)v2 + 28) = LoadIconA(*((HINSTANCE *)v6 + 3), (LPCSTR)0x80);
   v7 = operator new(1u);
   LOBYTE(v17) = 11;
   if ( v7 )
     v8 = sub_401000(v7);
   else
     v8 = 0;
   LOBYTE(v17) = 10;
   *((_DWORD *)v2 + 149) = v8;
   *((_BYTE *)v2 + 684) = 0;
   v9 = operator new(0x10u);
   if ( v9 )
   {
     *(_DWORD *)v9 = 0;
     *((_DWORD *)v9 + 1) = 0;
     *((_DWORD *)v9 + 2) = 0;
     *((_BYTE *)v9 + 12) = 1;
   }
   else
   {
     v9 = 0;
   }
   *((_DWORD *)v2 + 173) = v9;
   sub_40AB90(v9);
   *((_DWORD *)v2 + 172) = 0;
   v15 = 15;
   v14 = 0;
   LOBYTE(v13) = 0;
   LOBYTE(v17) = 12;
   sub_4016A0(&unk_431DA8, 4);
   v16 = 60;
   sub_4085C0(&v12);
   sub_4016A0(&unk_431B40, 6);
   v16 = 270;
   sub_4085C0(&v12);
   sub_4016A0(&unk_431C3C, 8);
   v16 = 90;
   sub_4085C0(&v12);
   sub_4016A0("hash值", 6);
   v16 = 280;
   sub_4085C0(&v12);
   sub_4016A0(&unk_431C34, 6);
   v16 = 90;
   sub_4085C0(&v12);
   v10 = v15 < 0x10;
   *((_DWORD *)v2 + 163) = 8;
   if ( !v10 )
     j__free(v13);
   return v2;
 }

可以看到虚表为off_431C50:

.rdata:00431C50 off_431C50      dd offset loc_42B8BC    ; DATA XREF: sub_409E20+21 o
 .rdata:00431C50                                         ; sub_40A150+41 o
 .rdata:00431C50                                         ; Exception filter 1 for function 401990
 .rdata:00431C54                 dd offset sub_40A440
 .rdata:00431C58                 dd offset nullsub_4
 .rdata:00431C5C                 dd offset unknown_libname_209 ; MFC 3.1-11.0 32bit
 .rdata:00431C60                 dd offset ?OnFinalRelease@CWnd@@UAEXXZ ; CWnd::OnFinalRelease(void)
 .rdata:00431C64                 dd offset sub_4229A6
 .rdata:00431C68                 dd offset ?_Get_deleter@_Ref_count_base@std@@UBEPAXABVtype_info@@@Z_6 ; std::_Ref_count_base::_Get_deleter(type_info const &)
 .rdata:00431C6C                 dd offset sub_4229AC
 .rdata:00431C70                 dd offset sub_4229AC
 .rdata:00431C74                 dd offset ?GetTypeLib@CCmdTarget@@UAEJKPAPAUITypeLib@@@Z ; CCmdTarget::GetTypeLib(ulong,ITypeLib * *)
 .rdata:00431C78                 dd offset sub_401C60
 .rdata:00431C7C                 dd offset sub_422A0C
 .rdata:00431C80                 dd offset sub_4229BD
 .rdata:00431C84                 dd offset sub_422A06
 .rdata:00431C88                 dd offset sub_423A14
 .rdata:00431C8C                 dd offset sub_4229C3
 .rdata:00431C90                 dd offset sub_4229FD

  同样我们只关注很少的一些,OnInitDialog为初始化函数,GetMessageMap可以得到消息响应函数,OnInitDialog做的最重要的一件事为:

if ( !sub_401020(*((_DWORD *)v1 + 149)) )
     sub_4243C6("无法连入emule网络", "警告", 0);

  而sub_4243C6做的是和上面uninit对应的init,都是dispatch.dll中的, 而149对应的变量则在CP2pSearcherDlg构造函数中可以找到踪迹。

 v7 = operator new(1u);
   LOBYTE(v17) = 11;
   if ( v7 )
     v8 = sub_401000(v7);
   else
     v8 = 0;
   LOBYTE(v17) = 10;
   *((_DWORD *)v2 + 149) = v8;
   *((_BYTE *)v2 + 684) = 0;
   v9 = operator new(0x10u);
   if ( v9 )
   {
     *(_DWORD *)v9 = 0;
     *((_DWORD *)v9 + 1) = 0;
     *((_DWORD *)v9 + 2) = 0;
     *((_BYTE *)v9 + 12) = 1;
   }
   else
   {
     v9 = 0;
   }
   *((_DWORD *)v2 + 173) = v9;
   sub_40AB90(v9);
   *((_DWORD *)v2 + 172) = 0;
   v15 = 15;
   v14 = 0;
   LOBYTE(v13) = 0;
   LOBYTE(v17) = 12;
   sub_4016A0(&unk_431DA8, 4);
   v16 = 60;
   sub_4085C0(&v12);
   sub_4016A0(&unk_431B40, 6);
   v16 = 270;
   sub_4085C0(&v12);
   sub_4016A0(&unk_431C3C, 8);
   v16 = 90;
   sub_4085C0(&v12);
   sub_4016A0("hash值", 6);
   v16 = 280;
   sub_4085C0(&v12);
   sub_4016A0(&unk_431C34, 6);
   v16 = 90;
   sub_4085C0(&v12);
   v10 = v15 < 0x10;
   *((_DWORD *)v2 + 163) = 8;
   if ( !v10 )
     j__free(v13);

  看完了之后我们知道了真正做的是dispatch.dll中的init和uninit,下面我们再来看消息处理

void ****sub_401C60()
 {
   return &off_4316D0;
 }
 .rdata:004316D0 off_4316D0      dd offset off_432340    ; DATA XREF: sub_401C60 o
 .rdata:004316D4                 dd offset dword_4316D8
 .rdata:004316D8 dword_4316D8    dd 113h                 ; DATA XREF: .rdata:004316D4 o
 .rdata:004316DC                 dd 0
 .rdata:004316E0                 dd 0
 .rdata:004316E4                 dd 0
 .rdata:004316E8                 dd 11h
 .rdata:004316EC                 dd offset OnTimer
 .rdata:004316F0                 dd 112h
 .rdata:004316F4                 dd 0
 .rdata:004316F8                 dd 0
 .rdata:004316FC                 dd 0
 .rdata:00431700                 dd 1Bh
 .rdata:00431704                 dd offset OnSysCommand
。。。。。。。。。。。

从MFC源码找答案

  先来看MFC里的相关知识,来说明这里为何这么做,我们每添加一个消息,都会在消息映射里增加一条,例如

BEGIN_MESSAGE_MAP(CtestmfcDlg, CDialogEx)
         ON_WM_PAINT()
         ON_WM_QUERYDRAGICON()
         ON_BN_CLICKED(IDOK, &CtestmfcDlg::OnBnClickedOk)
         ON_NOTIFY(NM_RCLICK, IDC_LIST1, &CtestmfcDlg::OnNMRClickList1)
 END_MESSAGE_MAP()

struct AFX_MSGMAP
 {
         const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
         const AFX_MSGMAP_ENTRY* lpEntries;
 };
 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)
 };

回到分析

  从这里很显然的可以看到如何对应上消息响应了。。。。。。。不用解释了吧,如此,对照前面说的windows消息代码,结合exescope查看控件id,可以吧004316D8开始的AFX_MSGMAP_ENTRY标注成消息回调函数:

OnTimer
OnSysCommand
OnPaint
OnDragIcon
OnSearch
OnSelectSource
OnTcnSelChange
OnLvnColumnClick
OnNMLVRClickList
OnNMTCRClickList
OnNMLVDoubleClick
OnSize
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 1.面向对象的程序设计思想是什么? 答:把数据结构和对数据结构进行操作的方法封装形成一个个的对象。 2.什么是类?...
    少帅yangjie阅读 4,987评论 0 14
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,590评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,407评论 25 707
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 女儿的衣服 今天下班,门卫喊住我,说有一个大包快递!哎!!!换季了,又是老妈给邮的新衣服――一件羊绒大衣,一件镶皮...
    瑞贝儿阅读 316评论 0 0