首页
社区
课程
招聘
[翻译]浅析MFC逆向(Basic MFC Reversing)
发表于: 2010-9-8 19:58 25579

[翻译]浅析MFC逆向(Basic MFC Reversing)

2010-9-8 19:58
25579

最近在看MFC逆向方面的资料,找了些国外的资料,第一次翻译东西,文章没有翻译难度,但这篇文章对我个人很有帮助,而分享才是最重要的,所以发了出来,希望对初识MFC逆向的朋友有所帮助。

目录
1 MFC逆向指导
1.1 相关工具
1.2 序言: 什么是MFC?
1.3 简介
1.4 实验
1.4.1 MFC 主过程
1.4.2 获取 MESSAGE_MAP
1.4.2.1 IDC 脚本
1.4.3 利用 WM_COMMAND
1.5 最后说明

相关工具

IDA
Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI
Crackme

前言:什么是MFC?

微软基础类库(也称为微软基础类 或 MFC)用C++类包装了部分Windows API,并包含了一个应用程序框架。类被定义为很多用句柄来管理的窗口对象,另外还有预定义窗口和各种通用控件。

介绍

使用MFC进行软件开发需要导入MFC80U.dll(MFC80U 是笔者写本文为止最近的一个版本的dll),视编译类型而定,可以使一个静态库或者共享DLL。
我将分析一个导入了这个dll并拥有调试信息的软件,这样会使我们的工作更容易一些。
一旦你通过这种方式而理解了MFC,你就可以通过在IDA中添加MFC和VisualC的签名来分析用MFC开发软件。

实验

这是windows下的一段标准的C源代码:

LRESULT CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        switch(uMsg)
        {
        case WM_COMMAND:
                switch(LOWORD(wParam))
                {

                case IDC_ABOUT:
                        DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MainDialogProc, 0);
                        break;
                        
                        // ...
                }
        }
}
class CAboutDlg : public CDialog
{
public:
        CAboutDlg();

// Dialog Data
        enum { IDD = IDD_ABOUTBOX };

protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
        DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)  //CAboutDlg::IDD is dialog ID          
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
        CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //Dialog Message Map: is like DialogProc
END_MESSAGE_MAP()

// App command to run the dialog
void CProvaRevApp::OnAppAbout() 
{
        CAboutDlg aboutDlg; 
        aboutDlg.DoModal();
}
.text:00401CBB                 public start
.text:00401CBB                 call    ___security_init_cookie
.text:00401CC0                 jmp     ___tmainCRTStartup

.text:004019FB ___tmainCRTStartup proc near            ; CODE XREF: start+5�j
.text:004019FB
.text:004019FB                 push    5Ch
.text:004019FD                 push    offset unk_403DD8
.text:00401A02                 call    __SEH_prolog4
;... other initialization code
.text:00401B3E                 push    ecx             ; nShowCmd
.text:00401B3F                 push    eax             ; lpCmdLine
.text:00401B40                 push    ebx             ; hPrevInstance
.text:00401B41                 push    400000h         ; hInstance
.text:00401B46                 call    _wWinMain@16    ; wWinMain(x,x,x,x)

; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
_wWinMain@16 proc near
        jmp     ?AfxWinMain@@YGHPAUHINSTANCE__@@0PA_WH@Z ; AfxWinMain(HINSTANCE__ *,HINSTANCE__ *,wchar_t *,int)
