通过windbg学习虚表继承及布局
2019-07-30 13:36:24 Author: bbs.pediy.com(查看原文) 阅读量:133 收藏

  1. 看雪论坛
  2. 『软件逆向』

[原创] 通过windbg学习虚表继承及布局

1天前 328

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

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

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

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

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

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

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

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

4) 类的虚表可以有多个

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

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

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

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

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


图1

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

CBase1,CBase2各有一张虚表;

ClassA,ClassB各有有两个虚表;

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

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

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


图2

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

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方便我们理解。

每个虚表的前面都会有一个 _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.

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


[公告]看雪 2019 安全开发者峰会,圆满落幕!现场精彩回顾!

返回


文章来源: https://bbs.pediy.com/thread-253429.htm
如有侵权请联系:admin#unsafe.sh