游戏安全实验室 首页 技术入门 查看内容

 阅读目录

虚函数逆向分析

发布于:2016-7-6 12:21   |    123002次阅读 作者: 管理员    |   原作者: TP   |   来自: 原创

[背景]

       虚函数表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

      编译器应该保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

 

 

无继承时虚函数表

编写demo:

#include <iostream>

using namespace std;

 

class base_class

{

private:

int m_base;

public:

virtual void v_func1()

{

cout << "This is base_class's v_func1()" << endl;

}

virtual void v_func2()

{

cout << "This is base_class's v_func2()" << endl;

}

virtual void v_func3()

{

cout << "This is base_class's v_func3()" << endl;

}

};

 

OD载入逆向分析

构造函数

我们查看一下虚表指针

内存中应该为:

 

 

 

 

 

虚表单一继承:

改写demo

class base_class

{

public:

virtual void v_func1()

{

cout << "This is base_class's v_func1()" << endl;

}

virtual void v_func2()

{

cout << "This is base_class's v_func2()" << endl;

}

virtual void v_func3()

{

cout << "This is base_class's v_func3()" << endl;

}

};

class dev_class : public base_class

{

public:

virtual void v_func4()

{

cout << "This is dev_class's v_func4()" << endl;

}

virtual void v_func5()

{

cout << "This is dev_class's v_func5()" << endl;

}

};

 

构造函数逆向如下

基类构造函数改写指针

此时虚表

 

在派生类中又改写了虚函数指针

此时虚表

在内存中的布局应该为:

 

在改写虚表指针的时候,按照父类-子类的顺序存放在虚表中

 

重写父类虚函数继承

 

我们改写demo:

class base_class

{

public:

virtual void v_func1()

{

cout << "This is base_class's v_func1()" << endl;

}

virtual void v_func2()

{

cout << "This is base_class's v_func2()" << endl;

}

virtual void v_func3()

{

cout << "This is base_class's v_func3()" << endl;

}

};

class dev_class : public base_class

{

public:

virtual void v_func3()

{

cout << "This is dev_class's v_func4()" << endl;

}

virtual void v_func4()

{

cout << "This is dev_class's v_func5()" << endl;

}

};

 

OD载入分析构造函数

我们按照上述方法打印虚表

在构造基类的时候

在派生类修改虚表指针后

可以很清楚的发现,在第三个虚函数地址被派生类修改

内存中布局应该是这样

 

 

 

多重继承下的虚函数表_子类没有改写父类

 

我们改写demo

 

class base_class_A

{

public:

virtual void v_func1()

{

cout << "This is base_class_A's v_func1()" << endl;

}

virtual void v_func2()

{

cout << "This is base_class_A's v_func2()" << endl;

}

};

class base_class_B

{

public:

virtual void v_func3()

{

cout << "This is base_class_B's v_func1()" << endl;

}

virtual void v_func4()

{

cout << "This is base_class_B's v_func2()" << endl;

}

};

class dev_class : public base_class_A,base_class_B

{

public:

virtual void v_func5()

{

cout << "This is dev_class`s v_func" << endl;

}

};

 

OD载入分析构造函数

Base_a 虚表

Base_b

Dev:

修改虚表指针

通过分析我们可以发现当多重继承中会存在多张虚表

内存中的布局应该为:

 

 

多重继承下的虚函数表_子类改写父类

我们改写demo

class base_class_A

{

public:

virtual void v_func1()

{

cout << "This is base_class_A's v_func1()" << endl;

}

virtual void v_func2()

{

cout << "This is base_class_A's v_func2()" << endl;

}

};

class base_class_B

{

public:

virtual void v_func3()

{

cout << "This is base_class_B's v_func1()" << endl;

}

virtual void v_func4()

{

cout << "This is base_class_B's v_func2()" << endl;

}

};

class dev_class : public base_class_A,base_class_B

{

public:

virtual void v_func1()

{

cout << "This is dev_class`s v_func1" << endl;

}

virtual void v_func3()

{

cout << "This is dev_class`s v_func3" << endl;

}

virtual void v_fun5()

{

cout << "This is dev_class`s v_func5" << endl;

}

};

虚表为

内存中的布局为

 

我们稍微修改下我们的demo

加入成员变量

class root 

{

private:

int m_r1;

int m_r2;

public:

root()

{

m_r1 = 1;

m_r2 = 2;

}

~root(){};

virtual void v_funr()

{

cout << "This is root" << endl;

}

 

};

class base_class_A : public root

{

private:

int m_a;

 

public:

base_class_A()

{

m_a = 3;

}

~base_class_A(){};

virtual void v_func1()

{

cout << "This is base_class_A's v_func1()" << endl;

}

virtual void v_func2()

{

cout << "This is base_class_A's v_func2()" << endl;

}

};

class base_class_B : public root

{

private: 

int m_b ;

 

public:

base_class_B()

{

m_b = 4;

}

~base_class_B(){};

void v_func3()

{

cout << "This is base_class_B's v_func1()" << endl;

}

void v_func4()

{

cout << "This is base_class_B's v_func2()" << endl;

}

};

 

