首页
论坛
专栏
课程

[调试逆向] [系统底层] [原创] 通过windbg学习虚表继承及布局

2019-7-29 10:47 1723

[调试逆向] [系统底层] [原创] 通过windbg学习虚表继承及布局

2019-7-29 10:47
1723

1- c++中虚函表的继承规律

1) 什么样本类会有虚表?

假设有一个类为ClassA,如果ClassA或者ClassA的父类中有虚函数,则ClassA就会有属于自己的虚表

2) 子类单继承一个父类,且无虚函数覆盖

子类会复制一份父类的虚表作为自己的虚表

3) 子类单继承一个父类,但有虚函数覆盖

子类会复制一份父类的虚表作为自己的虚表;

子类将需要覆盖的父类虚函数替换成自己的虚函数

4) 类的虚表可以有多个

假设ClassA继承了CBase1,CBase2且CBase1,CBase2都没有父类但都有虚函数,则ClassA将会有两张虚表,一张是从CBase1中拷贝过来的,一张是从CBase2中拷贝过来的

5) 子类新创建的虚函数放在该类的第一张虚表中的末尾

6) 类的虚函数表被该类的所有实例共享

7) 类对象的布局是按照类的继承顺序布局的

2- 虚函数表在内存中的布局情况

假设类的继承关系如下图所示:


图1


图1在加上第一段中的虚表继承规律可以知道:

CBase1,CBase2各有一张虚表;

ClassA,ClassB各有有两个虚表;

ClassA从CBase1中继承了一张表,从CBase2中继承了一张表,所以有两张表;

ClassB从ClassA中继承了两张表,因为ClassA有两张表

上述类对象的内存结构以及虚函数表在内存中的布局如下:


图2

3- 测试代码

我的代码在编译时取消了地址随机化,目的是为了方便测试。

class CBase1
{
public:
	int m_nBase1;
public:
	CBase1() { m_nBase1 = 1; }
	~CBase1() {}
	virtual void Function1() { printf("CBase1::Function1"); }
	virtual void Function2() { printf("CBase1::Function2"); }
};

class CBase2
{
public:
	int m_nBase2;
public:
	CBase2() { m_nBase2 = 2; }
	~CBase2() {}
	virtual void Function3() { printf("CBase2::Function3"); }
	virtual void Function4() { printf("CBase2::Function4"); }
};

class ClassA:public CBase1, public CBase2
{
public:
	int m_nClassA;
public:
	ClassA():CBase1(), CBase2(){ m_nClassA = 3; }
	~ClassA() {}
	virtual void Function1() { printf("ClassA::Function1"); }
	virtual void Function3() { printf("ClassA::Function3"); }
	virtual void Function5() { printf("ClassA::Function5"); }
};

class ClassB:public ClassA
{
public:
	int m_nClassB;
public:
	ClassB():ClassA() { m_nClassB = 4; }
	~ClassB() {}
	virtual void Function5() { printf("ClassB::Function5"); }
	virtual void Function6() { printf("ClassB::Function6"); }
};
typedef  void (*pFunction)();

