zoukankan      html  css  js  c++  java
  • C++反汇编-菱形继承

    学无止尽,积土成山,积水成渊-《C++反汇编与逆向分析技术揭秘》 读书笔记。马上就要出差了,回来后接着写吧。

    一、概述

      菱形继承是最复杂的对象结构,菱形结构会将单一继承与多重继承进行组合。菱形继承示意如下:
    class A;
    class B : virtual public A;
    class C : virtual public A;
    class D : public B, public C;
     
      其中菱形继承中使用了虚继承机制。虚继承定义为:在继承定义中包含了virtual关键字的继承关系。虚继承的提出就是为了解决多重继承。如果不采用虚继承,则D类对象会保存基类A的两份副本,从导致D类对象使用基类A成员变量或或成员函数时,出现二义性。但如果采用虚继承时,则D类对象则就只保留了基类A的一份副本,但编译器是如何实现这个机制呢?
      先从简单的单类继承讲起,如派生类B虚继承基类A:编译器会在派生类B的对象中保存一个基类A的副本,然后在类B的对象中添加一个变量,该变量是关于类A的数据在类B的对象中的偏移量(offset)。实际上类B的对象中并不直接保存偏移量(offset) 的值,而是保存一个指针(pvbtable),该指针指向一个vbtable表(virtual base table,虚基类表),vbtable表中的由有两项组成:
    • 第一项 vbtable[1]:所属类(即派生类B)对应的虚表指针相对于 pvbtable 的偏移值;
    • 第二项 vbtable[2]: 虚基类(即基类A)的虚表指针相对于 pvbtable 的偏移值。
      所有类B的对象共享该表,因此每个类B的对象均有一个单独的指针(pvbtable)指向该表。同理可知,类C的对象中也同样有个指针变量(pvbtable),指向一个vbtable表。
     

    1. 派生类D的对象内存排列:

    • 类B的虚表指针(pvftabe_B)
    • 类B的虚基类表的指针(pvbtable_B)
    • 类B的成员数据
    • 类C的虚表指针 (pvftabe_C)
    • 类C的虚基类表的指针(pvbtable_C)
    • 类C的成员数据
    • 类D的成员数据
    • 类A的虚表指针(pvftabe_A)
    • 类A的成员数据
      显然在类D的对象中只存在基类A数据的一份副本,而且具有以下等式
      vbtable_B[2] + address[pvbtable_B] = vbtable_C[2] + address[pvbtable_C] = address[pvftabe_A]。

    2.构造函数

     在对象D的构造函数内只会调用一次祖父类A的构造函数,编译器在调用类B、C、D的构造函数时,增添一个名为“构造标记”的参数。例如在调用B:B()构造函数时,“构造标记”=0时,则不调用用父类A的构造函数;“构造标记”=1时,则调用父类A的构造函数。在调用D:D()构造函数时,“构造标记”=0时,则不调用用祖父类A的构造函数;“构造标记”=1时,则调用祖父类A的构造函数。

    具体过程如下:

    ①“构造标记”置1,然后调用D:D();

    ②在D:D()内,调用A:A();

    ③在D:D()内,“构造标记”置0,然后调用B:B();

    D:D()内,“构造标记”置0,然后调用C:C();

    3.析构函数

     在对象D的构造函数内只会调用一次祖父类A的析构函数。

    二、源码(书中第十二章 菱形继承源码)

    // 定义家具类,等同于类A  
    class CFurniture{  
    public:  
        CFurniture(){  
            m_nPrice = 0;  
        }  
        virtual ~CFurniture(){              // 家具类的虚析构函数  
        printf("virtual ~CFurniture()
    ");  
        }  
        virtual int GetPrice(){         // 获取家具价格  
            return m_nPrice;  
        };  
    protected:  
        int m_nPrice;                   // 家具类的成员变量  
    };  
     
    // 定义沙发类,继承自类CFurniture,等同于类B  
    class CSofa : virtual public CFurniture{  
    public:  
        CSofa(){  
            m_nPrice = 1;  
            m_nColor = 2;  
        }  
        virtual ~CSofa(){               // 沙发类虚析构函数  
            printf("virtual ~CSofa()
    ");  
        }  
        virtual int GetColor(){         // 获取沙发颜色  
            return m_nColor;  
        }  
        virtual int SitDown(){              // 沙发可以坐下休息  
            return printf("Sit down and rest your legs
    ");  
        }  
    protected:  
        int m_nColor;                   // 沙发类成员变量  
    };  
     
    // 定义床类,继承自类CFurniture,等同于类C  
    class CBed : virtual public CFurniture{  
    public:  
        CBed(){  
            m_nPrice = 3;  
            m_nLength = 4;  
            m_nWidth = 5;  
        }  
        virtual ~CBed(){                // 床类的虚析构函数  
            printf("virtual ~CBed()
    ");  
        }  
        virtual int GetArea(){                  // 获取床面积  
            return m_nLength * m_nWidth;  
        }  
        virtual int Sleep(){                    // 床可以用来睡觉  
            return printf("go to sleep
    ");  
        }  
    protected:  
        int m_nLength;                      // 床类成员变量  
        int m_nWidth;  
    };  
     
    // 子类沙发床的定义,派生自类CSofa和类CBed,等同于类D  
    class CSofaBed :  public CSofa,  public CBed{  
    public:  
        CSofaBed(){  
        m_nHeight = 6;  
      }  
        virtual ~CSofaBed(){                    // 沙发床类的虚析构函数  
        printf("virtual ~CSofaBed()
    ");  
      }  
        virtual int SitDown(){                  // 沙发可以坐下休息  
            return printf("Sit down on the sofa bed
    ");  
        }  
        virtual int Sleep(){                    // 床可以用来睡觉  
            return printf("go to sleep on the sofa bed
    ");  
        }  
      virtual int GetHeight(){  
        return m_nHeight;  
      }  
    protected:  
      int m_nHeight;                        // 沙发类的成员变量  
    };  
    void main(int argc, char* argv[]){  
        CSofaBed  SofaBed;  
    } 

    三、汇编(VS2010编译)

    1.内存排列

    ;偏移        地址      值           值解析                                                    
    ebp-28h    002BF950  00CD4854  pvftable1---->const CSofaBed::`vftable'{for `CSofa'}       
    ebp-24h    002BF954  00CD4870  pvbtable1---->const CSofaBed::`vbtable'{for `CSofa'}         
    ebp-20h    002BF958  00000002  CSofa.m_nColor                                      
    ebp-1Ch    002BF95C  00CD4844  pvftable2---->const CSofaBed::`vftable'{for `CBed'}     
    ebp-18h    002BF960  00CD4864  pvbtable2---->const CSofaBed::`vbtable'{for `CBed'}     
    ebp-14h    002BF964  00000004  CBed.m_nLength                                        
    ebp-10h    002BF968  00000005  CBed.m_nWidth                                       
    ebp-0Ch    002BF96C  00000006  CSofaBed.m_nHeight                             
    ebp-08h    002BF970  00CD4834  pvftable3---->const CSofaBed::`vftable'{for `CFurniture'}    
    ebp-04h    002BF974  00000003  CFurniture.m_nPrice                                         
                                                                                   
    
    ;第一个虚表(pvftable1):
    ;const CSofaBed::`vftable'{for `CSofa'}                ;基类CSofa的虚表
    00CD4854  00CD10B4  CSofa::GetColor(void)
    00CD4858  00CD1005  CSofaBed::SitDown(void)
    00CD485C  00CD1127  CSofaBed::GetHeight(void)
    00CD4860  00000000
    
    ;第一个虚基表(vbtable1):
    ;const CSofaBed::`vbtable'{for `CSofa'}
    00CD4870  FFFFFFFC ;vbtable1[1]=-4, address[pvbtable1]+vbtable1[1]=address[pvftable1]
    00CD4874  0000001C ;vbtable1[2]=28, address[pvbtable1]+vbtable1[2]=address[pvftable3]
    00CD4878  00000000
    
    
    ;第二个虚表(pvftable2):
    ;const CSofaBed::`vftable'{for `CBed'}                ;基类CBed的虚表
    00CD4844  00CD10FA  CBed::GetArea(void)
    00CD4848  00CD1091  CSofaBed::Sleep(void)
    00CD484C  00000000
    
    ;第二个虚基表(vbtable2):
    ;const CSofaBed::`vbtable'{for `CBed'}                
    00CD4864  FFFFFFFC ;vbtable2[1]=-4, address[pvbtable2]+vbtable2[1]=address[pvftable2]
    00CD4868  00000010 ;vbtable2[2]=16, address[pvbtable2]+vbtable2[2]=address[pvftable3]
    00CD486C  00000000
    
    
    ;第三个虚表(pvftable3):
    ;const CSofaBed::`vftable'{for `CFurniture'}            ;虚基类CFurniture的虚表
    00CD4834  00CD1028  CSofaBed::`scalar deleting destructor'(uint)
    00CD4838  00CD1145  CFurniture::GetPrice(void)
    00CD483C  00000000

    2.构造函数

    mov     [ebp-4], ecx               ;临时保存this指针到[ebp-4]
    mov     dword ptr [ebp-48h], 0     ;构造标记置0
    cmp     dword ptr [ebp+8], 0    
    ;函数参数[ebp+8]==0,为0时不需要调用祖父类的构造函数;为1时,需要调用祖父类的构造函数。
    jz      short loc_4114BC
    
    mov     eax, [ebp-4]                ;eax=this
    ;初始化为[eax+4]=[this+4]=pvbtable1,对pvbtable1初始化为const CSofaBed::`vbtable'{for `CSofa'}
    mov     dword ptr [eax+4], offset ??_8CSofaBed@@7BCSofa@@@ 
    mov     eax, [ebp-4]                ;eax=this
    ;初始化为[eax+10h]=[this+10h]=pvbtable2,对pvbtable2初始化为const CSofaBed::`vbtable'{for `CBed'}
    mov     dword ptr [eax+10h], offset ??_8CSofaBed@@7BCBed@@@ 
    mov     ecx, [ebp-4]                ;ecx=this
    add     ecx, 20h                    ;ecx+20h=this+20h----->pvftable3
    call    j_??0CFurniture@@QAE@XZ     ;调用祖父类构造函数CFurniture::CFurniture(void)
    or      dword ptr [ebp-48h], 1      ;构造标记置1
    
    loc_4114BC:
    push    0                           ;压入参数0,作为构造标记,跳过虚基类CFurniture的构造函数
    mov     ecx, [ebp-4]                ;ecx=this
    call    j_??0CSofa@@QAE@XZ          ;调用父类构造函数CSofa::CSofa(void)
    push    0                           ;压入参数0,作为构造标记,跳过虚基类CFurniture的构造函数
    mov     ecx, [ebp-4]                ;ecx=this
    add     ecx, 0Ch                    ;ecx=this+0ch----->pvftable2
    call    j_??0CBed@@QAE@XZ           ;调用父类构造函数CBed::CBed(void)
    mov     eax, [ebp-4]                ;ecx=this
    ;[eax]=[this]----->pvftable1,初始化为const CSofaBed::`vftable'{for `CSofa'}
    mov     dword ptr [eax], offset ??_7CSofaBed@@6BCSofa@@@ 
    mov     eax, [ebp-4]                ;ecx=this
    ;ecx=this+0ch----->pvftable2,初始化为 const CSofaBed::`vftable'{for `CBed'}
    mov     dword ptr [eax+0Ch], offset ??_7CSofaBed@@6BCBed@@@ 
    mov     eax, [ebp-4]                ;eax=this
    mov     ecx, [eax+4]                ;ecx=[this+4]=pvbtable1
    mov     edx, [ecx+4]                ;edx=[pvbtable1+4]=vbtable1[2]
    mov     eax, [ebp-4]                ;eax=this=address[pvftable1]
    ;[eax+edx+4]=[this+vbtable1[2]]----->pvftable3,初始化为const CSofaBed::`vftable'{for `CFurniture'}
    mov     dword ptr [eax+edx+4], offset ??_7CSofaBed@@6BCFurniture@@@
    mov     eax, [ebp-4]                ;eax=this
    mov     dword ptr [eax+1Ch], 6      ;[eax+1ch]=[this+1ch]----->CSofaBed.m_nHeight,初始化为6

    3.析构函数

    4.函数调用

    CFurniture * pFurniture = &SofaBed; 
    mov     ecx, [ebp-24h]        ;参照栈表可知,ecx=[ebp-24h]=[this+4]=pvbtable1,指向第一个虚基表
    mov     edx, [ecx+4]          ;edx=[pvbtable1+4]=vbtable2[2]
    lea     eax, [ebp+edx-24h]    
    ;pvbtable1+vbtable2[2]=address[pvftable3],即eax=address[pvftable3]=this+20h。 mov [ebp-78h], eax mov ecx, [ebp-78h] mov [ebp-2Ch], ecx ;指针变量pFurniture在[ebp-2Ch]处,即pFurniture=this+20h ;printf("price is %d", pFurniture->GetPrice()); mov eax, [ebp-2Ch] ;把pFurniture赋值给eax,即eax=this+20h mov edx, [eax] ;edx=[this+20h]=pvftable3 mov ecx, [ebp-2Ch] ;ecx传参,ecx=this+20h----->pvftable3 mov eax, [edx+4]
    ;eax=[pvftable3+4]=vftable3[2]----->CFurniture::GetPrice(void) call eax ;调用CFurniture::GetPrice(void) push eax ;CFurniture::GetPrice(void)返回的结果入栈 push offset Format ;"price is %d" call ds:__imp__printf add esp, 8 ;CSofa * pSofa = &SofaBed; lea eax, [ebp-28h] ;参照栈表可知,eax=address[ebp-28h]=this mov [ebp-30h], eax ;指针变量pSofa在栈[ebp-30h]处,完成pSofa的赋值,即pSofa=this ;pSofa->SitDown(); mov eax, [ebp-30h] ;eax=pSofa=this mov edx, [eax] ;edx=[this]=pvftable1 mov ecx, [ebp-30h] ;ecx传参,ecx=pSofa=this mov eax, [edx+4]
    ;eax=[pvftable1+4]=vftable1[2]----->CSofaBed::SitDown(void) call eax ;调用CSofaBed::SitDown(void) CBed * pBed = &SofaBed; lea ecx, [ebp-28h] ;参照栈表可知,ecx=address[ebp-28h]=this add ecx, 0Ch ;ecx=this+0ch----->pvftable2 mov [ebp-78h], ecx ; jmp short loc_41142E ;---| ;| loc_41142E:;<----------------| mov edx, [ebp-78h] mov [ebp-34h], edx
    ;指针变量pBed在栈[ebp-34h]处,完成pBed的赋值,即pBed=this+0ch----->pvftable2 pBed->Sleep(); mov eax, [ebp-34h] ;eax=this+0ch mov edx, [eax] ;edx=[this+0ch]=pvftable2 mov ecx, [ebp-34h] ;ecx传参,ecx=this+0ch----->pvftable2 mov eax, [edx+4]
    ;eax=[pvftable2+4]=vftable2[2]----->CSofaBed::Sleep(void) call eax ;调用CSofaBed::Sleep(void)
  • 相关阅读:
    docker从容器中怎么访问宿主机
    fail2ban CentOS7安装
    关于sql当中的isnull和ifnull的区别
    关于mybatis反向生成的时候,对于数据库当中的数据类型是text的处理。
    关于Java当中的MapUtils工具类的使用和注意事项。
    在大数据管理平台HDP的ambria的时候遇到python的安全认证的问题
    通过java代码实现调用excel当中的宏的操作。
    通过vba实现对word当中个的标签进行批量的替换
    通过vba实现对word当中的表格动态的插入行,同时对表中进行数据的填充。
    关于vba实现删除word文档中的指定表格。
  • 原文地址:https://www.cnblogs.com/bigrabbit/p/3465778.html
Copyright © 2011-2022 走看看