_wWinMain@16 endp
[
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        _In_ LPTSTR lpCmdLine, int nCmdShow)
{
        ASSERT(hPrevInstance == NULL);

        int nReturnCode = -1;
        CWinThread* pThread = AfxGetThread();
        CWinApp* pApp = AfxGetApp();

        // AFX internal initialization
        if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
                goto InitFailure;

        // App global initializations (rare)
        if (pApp != NULL && !pApp->InitApplication())
                goto InitFailure;

        // Perform specific initializations
        if (!pThread->InitInstance())
        {
                if (pThread->m_pMainWnd != NULL)
                {
                        TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
                        pThread->m_pMainWnd->DestroyWindow();
                }
                nReturnCode = pThread->ExitInstance();
                goto InitFailure;
        }
        nReturnCode = pThread->Run();

InitFailure:
        AfxWinTerm();
        return nReturnCode;
}
.text:7831D2D2                 public AfxWinMain
.text:7831D2D2 AfxWinMain      proc near
.text:7831D2D2                 push    ebx
.text:7831D2D3                 push    esi
.text:7831D2D4                 push    edi
.text:7831D2D5                 or      ebx, 0FFFFFFFFh
.text:7831D2D8                 call    AfxGetModuleThreadState
.text:7831D2DD                 mov     esi, [eax+4] ;pThread
.text:7831D2E0                 call    AfxGetModuleState
.text:7831D2E5                 push    [esp+0Ch+arg_C]
.text:7831D2E9                 mov     edi, [eax+4]  ;pApp
.text:7831D2EC                 push    [esp+10h+arg_8]
.text:7831D2F0                 push    [esp+14h+arg_4]
.text:7831D2F4                 push    [esp+18h+arg_0]
.text:7831D2F8                 call    AfxWinInit
.text:7831D2FD                 test    eax, eax
.text:7831D2FF                 jz      short loc_7831D33D
.text:7831D301                 test    edi, edi     
.text:7831D303                 jz      short loc_7831D313
.text:7831D305                 mov     eax, [edi] 
.text:7831D307                 mov     ecx, edi
.text:7831D309                 call    dword ptr [eax+98h] 
.text:7831D30F                 test    eax, eax
.text:7831D311                 jz      short loc_7831D33D
.text:7831D313
.text:7831D313 loc_7831D313: 
.text:7831D313                 mov     eax, [esi]
.text:7831D315                 mov     ecx, esi
.text:7831D317                 call    dword ptr [eax+58h] 
.text:7831D31A                 test    eax, eax
.text:7831D31C                 jnz     short loc_7831D334
.text:7831D31E                 cmp     [esi+20h], eax
.text:7831D321                 jz      short loc_7831D32B
.text:7831D323                 mov     ecx, [esi+20h]
.text:7831D326                 mov     eax, [ecx]
.text:7831D328                 call    dword ptr [eax+68h]
.text:7831D32B
.text:7831D32B loc_7831D32B:
.text:7831D32B                 mov     eax, [esi]
.text:7831D32D                 mov     ecx, esi
.text:7831D32F                 call    dword ptr [eax+70h] 
.text:7831D332                 jmp     short loc_7831D33B
.text:7831D334
.text:7831D334 loc_7831D334: 
.text:7831D334                 mov     eax, [esi]
.text:7831D336                 mov     ecx, esi
.text:7831D338                 call    dword ptr [eax+5Ch]
.text:7831D33B
.text:7831D33B loc_7831D33B:  
.text:7831D33B                 mov     ebx, eax
.text:7831D33D
.text:7831D33D loc_7831D33D: 
.text:7831D33D                 call    AfxWinTerm
.text:7831D342                 pop     edi
.text:7831D343                 pop     esi
.text:7831D344                 mov     eax, ebx
.text:7831D346                 pop     ebx
.text:7831D347                 retn    10h
.text:7831D347 AfxWinMain      endp
.rdata:0040349C off_40349C      dd offset ?GetRuntimeClass@CWinApp@@UBEPAUCRuntimeClass@@XZ ;CWinApp::GetRuntimeClass(void)
.rdata:004034A0                 dd offset sub_401010
.rdata:004034A4                 dd offset nullsub_1
.rdata:004034A8                 dd offset nullsub_2
.rdata:004034AC                 dd offset nullsub_1
.rdata:004034B0                 dd offset ?OnCmdMsg@CCmdTarget@@UAEHIHPAXPAUAFX_CMDHANDLERINFO@@@Z ; CCmdTarget::OnCmdMsg(uint,int,void *,AFX_CMDHANDLERINFO *)
.rdata:004034B4                 dd offset ?OnFinalRelease@CCmdTarget@@UAEXXZ ; CCmdTarget::OnFinalRelease(void)
.rdata:004034B8                 dd offset ?IsInvokeAllowed@CCmdTarget@@UAEHJ@Z ; CCmdTarget::IsInvokeAllowed(long)
.rdata:004034BC                 dd offset ?GetDispatchIID@CCmdTarget@@UAEHPAU_GUID@@@Z ; CCmdTarget::GetDispatchIID(_GUID *)
.rdata:004034C0                 dd offset ?GetTypeInfoCount@CCmdTarget@@UAEIXZ ; CCmdTarget::GetTypeInfoCount(void)
.rdata:004034C4                 dd offset ?GetTypeLibCache@CCmdTarget@@UAEPAVCTypeLibCache@@XZ ; CCmdTarget::GetTypeLibCache(void)
.rdata:004034C8                 dd offset ?GetTypeLib@CCmdTarget@@UAEJKPAPAUITypeLib@@@Z ; CCmdTarget::GetTypeLib(ulong,ITypeLib * *)
.rdata:004034CC                 dd offset sub_401000
;.......................................................
.text:004023B0 sub_4023B0      proc near              
.text:004023B0                 push    0
.text:004023B2                 mov     ecx, offset dword_405498
.text:004023B7                 call    ??0CWinApp@@QAE@PB_W@Z ; CWinApp::CWinApp(wchar_t const *)
.text:004023BC                 push    offset sub_4023F0 ; void (__cdecl *)()
.text:004023C1                 mov     dword_405498, offset off_40349C ;<-- this is our offset
.text:004023CB                 call    _atexit
.text:004023D0                 pop     ecx
.text:004023D1                 retn
.rdata:00403304 unk_403304      db    0    
.rdata:00403305                 db    0
.rdata:00403306                 db    0
.rdata:00403307                 db    0
.rdata:00403308                 dd offset _pre_cpp_init
.rdata:0040330C                 dd offset ??__E_afxInitAppState@@YAXXZ ; `dynamic initializer for '_afxInitAppState''(void)
.rdata:00403310                 dd offset sub_4023B0
.text:00401AAC                 push    offset unk_403314
.text:00401AB1                 push    offset unk_403304 
.text:00401AB6                 call    _initterm

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (16)
雪    币: 412
活跃值: (30)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
2
支持一个LZ
2010-9-8 21:36
0
雪    币: 95
活跃值: (419)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
3
谢谢分享。。
2010-9-8 21:38
0
雪    币: 440
活跃值: (119)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
4
很不错,顶一个~~~
2010-9-9 13:59
0
雪    币: 364
活跃值: (1736)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
逆向MFC太难了.要了解MFC框架.
2010-9-9 15:09
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
6
是啊,以前还觉得自己MFC掌握的不错,等到逆东西的时候才发现了解的太少了。
2010-9-10 17:19
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
7
http://bbs.pediy.com/showthread.php?t=80439

这个也是你?
2010-9-10 19:40
0
雪    币: 563
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
好资料收下了!!
2010-9-12 14:06
0
雪    币: 206
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
谢谢~~~~~~~~~~!
2010-9-12 17:18
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
10
我晕,这个~~~我没看到~~尴尬了~
2010-9-12 18:09
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
11
这以后干什么事还是要先看看有没有前辈干过~
2010-9-12 18:10
0
雪    币: 219
活跃值: (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
没有关系 这样才能扩大资源的共享面积 让大家都很容易就能分享到好东西
2010-10-9 17:07
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
13
翻译辛苦,不懂自己怎么会漏过本帖
much thx
2010-10-9 21:11
0
雪    币: 7
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
唉我一直不会C++
2011-2-6 16:06
0
雪    币: 20
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
谢谢分享,
2011-6-2 09:12
0
雪    币: 8
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
写VC MFC的时候专门看过MFC的内部机制简析,当时就感觉很考验理解能力。LZ的这个从汇编的层面来分析,对调试工作很有指导意义。
2011-7-16 02:12
0
雪    币: 242
活跃值: (473)
能力值: ( LV11,RANK:188 )
在线值:
发帖
回帖
粉丝
17
辛苦! 我以前就是觉得逆向程序里,常见的难度依次是 其他的类型<MFC<delphi<VB,但VB和delphi有大量的玩家参与,使得它们的逆向有很多工具可以直接借用,但MFC似乎是因为有源码,反而很多时候只能靠自己的经验来琢磨,因为想指出来一个LZ翻译的文章里一点点可以改进的地方,索性就把逆向MFC的笔记也一起共享了:
首先,ida的话,不应该弄一个idc脚本出来的,先把ida默认带但不自动加进去的枚举类型MACRO_WM添进来,add standand enum即可,接着创建我们的MessageMap结构  :P
00000000 AFX_MSGMAP_ENTRY struc ; (sizeof=0x18)
00000000 nMessage        dd ?                    ; enum MACRO_WM
00000004 nCode           dd ?
00000008 nID             dd ?
0000000C nLastID         dd ?
00000010 nSig            dd ?
00000014 pfn             dd ?                    ; offset (00000000)
00000018 AFX_MSGMAP_ENTRY ends
ida的结构好就好在可以给每个元素都设置类型,选着我们的nMessage 然后, M->枚举类型选 MACRO_WM
接着就可以给每一个messagemap都alt+Q直接设置结构类型,再*来让ida判断它这个数组里有几个messagemap了

OD的debug->select import library里可以载入MFC的各个版本的DLL的lib,让那些#123变成可以识别的文字描述的符号名。

7.0~10.0 MFC常见类的常见被重载函数的在虚函数表里的位置:
消息表,MESSAGE_MAP这个消息表,是CCmdTarget中的函数,因为窗口类系列里,CCmdTarget是最深层的被继承者,所以它的函数总在虚函数表的最开始,可以很容易的找到:
.rdata:0040C150                 dd offset GetMessageMap
.rdata:0040C154                 CCmdTarget::GetCommandMap(void)
.rdata:0040C158                 CCmdTarget::GetDispatchMap(void)
.rdata:0040C15C                 CCmdTarget::GetConnectionMap(void)
.rdata:0040C160                 CWnd::GetInterfaceMap(void)
.rdata:0040C164                 CCmdTarget::GetEventSinkMap(void)

CWnd的 OnCommond和OnNotify是在GetSuperWndProcAddr的上边
0040C080                 dd OnCommond_unknowclass_
0040C084                 dd CWnd::OnNotify(uint,long,long *)
0040C088                 dd CWnd::GetSuperWndProcAddr(void)
PreTranslateMessage和WindowProc、OnWndMsg,是在DefWindowProcA上边的。
00405BF0  00401BD0  TA_试验?CWnd::BeginModalState
00405BF4  00401BC0  TA_试验?CWnd::EndModalState
00405BF8  00403B20  JMP to mfc100u.#?PreTranslateMessage@CWnd@@UAEHPAUtagMSG@@@Z_11210
0040C09C                 dd CWnd::OnAmbientProperty(COleControlSite *,long,tagVARIANT *)
0040C0A0                 dd CWnd::WindowProc(uint,uint,long)
0040C0A4                 dd CWnd::OnWndMsg(uint,uint,long,long *)
0040C0A8                 dd CWnd::DefWindowProcA(uint,uint,long)
常见的MFC类的区分,一个Cwnd重载出来的类,总会包含上面那些函数。
一个Frame类型的类,就总有一个LoadFrame的函数,它在虚函数表的位置是在Create这个函数的下边。
004059AC  004039EE  JMP to mfc100u.#?Create@CFrameWnd@@UAEHPB_W0KABUtagRECT@@PAVCWnd@@0KPAUCCreateContext@@@Z_2766
004059B0  004039F4  JMP to mfc100u.#?LoadFrame@CMDIChildWnd@@UAEHIKPAVCWnd@@PAUCCreateContext@@@Z_7561
一个View/CDialog这种作为临时创建类型的窗口类,就总会重载BeginModalState和EndModalState,在里面EnableWindow来设置自己是否显示。对话框的一个特殊之处在于,大部分的对话框都会重载一下DoDataExchange,这个函数的特征是里面调用DDX_Control来将某个数据交换给其他窗口。
.rdata:0040C21C                 dd offset DoDataExchange_CSkinDialogEx
.rdata:0040C220                 dd offset BeginModalState_CSkinDialogEx
.rdata:0040C224                 dd offset EndModalState_CSkinDialogEx
另外CDialog类型的窗口,也会包含对话框API特有的Create/CreateIndirect,和CDialog::DoModal,OnInitDialog
.rdata:0040C25C                 CDialog::Create(char const *,CWnd *)
.rdata:0040C260                 CDialog::CreateIndirect(void *,CWnd *)
.rdata:0040C264                 CDialog::CreateIndirect(DLGTEMPLATE const *,CWnd *,void *)
.rdata:0040C268                 CDialog::DoModal(void)
.rdata:0040C26C                 OnInitDialog_CSkinDialogEx
这几个函数,因为其包装总需要调用原始的MFC函数,所以根据对OnInitDialog的引用,就可以找到MFC的对话框虚函数表。

想知道一个程序的窗口类重载了哪些MFC函数,查找这个程序中对那个窗口类原始函数引用的地方就可以了,因为MFC大量的函数都只能写成包装的形式而非完全的覆盖。

要是需要查找某个窗口类所处理的对应消息,就需要先确定GetMessageMap ,CCmdTarget::GetCommandMap(void),CCmdTarget::GetDispatchMap(void),CCmdTarget::GetConnectionMap(void),CWnd::GetInterfaceMap(void),CCmdTarget::GetEventSinkMap(void)这几个获得MessageMap列表的函数,哪一个被目标类所重载了,即确定这几个函数哪个变成不是MFC库函数的了。接着根据查找到的函数中指定的MesssageMap,转换成MsgMap结构的数组即可得到对应的函数列表了:
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)
};

MFC的afxWinMain和普通的CRT里的WinMainCRTStartup是一样的,DLL和exe也是类似的,都有一个inititem用来初始化全局变量,全局变量WinApp的构造函数会在InitItem的调用表里被调用,其他的需要执行初始化代码的全局变量的初始化也会在这个表里,来让InitItem执行。

对于对话框程序,在OnCmdMsg()设置断点最好:
一、不用设置条件断点,只有在发生WM_COMMAND消息后,才运行到此。
二、在此函数中调用按钮消息函数AfxDispatchCmdMsg()时第4个参数为用户消息过程
mfc42中的名称为 MFC42.#?OnCommand@CWnd@@MAEHIJ@Z_4441
2011-7-24 04:59
0
游客
登录 | 注册 方可回帖
返回
//