zoukankan      html  css  js  c++  java
  • 虚函数逆向分析

    [背景]

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

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

     

     

    无继承时虚函数表

     1 编写demo:
     2 #include <iostream>
     3 using namespace std;
     4  
     5 class base_class
     6 {
     7 private:
     8 int m_base;
     9 public:
    10 virtual void v_func1()
    11 {
    12 cout << "This is base_class's v_func1()" << endl;
    13 }
    14 virtual void v_func2()
    15 {
    16 cout << "This is base_class's v_func2()" << endl;
    17 }
    18 virtual void v_func3()
    19 {
    20 cout << "This is base_class's v_func3()" << endl;
    21 }
    22 };

    OD载入逆向分析

    构造函数

    我们查看一下虚表指针

    内存中应该为:

     

     

     

     

     

    虚表单一继承:

     1 改写demo
     2 class base_class
     3 {
     4 public:
     5 virtual void v_func1()
     6 {
     7 cout << "This is base_class's v_func1()" << endl;
     8 }
     9 virtual void v_func2()
    10 {
    11 cout << "This is base_class's v_func2()" << endl;
    12 }
    13 virtual void v_func3()
    14 {
    15 cout << "This is base_class's v_func3()" << endl;
    16 }
    17 };
    18 class dev_class : public base_class
    19 {
    20 public:
    21 virtual void v_func4()
    22 {
    23 cout << "This is dev_class's v_func4()" << endl;
    24 }
    25 virtual void v_func5()
    26 {
    27 cout << "This is dev_class's v_func5()" << endl;
    28 }
    29 };

    构造函数逆向如下

    基类构造函数改写指针

    此时虚表

     

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

    此时虚表

    在内存中的布局应该为:

     

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

     

    重写父类虚函数继承

     1 我们改写demo:
     2 class base_class
     3 {
     4 public:
     5 virtual void v_func1()
     6 {
     7 cout << "This is base_class's v_func1()" << endl;
     8 }
     9 virtual void v_func2()
    10 {
    11 cout << "This is base_class's v_func2()" << endl;
    12 }
    13 virtual void v_func3()
    14 {
    15 cout << "This is base_class's v_func3()" << endl;
    16 }
    17 };
    18 class dev_class : public base_class
    19 {
    20 public:
    21 virtual void v_func3()
    22 {
    23 cout << "This is dev_class's v_func4()" << endl;
    24 }
    25 virtual void v_func4()
    26 {
    27 cout << "This is dev_class's v_func5()" << endl;
    28 }
    29 };

    OD载入分析构造函数

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

    在构造基类的时候

    在派生类修改虚表指针后

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

    内存中布局应该是这样

     

     

     

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

     1 我们改写demo
     2  
     3 class base_class_A
     4 {
     5 public:
     6 virtual void v_func1()
     7 {
     8 cout << "This is base_class_A's v_func1()" << endl;
     9 }
    10 virtual void v_func2()
    11 {
    12 cout << "This is base_class_A's v_func2()" << endl;
    13 }
    14 
    15 };
    16 class base_class_B
    17 {
    18 public:
    19 virtual void v_func3()
    20 {
    21 cout << "This is base_class_B's v_func1()" << endl;
    22 }
    23 virtual void v_func4()
    24 {
    25 cout << "This is base_class_B's v_func2()" << endl;
    26 }
    27 };
    28 class dev_class : public base_class_A,base_class_B
    29 {
    30 public:
    31 virtual void v_func5()
    32 {
    33 cout << "This is dev_class`s v_func" << endl;
    34 }
    35 
    36 };

    OD载入分析构造函数

    Base_a 虚表

    Base_b

    Dev:

    修改虚表指针

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

    内存中的布局应该为:

     

     

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

     1 我们改写demo
     2 class base_class_A
     3 {
     4 public:
     5 virtual void v_func1()
     6 {
     7 cout << "This is base_class_A's v_func1()" << endl;
     8 }
     9 virtual void v_func2()
    10 {
    11 cout << "This is base_class_A's v_func2()" << endl;
    12 }
    13 
    14 };
    15 class base_class_B
    16 {
    17 public:
    18 virtual void v_func3()
    19 {
    20 cout << "This is base_class_B's v_func1()" << endl;
    21 }
    22 virtual void v_func4()
    23 {
    24 cout << "This is base_class_B's v_func2()" << endl;
    25 }
    26 };
    27 class dev_class : public base_class_A,base_class_B
    28 {
    29 public:
    30 virtual void v_func1()
    31 {
    32 cout << "This is dev_class`s v_func1" << endl;
    33 }
    34 virtual void v_func3()
    35 {
    36 cout << "This is dev_class`s v_func3" << endl;
    37 }
    38 virtual void v_fun5()
    39 {
    40 cout << "This is dev_class`s v_func5" << endl;
    41 }
    42 
    43 };

    虚表为

    内存中的布局为

     

    我们稍微修改下我们的demo

    加入成员变量:

     1 class root 
     2 {
     3 private:
     4 int m_r1;
     5 int m_r2;
     6 public:
     7 root()
     8 {
     9 m_r1 = 1;
    10 m_r2 = 2;
    11 }
    12 ~root(){};
    13 virtual void v_funr()
    14 {
    15 cout << "This is root" << endl;
    16 }
    17  
    18 
    19 };
    20 class base_class_A : public root
    21 {
    22 private:
    23 int m_a;
    24  
    25 public:
    26 base_class_A()
    27 {
    28 m_a = 3;
    29 }
    30 ~base_class_A(){};
    31 virtual void v_func1()
    32 {
    33 cout << "This is base_class_A's v_func1()" << endl;
    34 }
    35 virtual void v_func2()
    36 {
    37 cout << "This is base_class_A's v_func2()" << endl;
    38 }
    39 
    40 };
    41 class base_class_B : public root
    42 {
    43 private: 
    44 int m_b ;
    45  
    46 public:
    47 base_class_B()
    48 {
    49 m_b = 4;
    50 }
    51 ~base_class_B(){};
    52 void v_func3()
    53 {
    54 cout << "This is base_class_B's v_func1()" << endl;
    55 }
    56 void v_func4()
    57 {
    58 cout << "This is base_class_B's v_func2()" << endl;
    59 }
    60 };
    61  
    62 class dev_class : public base_class_A,base_class_B
    63 {
    64 private: 
    65 int m_a;
    66 int m_b;
    67 int m_c;
    68 public:
    69 dev_class();
    70 ~dev_class(){};
    71 virtual void v_func1()
    72 {
    73 cout << "This is dev_class`s v_func1" << endl;
    74 }
    75 virtual void v_func3()
    76 {
    77 cout << "This is dev_class`s v_func3" << endl;
    78 }
    79 virtual void v_fun5()
    80 {
    81 cout << "This is dev_class`s v_func5" << endl;
    82 }
    83 
    84 };
    85  
    86  dev_class :: dev_class():m_a(1),m_b(2),m_c(3)
    87 {
    88  
    89 }

    加入成员变量

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

    虚表为

     

     

    虚拟多重继承

     1 Demo:
     2 class root 
     3 {
     4 private:
     5 int m_r1;
     6 int m_r2;
     7 public:
     8 root()
     9 {
    10 m_r1 = 1;
    11 m_r2 = 2;
    12 }
    13 ~root(){};
    14 virtual void v_funr()
    15 {
    16 cout << "This is root" << endl;
    17 }
    18  
    19  
    20 };
    21 class base_class : virtual public root
    22 {
    23 private:
    24 int m_a;
    25 int m_b;
    26  
    27 public:
    28 base_class()
    29 {
    30 m_a = 3;
    31 m_b = 4;
    32 }
    33 ~base_class(){};
    34 virtual void v_funr()
    35 {
    36 cout << "This is base_class_A's v_funcr()" << endl;
    37 }
    38 virtual void v_func1()
    39 {
    40 cout << "This is base_class_A's v_func1()" << endl;
    41 }
    42 virtual void v_func2()
    43 {
    44 cout << "This is base_class_A's v_func2()" << endl;
    45 }
    46  
    47 };
    48  
    49 class dev_class :virtual public base_class
    50 {
    51 private: 
    52 int m_a;
    53 int m_b;
    54 int m_c;
    55 public:
    56 dev_class();
    57 ~dev_class(){};
    58 virtual void v_funr()
    59 {
    60 cout << "This is dev_class's v_funcr()" << endl;
    61 }
    62 virtual void v_func1()
    63 {
    64 cout << "This is dev_class`s v_func1" << endl;
    65 }
    66 virtual void v_func3()
    67 {
    68 cout << "This is dev_class`s v_func3" << endl;
    69 }
    70 virtual void v_fun5()
    71 {
    72 cout << "This is dev_class`s v_func5" << endl;
    73 }
    74  
    75 };
    76 dev_class :: dev_class():m_a(1),m_b(2),m_c(3)
    77 {
    78  
    79 }

    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)

  • 相关阅读:
    dotnet 新项目格式与对应框架预定义的宏
    dotnet 线程静态字段
    dotnet 线程静态字段
    dotnet 通过 WMI 拿到显卡信息
    dotnet 通过 WMI 拿到显卡信息
    dotnet 通过 WMI 获取指定进程的输入命令行
    dotnet 通过 WMI 获取指定进程的输入命令行
    dotnet 通过 WMI 获取系统信息
    dotnet 通过 WMI 获取系统信息
    PHP show_source() 函数
  • 原文地址:https://www.cnblogs.com/nothx/p/8537334.html
Copyright © 2011-2022 走看看