class dev_class : public base_class_A,base_class_B

{

private: 

int m_a;

int m_b;

int m_c;

public:

dev_class();

~dev_class(){};

virtual void v_func1()

{

cout << "This is dev_class`s v_func1" << endl;

}

virtual void v_func3()

{

cout << "This is dev_class`s v_func3" << endl;

}

virtual void v_fun5()

{

cout << "This is dev_class`s v_func5" << endl;

}

};

 

 dev_class :: dev_class():m_a(1),m_b(2),m_c(3)

{

 

}

 

加入成员变量

我们看下最开始的基类root的构造

虚表为

 

 

 

 

 

 

虚拟多重继承

Demo:

class root 

{

private:

int m_r1;

int m_r2;

public:

root()

{

m_r1 = 1;

m_r2 = 2;

}

~root(){};

virtual void v_funr()

{

cout << "This is root" << endl;

}

 

 

};

class base_class : virtual public root

{

private:

int m_a;

int m_b;

 

public:

base_class()

{

m_a = 3;

m_b = 4;

}

~base_class(){};

virtual void v_funr()

{

cout << "This is base_class_A's v_funcr()" << endl;

}

virtual void v_func1()

{

cout << "This is base_class_A's v_func1()" << endl;

}

virtual void v_func2()

{

cout << "This is base_class_A's v_func2()" << endl;

}

 

};

 

class dev_class :virtual public base_class

{

private: 

int m_a;

int m_b;

int m_c;

public:

dev_class();

~dev_class(){};

virtual void v_funr()

{

cout << "This is dev_class's v_funcr()" << endl;

}

virtual void v_func1()

{

cout << "This is dev_class`s v_func1" << endl;

}

virtual void v_func3()

{

cout << "This is dev_class`s v_func3" << endl;

}

virtual void v_fun5()

{

cout << "This is dev_class`s v_func5" << endl;

}

 

};

dev_class :: dev_class():m_a(1),m_b(2),m_c(3)

{

 

}

Dev_class的时候

此时[eax+0x4]和[eax+0x2c]存放的不再为虚表指针,而是一个偏转

我们可以查看下地址

 

在root构造时,

00A22BA7    8B45 F8         mov eax,dword ptr ss:[ebp-0x8]

00A22BAA    C700 90DCA200   mov dword ptr ds:[eax],offset vft.base_class::`vftable'

00A22BB0    8B45 F8         mov eax,dword ptr ss:[ebp-0x8]

00A22BB3    8B48 04         mov ecx,dword ptr ds:[eax+0x4]                                          ; 得到偏转表地址

00A22BB6    8B51 04         mov edx,dword ptr ds:[ecx+0x4]                                          ; 得到偏移地址

00A22BB9    8B45 F8         mov eax,dword ptr ss:[ebp-0x8]

00A22BBC    C74410 04 A0DCA>mov dword ptr ds:[eax+edx+0x4],offset vft.base_class::`vftable'         ; 通过偏转地址计算得到虚基类指针并修改

00A22BC4    8B45 F8         mov eax,dword ptr ss:[ebp-0x8]

00A22BC7    8B48 04         mov ecx,dword ptr ds:[eax+0x4]

00A22BCA    8B51 04         mov edx,dword ptr ds:[ecx+0x4]

00A22BCD    83EA 10         sub edx,0x10                                                            ; 减去类大小得到相对长度

00A22BD0    8B45 F8         mov eax,dword ptr ss:[ebp-0x8]

00A22BD3    8B48 04         mov ecx,dword ptr ds:[eax+0x4]                                          ; 得到偏移表地址

00A22BD6    8B41 04         mov eax,dword ptr ds:[ecx+0x4]                                          ; 得到偏移

00A22BD9    8B4D F8         mov ecx,dword ptr ss:[ebp-0x8]

00A22BDC    891401          mov dword ptr ds:[ecx+eax],edx                                          ; 将偏移大小存放在虚基类前

00A22BDF    8B45 F8         mov eax,dword ptr ss:[ebp-0x8]

00A22BE2    C740 08 0300000>mov dword ptr ds:[eax+0x8],0x3

00A22BE9    8B45 F8         mov eax,dword ptr ss:[ebp-0x8]

 

 

可以发现刚刚分析 出来的偏转地址均指向 虚基类(root)的虚表指针

FFFFFFFC则为-4,指向偏转表的前一个DWORD地址

我们继续看base类的构造

通过偏移,使子类可以很容易访问到虚基类,进而对虚基类指针进行改写

 

00FE2C8C    837D 08 00      cmp dword ptr ss:[ebp+0x8],0x0                                          ; 判断虚基类

00FE2C90    74 51           je Xvft.00FE2CE3

00FE2C92    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2C95    C740 04 58DDFE0>mov dword ptr ds:[eax+0x4],offset vft.dev_class::`vbtable'              ; 偏转表

00FE2C9C    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2C9F    C740 2C 68DDFE0>mov dword ptr ds:[eax+0x2C],offset vft.dev_class::`vbtable'             ; 偏转表

00FE2CA6    8B4D EC         mov ecx,dword ptr ss:[ebp-0x14]

