mysql 字符串反续函数_虚函数逆向分析

[背景]

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

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

无继承时虚函数表

1 编写demo:2 #include

3 using namespacestd;4

5 classbase_class6 {7 private:8 intm_base;9 public:10 virtual voidv_func1()11 {12 cout << "This is base_class's v_func1()" <

OD载入逆向分析

构造函数

77df8b47df5a40e9ff29fccc6807bf37.png

我们查看一下虚表指针

849ade8dfc12a173716e0f56519704bd.png

内存中应该为:

cc4de015e48cafa3342d06b7b778bd04.png

虚表单一继承:

1 改写demo2 classbase_class3 {4 public:5 virtual voidv_func1()6 {7 cout << "This is base_class's v_func1()" <

构造函数逆向如下

基类构造函数改写指针

6f3e8d06e82122b876ea2f30137a8cfa.png

此时虚表

8bcae74341c8354b47e26314e3c92e0c.png

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

960a7b11ae92b0c4c035d928eb015186.png

此时虚表

ea46f0ab973c3ebab4a6d1b05e010bc2.png

在内存中的布局应该为:

56253f3c9dbf807638f2f73fed69f04c.png

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

重写父类虚函数继承

1 我们改写demo:2 classbase_class3 {4 public:5 virtual voidv_func1()6 {7 cout << "This is base_class's v_func1()" <

OD载入分析构造函数

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

在构造基类的时候

30256262f46e82fc405e44b8d552d3b4.png

在派生类修改虚表指针后

ed7605af8122b752097c2d490e6a186c.png

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

内存中布局应该是这样

7d575cc4b99a59912cf65f1e259f9cc1.png

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

1 我们改写demo2

3 classbase_class_A4 {5 public:6 virtual voidv_func1()7 {8 cout << "This is base_class_A's v_func1()" <

15 };16 classbase_class_B17 {18 public:19 virtual voidv_func3()20 {21 cout << "This is base_class_B's v_func1()" <

36 };

OD载入分析构造函数

11f89da695ee0bb9ae8d1454cdb1acbd.png

bc434a8189417ab300335329dc7b8caf.png

Base_a 虚表

137fb3b57a5dfa2d9335932d724ae7b1.png

Base_b

51c76a1926bf66a160bb8033f60f6382.png

Dev:

修改虚表指针

67e0b831be87f5d88eeae647cfcfdc27.png

308eca660a0ac2f843820e6c2a601875.png

00fb71b452faf15f1d6a9af3a81f6739.png

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

内存中的布局应该为:

4fcc76ae86e30dc687ca1a1b1e12cf86.png

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

1 我们改写demo2 classbase_class_A3 {4 public:5 virtual voidv_func1()6 {7 cout << "This is base_class_A's v_func1()" <

14 };15 classbase_class_B16 {17 public:18 virtual voidv_func3()19 {20 cout << "This is base_class_B's v_func1()" <

43 };

虚表为

43076e7afe442c68011a6f6421e15fda.png

内存中的布局为

8af0a65cc62413cc7b75126a4c51726a.png

我们稍微修改下我们的demo

加入成员变量:

1 classroot2 {3 private:4 intm_r1;5 intm_r2;6 public:7 root()8 {9 m_r1 = 1;10 m_r2 = 2;11 }12 ~root(){};13 virtual voidv_funr()14 {15 cout << "This is root" <

18

19 };20 class base_class_A : publicroot21 {22 private:23 intm_a;24

25 public:26 base_class_A()27 {28 m_a = 3;29 }30 ~base_class_A(){};31 virtual voidv_func1()32 {33 cout << "This is base_class_A's v_func1()" <

40 };41 class base_class_B : publicroot42 {43 private:44 intm_b ;45

46 public:47 base_class_B()48 {49 m_b = 4;50 }51 ~base_class_B(){};52 voidv_func3()53 {54 cout << "This is base_class_B's v_func1()" <

62 class dev_class : publicbase_class_A,base_class_B63 {64 private:65 intm_a;66 intm_b;67 intm_c;68 public:69 dev_class();70 ~dev_class(){};71 virtual voidv_func1()72 {73 cout << "This is dev_class`s v_func1" <

84 };85

86 dev_class :: dev_class():m_a(1),m_b(2),m_c(3)87 {88

89 }

加入成员变量

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

31aacf069a7bbf1b229a6fc88c45f0a6.png

虚表为

a82a79fa074ace0fabade97c44d9aee6.png

虚拟多重继承

1 Demo:2 classroot3 {4 private:5 intm_r1;6 intm_r2;7 public:8 root()9 {10 m_r1 = 1;11 m_r2 = 2;12 }13 ~root(){};14 virtual voidv_funr()15 {16 cout << "This is root" <

19

20 };21 class base_class : virtual publicroot22 {23 private:24 intm_a;25 intm_b;26

27 public:28 base_class()29 {30 m_a = 3;31 m_b = 4;32 }33 ~base_class(){};34 virtual voidv_funr()35 {36 cout << "This is base_class_A's v_funcr()" <

47 };48

49 class dev_class :virtual publicbase_class50 {51 private:52 intm_a;53 intm_b;54 intm_c;55 public:56 dev_class();57 ~dev_class(){};58 virtual voidv_funr()59 {60 cout << "This is dev_class's v_funcr()" <

75 };76 dev_class :: dev_class():m_a(1),m_b(2),m_c(3)77 {78

79 }

Dev_class的时候

cd330077bb779bb748afbbe7b6c34366.png

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

我们可以查看下地址

550a9f8b29ffe5fd4f3e36235c42b86b.png

37ba81779fb255c12bb29093e0830019.png

在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],0x300A22BE9 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]

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

717eec8d45bdf7e70e9fa91ee92f2b82.png

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

我们继续看base类的构造

23a51fb315d4156da5af5330c2d1720f.png

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

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],0x000FE2CB8 8B85 20FFFFFF mov eax,dword ptr ss:[ebp-0xE0]00FE2CBE 83C8 01 oreax,0x100FE2CC1 8985 20FFFFFF mov dword ptr ss:[ebp-0xE0],eax ;虚基类已经构造

00FE2CC7 6A 00 push0x000FE2CC9 8B4D EC mov ecx,dword ptr ss:[ebp-0x14]00FE2CCC 83C1 28 addecx,0x2800FE2CCF E8 BFE6FFFF call vft.00FE1393 ;base构造

00FE2CD4 8B85 20FFFFFF mov eax,dword ptr ss:[ebp-0xE0]00FE2CDA 83C8 02 oreax,0x200FE2CDD 8985 20FFFFFF mov dword ptr ss:[ebp-0xE0],eax00FE2CE3 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

最终虚表为

b2d01a51365a778415d5a00364db7d1a.png

在虚表中我们发现

cce800b71145cec5361e7b38bb90b209.png

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

77abeb0f4c320da9d026c02141cfb17c.png

在dev.func1也有这样的结构

e3e1ad51c3d605f593342d59cc353190.png

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

0ce2b0e4c24ba26bcaaaeec03eeba8d8.png

结论:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值