int main()
{

	CBase1 objCBase1;
	CBase2 objCBase2;
	ClassA objClassA;
	ClassB objClassB;
	DWORD*  m_dwCBase1_table	= (DWORD*) *(DWORD*)&objCBase1;
	DWORD*  m_dwCBase2_table	= (DWORD*) *(DWORD*)&objCBase2;
	DWORD*  m_dwClassA_table1	= (DWORD*) *(DWORD*)&objClassA;
	DWORD*  m_dwClassA_table2	= (DWORD*) *((DWORD*)&objClassA+2);
	DWORD*  m_dwClassB_table1	= (DWORD*) *(DWORD*)&objClassB;
	DWORD*  m_dwClassB_table2	= (DWORD*) *((DWORD*)&objClassB+2);


	printf("CBase1_VFT:\t0x%08x", m_dwCBase1_table);
	printf("\r\n\t\t0x%08x ", *(m_dwCBase1_table));		((pFunction)*(m_dwCBase1_table))();
	printf("\r\n\t\t0x%08x ",*(m_dwCBase1_table + 1));	((pFunction)*(m_dwCBase1_table + 1))();
	printf("\r\n");

	printf("\r\nCBase2_VFT:\t0x%08x", m_dwCBase2_table);
	printf("\r\n\t\t0x%08x ", *(m_dwCBase2_table));		((pFunction)*(m_dwCBase2_table))();
	printf("\r\n\t\t0x%08x ", *(m_dwCBase2_table + 1));	((pFunction)*(m_dwCBase2_table + 1))();
	printf("\r\n");

	printf("\r\nClassA_VFT1:\t0x%08x", m_dwClassA_table1);
	printf("\r\n\t\t0x%08x ", *(m_dwClassA_table1));	((pFunction)*(m_dwClassA_table1))();
	printf("\r\n\t\t0x%08x ", *(m_dwClassA_table1 + 1));	((pFunction)*(m_dwClassA_table1 + 1))();
	printf("\r\n\t\t0x%08x ", *(m_dwClassA_table1 + 2));	((pFunction)*(m_dwClassA_table1 + 2))();
	printf("\r\nClassA_VFT2:\t0x%08x", m_dwClassA_table2);
	printf("\r\n\t\t0x%08x ", *(m_dwClassA_table2));	((pFunction)*(m_dwClassA_table2))();
	printf("\r\n\t\t0x%08x ", *(m_dwClassA_table2 + 1));	((pFunction)*(m_dwClassA_table2 + 1))();
	printf("\r\n");

	printf("\r\nClassB_VFT1:\t0x%08x", m_dwClassB_table1);
	printf("\r\n\t\t0x%08x ", *(m_dwClassB_table1));	((pFunction)*(m_dwClassB_table1))();
	printf("\r\n\t\t0x%08x ", *(m_dwClassB_table1 + 1));	((pFunction)*(m_dwClassB_table1 + 1))();
	printf("\r\n\t\t0x%08x ", *(m_dwClassB_table1 + 2));	((pFunction)*(m_dwClassB_table1 + 2))();
	printf("\r\n\t\t0x%08x ", *(m_dwClassB_table1 + 3));	((pFunction)*(m_dwClassB_table1 + 3))();
	printf("\r\nClassA_VFT2:\t0x%08x", m_dwClassB_table2);
	printf("\r\n\t\t0x%08x ", *(m_dwClassB_table2));	((pFunction)*(m_dwClassB_table2))();
	printf("\r\n\t\t0x%08x ", *(m_dwClassB_table2 + 1));	((pFunction)*(m_dwClassB_table2 + 1))();

	printf("\r\n");
}

运行结果


图3

将图3结果整合到图2中得到图3,图3方便我们理解。


图3

4- 如何通过虚表来查看类继承关系哪?

每个虚表的前面都会有一个 _s__RTTICompleteObjectLocator 类型的指针。通过分析该结构体就能获得该类的继承关系。

接下来需要了解几个重要结构体:

  _s__RTTICompleteObjectLocator   位于虚表地址-4的地方
  _TypeDescriptor   类型描述信息
  _s__RTTIClassHierarchyDescriptor   类继承描述信息
  _s__RTTIBaseClassArray   基类数组
  _s__RTTIBaseClassDescriptor   基类描述信息
这里的 RTTI 是 RunTime Type Information 的缩写。

4.1- 通过windbg分析 objCBase1的虚表来熟悉上述几个结构体。

Step1:查看objCBase1变量信息得到虚表地址

0:000> dt objCBase1 
Local var @ 0x19fed4 Type CBase1
   +0x000 __VFN_table : 0x00419b34 //虚表地址
   +0x004 m_nBase1         : 0n1

Step2:通过 __VFN_table - 4 查看虚表的 _s__RTTICompleteObjectLocator 结构体

