作 者: 北斗之摇光
时 间: 2007-03-15 17:14
链 接: http://bbs.pediy.com/showthread.php?t=41087
详细信息:
【文章标题】: MFC逆向初级研究(1)
【文章作者】: 北斗之摇光
【作者邮箱】: hardlywhen@hotmail.com
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
引言
本文主要针对微软的VC++6.0中使用MFC产生的EXE文件的逆向研究,我曾经使用微软的Visual Studio 2005编译了一
个EXE文件,通过IDA反汇编以后发现该文件与VC++6.0产生的文件还是有所区别,因此特别在此声明一下。文中主要使用了I
DA pro 5.0和在看雪(www.pediy.com)下载的OllyICE作为工具对目标文件进行反汇编。在此也感谢看雪论坛的各位的无私奉
献,在研究过程的中的困难多通过各位的帖子得到了帮助。
逆向的关键
我认为逆向的关键主要是要弄明白目标文件的算法和实现过程,在Window *** 作系统下,软件的实现过程就体现在其
对Window消息的处理,而软件的算法则包含在处理的具体过程中。对于通过SDK编写的"传统"的Windows应用程序基本都具备
几个共同的特征:WinMain函数、WinProc函数、窗口注册、消息循环。对于这类目标文件的分析主要集中的WinProc的分析上
,WinProc的函数地址获得一般是通过窗口注册函数中的参数获得。(由于我对于这类文件没有具体逆向过,所以只是大概的
说说,有不对的地方请各位不要客气,尽管拍砖)
而使用MFC(Microsoft Function Class)顾名思义,该类库主要封装了大部分的Windows API函数所以在代码中看
不到原本的SDK编程中的消息循环、窗口过程函数等等东西,所有这些封装在相应的mfcxx.dll中,让程序员能够专著与处理
过程与算法。这种做法于逆向而言有好处也有坏处:
坏处就是加大了对于MFC产生的EXE文件的逆向难度,让许多的和我一样的菜鸟迷失在汇编代码中找不找北了,基本主要就靠
猜测实现过程中用到了那些函数,然后对文件导入表的函数下断点来寻找我们所需要的处理过程;
好处就是这样的做法使得EXE文件中主要都是目标程序的Window消息处理流程以及算法,而且dll中的大部分函数的功能都能
在MSDN中查到。如果能够通过对目标文件的分析得到这个Window消息处理流程和算法架构,基本上我们就可以重写整个软件;
要做到上面的目标,首先我们要对MFC有所了解,推荐没有基础的兄弟们读读候俊杰的《深入浅出MFC》。该书在逆向过
程中完全可以作为一本参考书,让你能通过源代码了解实现过程,网上有很多该书的电子版下载。
一个逆向MFC产生的EXE文件的例子
下面我们就通过一个具体的例子来学习一下如何从目标文件中挖到我们需要的东西。首先我们来产生一个需要的EXE文件。
在此我假定各位对MFC有过一定的使用经验,毕竟逆向分析才是本文的重点。
1.产生例子所需要的目标文件:
我们通过VC++6.0的向导来产生一个名为ReverseMFC的工程,这个工程的设置情况如下:
Application type of fff:
Dialog-Based Application targeting:
Win32
Classes to be created:
Application: CFffApp in ReverseMFC.h and ReverseMFC.cpp
Dialog: CFffDlg in ReverseMFCDlg.h and ReverseMFCDlg.cpp
Features:
+ Uses shared DLL implementation (MFC42.DLL)
+ Localizable text in:
中文[中国]
直接编译以后就能够运行,为了确定我们是否正确的分析的整个目标文件,在该对话框中加入一个我们自定义的按钮如
下,对于该按钮的处理函数如下设定为:
AfxMessageBox("I find it!",MB_OK);编译后就得到了我们需要的目标文件。
现在我们得到了所需要的目标文件,在IDA中载入该文件。在此我们最好是产生Release版本的EXE文件,毕竟所有的发
布软件都是Release版本的。
2.具体分析
在IDA中按Ctrl+S找到.rdata段,该段主要存储了目标文件的类运行时创建信息、MessageMap信息、MessageEntry信息、
虚函数表、RTTI数据(如果编译选项中选择了支持RTTI的话)。
在到达.rdata段后我们可以看到这样的代码,对数据进行格式转换后可以得到如下图所示的数据。
.rdata:004021C0 屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯?
.rdata:004021C0
.rdata:004021C0 Segment type: Pure data
.rdata:004021C0 Segment permissions: Read
.rdata:004021C0 _rdata segment para public 'DATA' use32
.rdata:004021C0 assume cs:_rdata
.rdata:004021C0 org 4021C0h
.rdata:004021C0 off_4021C0 dd offset sub_401000DATA XREF: sub_401010o
.rdata:004021C4 dd offset dword_4021C8
.rdata:004021C8 dword_4021C8dd 111h DATA XREF: .rdata:004021C4o
.rdata:004021CC dd 0
.rdata:004021D0 dd 0E146h
.rdata:004021D4 dd 0E146h
.rdata:004021D8 dd 0Ch
.rdata:004021DC dd offset CWinApp::OnHelp(void)
.rdata:004021E0 dd 0
.rdata:004021E4 dd 0
.rdata:004021E8 dd 0
.rdata:004021EC dd 0
.rdata:004021F0 dd 0
.rdata:004021F4 dd 0
.rdata:004021F8 off_4021F8 dd offset CWinApp::GetRuntimeClass(void)
.rdata:004021F8 DATA XREF: unknown_libname_1-56o
.rdata:004021FC dd offset sub_401040
.rdata:00402200 dd offset nullsub_2
.rdata:00402204 dd offset nullsub_3
.rdata:00402208 dd offset nullsub_2
.rdata:0040220C dd offset CCmdTarget::OnCmdMsg(uint,int,void *,AFX_CMDHANDLERINFO *)
其中的off_4021C0就是一个MessageMap数据;dword_4021C8就是MessageMap所指的MessageEntry数据;off_4021F8就是一个
类的虚函数表的开始位置。那么具体这些数据时那个类的相关数据呢?如此判断的依据是什么?
首先我们知道MessageEntry是的数据结构定义如下,而且以6个0表示整个数组的结束。
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 nSig // signature type (action) or pointer to message #
AFX_PMSG pfn // routine to call (or special value)
}
因此我们有理由假设"dword_4021C8就是MessageMap所指的MessageEntry数据"。
而MessageMap数据结构定义如下:
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)()
#else
const AFX_MSGMAP* pBaseMap
#endif
const AFX_MSGMAP_ENTRY* lpEntries
}
off_4021C0的两个数据中第二个数据恰恰就是我们前面假设为MessageEntry的指针,跟入其第一个数据,我们看到如下的代
码:
.text:00401000 *************** S U B R O U T I N E ***************************************
.text:00401000
.text:00401000
.text:00401000 sub_401000 proc near DATA XREF: .rdata:off_4021C0o
.text:00401000 mov eax, ds:AFX_MSGMAP const CWinApp::messageMap
.text:00401005 retn
.text:00401005
.text:00401005 sub_401000 endp
恰恰是一个返回基类的MessageMap的函数。因此我们也同样有理由假设"off_4021C0就是一个MessageMap数据"。
对于虚函数表的假设是如何被证明呢?首先我们要知道关于虚函数表的一点知识:虚函数表由虚函数的地址组成,表中函数
地址的顺序和它们第一次出现的顺序(即在类定义的顺序)一致。若有重载的函数,则替换掉基类函数的地址。通过这个我
们可以知道MFC中虚函数表中的函数顺序必然是先按照CObject->CCmdtarget->。。。。这个类继承顺序中的虚函数顺序来处
理虚函数表中的函数顺序的。只要证明这个我们"假设的虚函数"中的函数顺序与上面提到的知识相符合则有理由说明我们的
假设成立。
首先来看CObject中虚函数的顺序,在查看CObject的声明文件后得到了这个类的虚函数顺序:
virtual CRuntimeClass* GetRuntimeClass() const
virtual ~CObject() // virtual destructors are necessary
virtual void Serialize(CArchive&ar)
#if defined(_DEBUG) || defined(_AFXDLL)
// Diagnostic Support
virtual void AssertValid() const
virtual void Dump(CDumpContext&dc) const
再来查看CCmdtarget的虚函数顺序,在查看CObject的声明文件后得到了这个类的虚函数顺序:
DECLARE_DYNAMIC(CCmdTarget);
virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
#ifndef _AFX_NO_OLE_SUPPORT
// called when last OLE reference is released
virtual void OnFinalRelease()
#endif
#ifndef _AFX_NO_OLE_SUPPORT
// called before dispatching to an automation handler function
virtual BOOL IsInvokeAllowed(DISPID dispid)
virtual BOOL GetDispatchIID(IID* pIID)
virtual UINT GetTypeInfoCount()
virtual CTypeLibCache* GetTypeLibCache()
virtual HRESULT GetTypeLib(LCID lcid, LPTYPELIB* ppTypeLib)
之所以还要列出"DECLARE_DYNAMIC(CCmdTarget);"是因为这个宏的定义如下:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass()\
public: \
static const AFX_DATA CRuntimeClass class##class_name\
virtual CRuntimeClass* GetRuntimeClass() const\
这个virtual CRuntimeClass* GetRuntimeClass() const覆盖掉了一开始的CObject的相对应函数。依次按照类的顺序对
照下来,就可以知道该表确实是虚函数表。同时,对应的GetMessageMap虚函数的位置上跟入后,可以得到如下代码:
.text:00401010 *************** S U B R O U T I N E ***************************************
.text:00401010
.text:00401010
.text:00401010 sub_401010 proc near DATA XREF: .rdata:00402228o
.text:00401010 mov eax, offset off_4021C0
.text:00401015 retn
.text:00401015
.text:00401015 sub_401010 endp
恰恰是返回了我们之前假设的MessageMap的地址。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
其实目前中国的软件行业很浮肿……大量的软件公司都在做外包,而外包程序员无非就是高级打字员……
真正中国自主开发的软件并不多,能做好做大的就更少了~
前景来看,驱动开发和逆向工程都是很辛苦但也是收入颇丰的~
而且目前来看国内这方面的人才都很紧俏~
所以如果能在学校期间熟悉这两门技术之一,
对于以后就业直接走进国内一线软件公司或者大的外企
都是一个有力的筹码
逆向和驱动如果结合起来,就是内核调试
而逆向和你学的.NET平台结合起来,则是.NET逆向
都属于更为稀缺和抢手的技术!
而你说的反病毒,如果是病毒分析,其实只是逆向的一个初步。
如果你能深入的学习逆向,那病毒分析就如砍瓜切菜一般容易~
而再加上正想的驱动开发技术,那开发属于自己的杀毒软件或是HIPS也绝非难事~
这样想来,你说就业前景还会差么?
但是话说回来,真的很难。
我现在还没接触到内核,还在摸索阶段
望你早日成功吧~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)