首页
社区
课程
招聘
[原创]通过分析RTTI识别类及类之间的继承关系
发表于: 2013-2-21 14:23 6962

[原创]通过分析RTTI识别类及类之间的继承关系

2013-2-21 14:23
6962

【文章标题】: 通过分析RTTI识别类及类之间的继承关系
【文章作者】: 绝对小白
【编写语言】: VC
【使用工具】:IDA6.1
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
背景交代
  这篇文章是以前学习RTTI的时候写的,很多东西都是参考一此前辈的文章来的,望各位前辈多多包含,别拍砖啊。今天刚刚用到RTTI整理了一下,也不怕各位大牛笑话,因为最近看了厚黑心里学,所有就发上来了~!

【详细过程】

通过分析RTTI识别类及类之间的继承关系
一、什么是RTTI?
  RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息。RTTI(Run-Time Type Identification)运行时类型识别是由编译器生成的特殊信息,用于支持像dynamic_cast<>和typeid()这样的C++运算符,以及C++异常。

开启RTTI。
  在默认情况下VC++6.0是把RTT关闭的。在VC++6.0菜单上的project-->setings-->C/C++进行设置,在Category:选择“C++ Language”,选中“Enable Run-Time Type Information(RTTI)复选框”如图:

三、预备知识补充。
  为了实现 RTTI,MSVC编译器在编译完了的二进制可执行文件中加入一些结构体,这些结构体包含了代码中关于类(特别是多态类)的信息。这些结构体是:
1、RTTI Complete Object Locator结构体。
  这个结构体在32位系统中占20个字节,其中包含了2个指针,一个指向当前类的信息,另一个指向类的继承关系信息。该结构体信息如下(来源于网上):
struct RTTICompleteObjectLocator

{

    DWORD signature; //总是0 ?

    DWORD offset;    //这个vftable在整个类中的偏移

    DWORD cdOffset;  //构造函数位移的偏移

         struct TypeDescriptor* pTypeDescriptor; //整个类的类型描述符

   struct RTTIClassHierarchyDescriptor* pClassDescriptor; //描述继承关系(hierarchy)
};

2、其中TypeDescriptor结构信息如下,这个结构中定义当前类的类名,逆向的时候根据类名可以大概猜出这个类是做什么的。
struct TypeDescriptor{        VOID *pVFTable;        //Always points to type_info’s vftable
  DWORD spar3;        //保留
  char *name;                //Class Name
}
  
  
ClassHierarchyDescriptor结构体。
   该结构体记录了类的继承信息,包括基类的数量,以及一个RTTIBaseClassDescriptor结构数组。

struct ClassHierarchyDescriptor{DWORD signature; //总是 zero?DWORD attributes; //bit 0 set = multiple inheritance, bit 1 set = virtual inheritanceDWORD numBaseClasses; //number of classes in pBaseClassArraystruct RTTIBaseClassArray* pBaseClassArray;};

RTTIBaseClassDescriptor
  这个结构体记录了关于基类的有关信息,它包括一个指向基类的TypeDescriptor 的指针和一个指向基类的RTTIClassHierarchyDescriptor 的指针另外它还包含有 一 个 PMD 结 构 体。这个结构体记录了这个类中各个基类的位置。
struct RTTIBaseClassDescriptor{struct TypeDescriptor* pTypeDescriptor; //type descriptor of the classDWORD numContainedBases; //number of nested classes following in the Base Class Arraystruct PMD where; //pointer-to-member displacement infoDWORD attributes; //flags, usually 0};
  
PMD结构体
  PMD结构体描述一个基类是如何安置在一个完整的类里。如果是一个简单的继承,它被安置在从对象起始位置开始的一个固定的偏移,这个偏移就是_mdisp_字段。如果它是一个虚基类,那么需要从vbtable得到一个附加的偏移。

struct PMD

{

    int mdisp;  //成员偏移

    int pdisp;  //vbtable偏移

    int vdisp;  //vbtable内偏移

};

四、实战。
启用RTTI。
  在开户RTTI后,MSVC编译器在vftable前(偏移-4)设置了一个指针,指向了“Complete Object Locator”(完整对象定位器)的结构。
  
把下面的代码编译为DEBUG版(什么DEBUG版的,为了更好的学校嘛):
#include <iostream>
class CFather
{
public:
        virtual        void SetTall(int a)
        {
                m_ntall=a;
        }
        virtual        void ShowTall()
        {
                printf("m_ntall=%d\n",m_ntall);
        }
public:
        CFather(){}
        virtual ~CFather(){}
private:
        int m_ntall;
};

class CMother
{
public:
        virtual void SetWeight(int a)
        {
                m_nWeight=a;
        }
        virtual void ShowWeight()
        {
                printf("m_Weight=%d\n",m_nWeight);
        }
public:
        CMother(){}
        virtual ~CMother(){}
private:
        int m_nWeight;
};

class CSon :public CFather,public CMother
{
public:
        void SetAge(int a)
        {
                m_nAge=a;
        }
        void ShowAge()
        {
                printf("m_nAge=%d\n",m_nAge);
        }
public:
        CSon(){}
        virtual ~CSon(){}
private:
        int m_nAge;
};

int main(int argc, char* argv[])
{
        CSon son;
        son.SetAge(18);
        son.ShowAge();
        return 0;
}

IDA分析。
1、定位到虚表
  首先来到找到构造函数,如图:
  
  双击“j_??0CSon@@QAE@XZ”来到构造函数实的地方,如图:下面红色代码显示的地方可以看到两张虚表。关于继承的内存部局不了解的请自己查看相关资料。

      上图显示的是son对象的内存部局

2、通过虚表,定位RTTICompleteObjectLocator结构,找到类名。
  双击“_7CSon@@6BCFather@@@”这一全局变量,来到虚表的位置,如图:
  
  上面已经说过了,RTTICompleteObjectLocator指针就在虚表前偏移-4的地方。也就是上面那个DWORD类型的“??_R4CSon@@6BCFather@@@”。如何确定是一个RTTICompleteObjectLocator结构指什,IDA后面已经给出注释,进一步确认,通过分析RTTICompleteObjectLocator->pTypeDescriptor是不是指向一个有效的TypeDescriptor;检查TypeDescriptor是否正确,可以看TypeDescriptor.name是不是以“.?AV(是VC编译的时候给类别加的的前缀)”开头的字串。

双击“ ??_R4CSon@@6BCFather@@@”,来到RTTICompleteObjectLocator的结构,如图:

通过TypeDescriptor.name检查是否有效,双击“??_R0?AVCSon@@@8”来到TypeDescriptor,如图:

整理后如下图:

找出类的继承信息、继承数量及基类信息。
   来到RTTICompleteObjectLocator的结构,如图:

双击“ ??_R3CSon@@8”来到RTTIClassHierarchyDescriptor指针地址,如图:

转为ClassHierarchyDescriptor内存结构整理为,如图:

双击“??_R2CSon@@8”来到BaseClassArray内存地址,如图:

通过以上信息,一层层向上分析,直到找出类的类与类之间的关系。
教程结束。
图片太多,不能一一上传,发了个附件。


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

上传的附件:
收藏
免费 6
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//