0:000> dd 0x00419b30 L4
00419b30  0041aacc 00411253 0041121c 00000000
//0041aacc 为_s__RTTICompleteObjectLocator *
//00411253 虚表的第一个元素,即CBase1::Function1()
//0041121c 虚表的第二个元素,即CBase1::Function2()

0:000> dx -r1 ((_s__RTTICompleteObjectLocator *)0x0041aacc)
((_s__RTTICompleteObjectLocator *)0x0041aacc)                 : 0x41aacc [Type: _s__RTTICompleteObjectLocator *]
    [+0x000] signature        : 0x0 [Type: unsigned long]
    [+0x004] offset           : 0x0 [Type: unsigned long]
    [+0x008] cdOffset         : 0x0 [Type: unsigned long]
    [+0x00c] pTypeDescriptor  : 0x41c138 [Type: _TypeDescriptor *]
    [+0x010] pClassDescriptor : 0x41aae4 [Type: _s__RTTIClassHierarchyDescriptor *]

step3: 通过pTypeDescriptor查看类型描述信息

0:000> r @$t0=0x0041aacc   //将_s__RTTICompleteObjectLocator的地址赋值给变量@$t0,方便后边引用
0:000> r @$t1=poi($t0+0x0c) //@$t1=pTypeDescriptor
0:000> dx -r1 ((_TypeDescriptor *)(@$t1))
((_TypeDescriptor *)(@$t1))   : 0x41c138 [Type: _TypeDescriptor *]
    [+0x000] pVFTable         : 0x419ecc [Type: void *]
    [+0x004] spare            : 0x0 [Type: void *]
    [+0x008] name             : ".?AVCBase1@@" [Type: char [0]] //该虚表属于CBase1

step4:通过pClassDescriptor查看类的继承描述信息

0:000> r @$t2=poi($t0+0x10);dx -r1 ((_s__RTTIClassHierarchyDescriptor *)(@$t2)) // @$t2是pClassDescriptor
((_s__RTTIClassHierarchyDescriptor *)(@$t1))                 : 0x41aae4 [Type: _s__RTTIClassHierarchyDescriptor *]
    [+0x000] signature        : 0x0 [Type: unsigned long]
    [+0x004] attributes       : 0x0 [Type: unsigned long]
    [+0x008] numBaseClasses   : 0x1 [Type: unsigned long] //几个基类
    [+0x00c] pBaseClassArray  : 0x41aaf8 [Type: _s__RTTIBaseClassArray *] 
                                                          //基类数组,每个元素的类型为_s__RTTIBaseClassDescriptor * 

step5:通过 pBaseClassArray 获取基类信息

0:000> r @$t3 = poi($t2+0c);r $t3
$t3=0041aaf8  //$t3 = pBaseClassArray 

0:000> r @$t4 = poi($t3);r $t4
$t4=0041ab00 //$t4 = pBaseClassArray[0]

0:000> dx -r1 ((_s__RTTIBaseClassDescriptor *)(@$t4))
((_s__RTTIBaseClassDescriptor *)(@$t4))                 : 0x41ab00 [Type: _s__RTTIBaseClassDescriptor *]
    [+0x000] pTypeDescriptor  : 0x41c138 [Type: _TypeDescriptor *]
    [+0x004] numContainedBases : 0x0 [Type: unsigned long] //包含基类个数为0,表示只有自己一个类
    [+0x008] where            [Type: _PMD]
    [+0x014] attributes       : 0x40 [Type: unsigned long]
    [+0x018] pClassDescriptor : 0x41aae4 [Type: _s__RTTIClassHierarchyDescriptor *] //该类的描述信息和step2中的一样

4.2- 通过windbg分析objClassA的虚表

0:000> dt objClassA 
Local var @ 0x19fea8 Type ClassA
   +0x000 __VFN_table : 0x00419bb4 
   +0x004 m_nBase1         : 0n1
   +0x008 __VFN_table : 0x00419bc8 
   +0x00c m_nBase2         : 0n2
   +0x010 m_nClassA        : 0n3