00FE2CA9    83C1 18         add ecx,0x18                                                            ; 得到虚基类指针

00FE2CAC    E8 5FE7FFFF     call vft.00FE1410

00FE2CB1    C745 FC 0000000>mov dword ptr ss:[ebp-0x4],0x0

00FE2CB8    8B85 20FFFFFF   mov eax,dword ptr ss:[ebp-0xE0]

00FE2CBE    83C8 01         or eax,0x1

00FE2CC1    8985 20FFFFFF   mov dword ptr ss:[ebp-0xE0],eax                                         ; 虚基类已经构造

00FE2CC7    6A 00           push 0x0

00FE2CC9    8B4D EC         mov ecx,dword ptr ss:[ebp-0x14]

00FE2CCC    83C1 28         add ecx,0x28

00FE2CCF    E8 BFE6FFFF     call vft.00FE1393                                                       ; base构造

00FE2CD4    8B85 20FFFFFF   mov eax,dword ptr ss:[ebp-0xE0]

00FE2CDA    83C8 02         or eax,0x2

00FE2CDD    8985 20FFFFFF   mov dword ptr ss:[ebp-0xE0],eax

00FE2CE3    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2CE6    C700 30DDFE00   mov dword ptr ds:[eax],offset vft.dev_class::`vftable'                  ; dev的虚表指针(指向fun3,fun5)

00FE2CEC    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2CEF    8B48 04         mov ecx,dword ptr ds:[eax+0x4]

00FE2CF2    8B51 04         mov edx,dword ptr ds:[ecx+0x4]                                          ; 得到偏移

00FE2CF5    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2CF8    C74410 04 40DDF>mov dword ptr ds:[eax+edx+0x4],offset vft.dev_class::`vftable'          ; 通过偏移访问到虚基类并修改

00FE2D00    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2D03    8B48 04         mov ecx,dword ptr ds:[eax+0x4]

00FE2D06    8B51 08         mov edx,dword ptr ds:[ecx+0x8]                                          ; 取到base类偏移

00FE2D09    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]                                         ; 得到基址

00FE2D0C    C74410 04 4CDDF>mov dword ptr ds:[eax+edx+0x4],offset vft.dev_class::`vftable'          ; 修改base类虚表指针

00FE2D14    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2D17    8B48 04         mov ecx,dword ptr ds:[eax+0x4]

00FE2D1A    8B51 04         mov edx,dword ptr ds:[ecx+0x4]                                          ; 得到长度

00FE2D1D    83EA 14         sub edx,0x14                                                            ; 减去类大小

00FE2D20    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2D23    8B48 04         mov ecx,dword ptr ds:[eax+0x4]

00FE2D26    8B41 04         mov eax,dword ptr ds:[ecx+0x4]

00FE2D29    8B4D EC         mov ecx,dword ptr ss:[ebp-0x14]

00FE2D2C    891401          mov dword ptr ds:[ecx+eax],edx                                          ; 将偏移存放在虚基类前

00FE2D2F    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2D32    8B48 04         mov ecx,dword ptr ds:[eax+0x4]

00FE2D35    8B51 08         mov edx,dword ptr ds:[ecx+0x8]

00FE2D38    83EA 24         sub edx,0x24

00FE2D3B    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]                                         ; 得到基址

00FE2D3E    8B48 04         mov ecx,dword ptr ds:[eax+0x4]                                          ; 偏转表

00FE2D41    8B41 08         mov eax,dword ptr ds:[ecx+0x8]                                          ; 偏移大小

00FE2D44    8B4D EC         mov ecx,dword ptr ss:[ebp-0x14]                                         ; 基址

00FE2D47    891401          mov dword ptr ds:[ecx+eax],edx                                          ; 将相对偏移存放在base前

00FE2D4A    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2D4D    C740 08 0100000>mov dword ptr ds:[eax+0x8],0x1                                          ; m_a = 1

00FE2D54    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2D57    C740 0C 0200000>mov dword ptr ds:[eax+0xC],0x2                                          ; m_b = 2

00FE2D5E    8B45 EC         mov eax,dword ptr ss:[ebp-0x14]

00FE2D61    C740 10 0300000>mov dword ptr ds:[eax+0x10],0x3                                         ; m_c = 3

最终虚表为

在虚表中我们发现

而我们在funcr时发现有这样的结构

dev.func1也有这样的结构

我们现在总结在内存中,虚拟继承结构如下:

 

结论:

在分析虚函数,当存在多重继承(虚拟继承中有虚函数)情况下,虚表的结构会发生变化,将会多出一个偏转表,通过对偏移地址的操作进而去访问和改写父类虚表指针。而其在内存中的结构也与普通继承有些不同(考虑跟编译器有关!)。


*转载请注明来自游戏安全实验室(GSLAB.QQ.COM)

分享到:
踩0 赞0

收藏

上一篇:植物大战僵尸游戏分析:种植功能相关逻辑分析

下一篇:C语言与汇编语言对照分析

最新评论
引用 月魂 2016-7-11 11:36
代码编辑器不友好。

查看全部评论(1)

B Color Image Link Quote Code Smilies

发表评论