1、C+继承关系( 含虚函数) 内存结构分析 以及 动态绑定的细节分析一 说明本文较为深入的研究 C+的继承(含多重继承)情况下带虚函数时的实例内存结构,较为深入的剖析了继承实例间是如何组织的,以及动态绑定的实现细节。二 依据以下阐述的细节均依据 VS2005 生成的 DEBUG 模式程序在 IDA Pro5.2 反编译所得,部分数据结构根据程序分析得出。三 术语说明对于以下将频繁用到的术语,我可能简写为如下classX:RTTI_COL x 原语:classX:RTTI Complete Object Locator for x classX:RTTI_CHD 原语:classX:RTTI cl
2、ass Hierarchy DescriptorclassX:RTTI_BCD 原语:classX:RTTI Base class Descriptor以上简写均采用单词首字母缩写,其中 Derive 表示派生类,x 表示 Derive 的一个基类,classX 表示以上任意类四 分析1. 派生类内存分布及虚表Figure 1:派生类实例对象内存分布及虚表映射如上图,其中左部为派生类 class 的实例对象内存格局(继承来自 A,B) ,其中第一项Derive :RTTI_COL xvirtual Derive:fun1(overwrite by derive class) for base
3、Avirtual A:fun2(inherit from base A) for base A. . . . . .virtual fun(if derive own virtual itslef) for derive* perhaps notvfTable for base Adata of base AvfTable for base Bdata of base Bdata of Derive itself为 base A 的虚表地址 (A,B 数据在 class 实例中布局顺序是按照 Derive 在声明时继承顺序决定的),从图中箭头可知 base A 的虚表地址指向了一个数组,里面存
4、放了 Derive 对应的虚函数地址: 首先存放重写过的 base A 声明的的虚函数地址( 如果虚函数是从基类继承过来的,那么虚表中存放的也是 base A 中该函数地址)。 然后存放 Derive 自己声明的虚函数(可能没有) 。*注意:vfTable 地址指向的虚表是从右边表格黄色以下位置开始,黄色以上是虚表附加信息,不为虚表所有,为本人 IDA Pro 反汇编分析所得。其中 Derive :RTTI_COL x 是定位对象所使用的相关数据结构,其结构经反汇编如下:2. classX : RTTI Complete Object Locator for xField Length Rem
5、arkreserve_1 4 not used,filled with 0x00offset_x 4 the offset of vfTable( for x) and the base address of classX reserve_3 4 not used,filled with 0x00pTypeDescriptor 4 address of classX:RTTI Type DescriptorpHierarchyDescriptor 4 address of classX:RTTI class Hierarchy DescriptorFigure 2: classX : RTTI
6、 Complete Object Locator for x反汇编结构offset_x 字段描述 基类 x 在 classX 实例内存空间中的起始地址相对于 classX 基址的偏移(也就是 x 的 vfTable 字段在 classX 内存布局中的偏移 )pHierarchyDescriptor 字段存放 classX:RTTI class Hierarchy Descriptor 结构的地址,该结构描述了 Derive 类的内存结构层次信息。*Remark:对于 classX 为派生类 Derive 情况时,该结构描述了基类 x 相对于Derive 的信息 ,此时表格中所有 classX
7、均应以 Derive 替换;但是当 classX 为基类 x 情况时,该结构描述了基类自身的信息,此时表格中所有 classX 均应以 x替换3. classX:RTTI class Hierarchy DescriptorField Length Remarkreserve_1 4 unknownreserve_2 4 unknowndwArrayElemCount 4 the count of the element inside pBaseClassArraypBaseClassArray 4 address of classX:RTTI Base class ArrayFigure 3
8、: classX:RTTI class Hierarchy Descriptor 反汇编结构该结构详细描述了类实例中相关重要信息的的索引pBaseClassArray 字段存放 classX:RTTI Base class Array 数组基址。dwArrayElemCount 字段存放 classX:RTTI Base class Array 数组项数,包括自身一项和每个基类一项。*Remark:对于 classX 为派生类 Derive 情况时,该结构描述了派生类 Derive的内存结构层次信息,此时表格中所有 classX 均应以 Derive 替换;但是当classX 为基类 x 情况
9、时,该结构描述了基类自身的内存结构层次信息,此时表格中所有 classX 均应以 x 替换4. classX:RTTI Base class Array此数组存放了该类实例对象所需的所有布局信息。数组中每项均存放了一个类描述符classX:RTTI_BCD 的地址,其中实例自身的类描述符地址放在数组第一项,然后存放基类描述符地址5. classX:RTTI Base class DescriptorField Length RemarkpTypeDescriptor 4 address of classX:RTTI Type DescriptordwBaseClassCount 4 the c
10、ount of Base class of classXOffset_X 4 the offset of vfTable( for classX) and the base address of Derivereserve_1 4 unknown,filled with 0xffffffffreserve_2 4 unknown,filled with 0x00000000reserve_3 4 unknown,filled with 0x00000040Figure 4: classX:RTTI Base class Descriptor 反汇编结构该结构中有几个特别需要注意的地方,首先该结
11、构是从 classX:RTTI class Hierarchy Descriptor classX:RTTI Base class Array 引出的,所以此结构中的Offset_X 数据都是相对于 classX:RTTI class Hierarchy Descriptor 中的 classX 对象而言举例说明,从 Derive:RTTI class Hierarchy Descriptor 中引出的 x:RTTI Base class Descriptor 中的 Offset_X 字段是 x 相对于 Derive 而言的;但是如果从 x:RTTI class Hierarchy Descr
12、iptor 中引出的 x:RTTI Base class Descriptor 中的 Offset_X 字段则是 x 相对于 x 而言的小结:根据上述数据结构,我们可以清晰的将一个类继承关系描述出来,当然只包括虚函数部分,我们甚至可以基于此信息来完成一个 PE 的类继承视图的扫描器,我们甚至有可能通过对一个基类的扫描而窥知其派生类的继承视图,对,这是有可能的,以上结构信息我相信还有其他用途可供开发研究,再次本人不予赘述。补充部分(关于 C+的虚函数的调用过程细节):以下结果是根据 IDA PRO5.2 逆向反汇编分析出的,分析版本为 Debug根据逆向分析,可知编译器在虚函数处理过程中完成了不
13、少的工作,当然是编译时工作,根据分析发现虚函数表中函数分布是根据函数声明顺序来存放的,类本身的虚函数置于第一虚表中的末尾位置,这在派生类内存分布及虚表小节中有详细说明,而虚表地址字段又是根据派生类对基类的引用顺序来存放的,那么分析可知,对于某个虚函数而言,无论动态绑定后的结果如何,此虚函数在虚表中的偏移在编译时就能被确定了,那么我们可以认为编译器在对虚函数的绑定技术是基于 虚表基址+偏移地址 取内容来完成的。虚函数的编码问题,根据 Debug 版本逆向分析可知,对于虚函数中的 this引用,编译器采用的是该函数所在虚表地址字段 虚表地址字段相对于本实例内存基址的偏移,这样就可得出该实例的内存基址,其中虚表地址字段相对实例基址的偏移在编译时就能确定。thiscall 问题,根据被调用的虚函数所在的虚表来确定传入的 this 指针,虚函数所在虚表在编译时就能确定,此时,将虚表地址字段的地址作为 this 指针传入(其实也可理解为此虚函数声明所在的类实例的起始地址)。通过传入的this 指针,虚函数根据基址相对偏移可以轻松定位到对象的真正基址,从而就能达到通过指针实现动态绑定技术。以上内容均为原创分析,如需引用请注明出处。by kalrey 2010/07/15 午夜至凌晨