//虚表1的相关信息
0:000> dd 0x00419bb4-4 L8
00419bb0  0041ab7c 004111b3 0041121c 0041132f // 0041ab7c 是虚表1的描述信息结构体
00419bc0  00000000 0041ac00 004113f7 004110c8

//虚表2的相关信息
0:000> dd 0x00419bc8-4 L4
00419bc4  0041ac00 004113f7 004110c8 00000000 // 0041ac00 是虚表2的描述信息结构体

//虚表1的描述信息
0:000> dx -r1 ((_s__RTTICompleteObjectLocator *)0x0041ab7c)
((_s__RTTICompleteObjectLocator *)0x0041ab7c)                 : 0x41ab7c [Type: _s__RTTICompleteObjectLocator *]
    [+0x000] signature        : 0x0 [Type: unsigned long]
    [+0x004] offset           : 0x0 [Type: unsigned long]
    [+0x008] cdOffset         : 0x0 [Type: unsigned long]
    [+0x00c] pTypeDescriptor  : 0x41c170 [Type: _TypeDescriptor *]
    [+0x010] pClassDescriptor : 0x41ab94 [Type: _s__RTTIClassHierarchyDescriptor *]

//查看虚表2的描述信息
0:000> dx -r1 ((_s__RTTICompleteObjectLocator *)0x0041ac00)
((_s__RTTICompleteObjectLocator *)0x0041ac00)                 : 0x41ac00 [Type: _s__RTTICompleteObjectLocator *]
    [+0x000] signature        : 0x0 [Type: unsigned long]
    [+0x004] offset           : 0x8 [Type: unsigned long] //虚表2在对象中的偏移量
    [+0x008] cdOffset         : 0x0 [Type: unsigned long]
    [+0x00c] pTypeDescriptor  : 0x41c170 [Type: _TypeDescriptor *]//与虚表1中的pTypeDescriptor相同
    [+0x010] pClassDescriptor : 0x41ab94 [Type: _s__RTTIClassHierarchyDescriptor *]
                                                                  //与虚表1中的pClassDescriptor相同

//查看虚表1,2属于哪个类
0:000> dx -r1 ((_TypeDescriptor *)0x41c170)
((_TypeDescriptor *)0x41c170)                 : 0x41c170 [Type: _TypeDescriptor *]
    [+0x000] pVFTable         : 0x419ecc [Type: void *]
    [+0x004] spare            : 0x0 [Type: void *]
    [+0x008] name             : ".?AVClassA@@" [Type: char [0]] // 虚表1属于ClassA

//查看类的继承信息
0:000> dx -r1 ((_s__RTTIClassHierarchyDescriptor *)0x41ab94)
((_s__RTTIClassHierarchyDescriptor *)0x41ab94)                 : 0x41ab94 [Type: _s__RTTIClassHierarchyDescriptor *]
    [+0x000] signature        : 0x0 [Type: unsigned long]
    [+0x004] attributes       : 0x1 [Type: unsigned long]
    [+0x008] numBaseClasses   : 0x3 [Type: unsigned long] // 有3个基类
    [+0x00c] pBaseClassArray  : 0x41aba8 [Type: _s__RTTIBaseClassArray *] //基类数组

//查看3个基类的信息
0:000> dd 0x41aba8  L4
0041aba8  0041abb8 0041ab00 0041abdc 00000000

//基类1:0x0041abb8
0:000> dx -r1 ((_s__RTTIBaseClassDescriptor *)0x0041abb8)
((_s__RTTIBaseClassDescriptor *)0x0041abb8)                 : 0x41abb8 [Type: _s__RTTIBaseClassDescriptor *]
    [+0x000] pTypeDescriptor  : 0x41c170 [Type: _TypeDescriptor *]
    [+0x004] numContainedBases : 0x2 [Type: unsigned long] //该类继承了2个基类
    [+0x008] where            [Type: _PMD]
    [+0x014] attributes       : 0x40 [Type: unsigned long]
    [+0x018] pClassDescriptor : 0x41ab94 [Type: _s__RTTIClassHierarchyDescriptor *]
0:000> dx -r1 ((_TypeDescriptor *)0x41c170)
((_TypeDescriptor *)0x41c170)                 : 0x41c170 [Type: _TypeDescriptor *]
    [+0x000] pVFTable         : 0x419ecc [Type: void *]
    [+0x004] spare            : 0x0 [Type: void *]
    [+0x008] name             : ".?AVClassA@@" [Type: char [0]]
//0x0041abb8描述的类名是ClassA,且ClassA有两个父类。

//基类2:0x0041ab00
0:000> dx -r1 ((_s__RTTIBaseClassDescriptor *)0x0041ab00)
((_s__RTTIBaseClassDescriptor *)0x0041ab00)                 : 0x41ab00 [Type: _s__RTTIBaseClassDescriptor *]
    [+0x000] pTypeDescriptor  : 0x41c138 [Type: _TypeDescriptor *]
    [+0x004] numContainedBases : 0x0 [Type: unsigned long] //该类没有父类
    [+0x008] where            [Type: _PMD]
    [+0x014] attributes       : 0x40 [Type: unsigned long]
    [+0x018] pClassDescriptor : 0x41aae4 [Type: _s__RTTIClassHierarchyDescriptor *]
0:000> dx -r1 ((TestProjectC__!_TypeDescriptor *)0x41c138)
((TestProjectC__!_TypeDescriptor *)0x41c138)                 : 0x41c138 [Type: _TypeDescriptor *]
    [+0x000] pVFTable         : 0x419ecc [Type: void *]
    [+0x004] spare            : 0x0 [Type: void *]
    [+0x008] name             : ".?AVCBase1@@" [Type: char [0]]
//0x0041ab00描述的类名为CBase1,且CBase1没有父类

//基类3:0x0041abdc
0:000> dx -r1 ((_s__RTTIBaseClassDescriptor *)0x0041abdc)
((_s__RTTIBaseClassDescriptor *)0x0041abdc)                 : 0x41abdc [Type: _s__RTTIBaseClassDescriptor *]
    [+0x000] pTypeDescriptor  : 0x41c154 [Type: _TypeDescriptor *]
    [+0x004] numContainedBases : 0x0 [Type: unsigned long] //该类没有父类
    [+0x008] where            [Type: _PMD]
    [+0x014] attributes       : 0x40 [Type: unsigned long]
    [+0x018] pClassDescriptor : 0x41ab3c [Type: _s__RTTIClassHierarchyDescriptor *]
0:000> dx -r1 ((_TypeDescriptor *)0x41c154)
((_TypeDescriptor *)0x41c154)                 : 0x41c154 [Type: _TypeDescriptor *]
    [+0x000] pVFTable         : 0x419ecc [Type: void *]
    [+0x004] spare            : 0x0 [Type: void *]
    [+0x008] name             : ".?AVCBase2@@" [Type: char [0]]
//0x0041abdc描述的类名为CBase2,且CBase2没有父类

0:000> dx -r1 (*((_PMD *)0x41abe4))
(*((_PMD *)0x41abe4))                 [Type: _PMD]
    [+0x000] mdisp            : 8 [Type: int] //该类的成员数据在子类对象中的偏移量
    [+0x004] pdisp            : -1 [Type: int]
    [+0x008] vdisp            : 0 [Type: int]



整理了一下这几个结构体的关系图,图4.


图4

自己可以试着手动分析objClassB.

有时间再画一下剩下的两个关系图。




[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (5)
kongfubull 2019-7-29 11:17
2
0
光是这个格式,看着就很舒服,内容也很实在,mark
binlmmhc 2019-7-29 14:01
3
0
可以
yber 2019-7-29 21:11
4
0
位于虚表地址-4的地方
Koorey 2019-7-30 22:15
5
0
8月1号我再把自己的测试源码和编译后的程序上传一下。
Anskya 20 2019-7-31 11:24
6
0
配合《深度探索c++对象模型》这本书看更舒服哦~理论结合实际
游客
登录 | 注册 方可回帖
返回