C++反彙編-菱形繼承

學無止盡,積土成山,積水成淵-《C++反彙編與逆向分析技術揭祕》 讀書筆記。立刻就要出差了,回來後接着寫吧。

1、概述

  菱形繼承是最複雜的對象結構,菱形結構會將單一繼承與多重繼承進行組合。菱形繼承示意以下:
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的構造函數。函數

具體過程以下:ui

①「構造標記」置1,而後調用D:D();this

②在D:D()內,調用A:A();spa

③在D:D()內,「構造標記」置0,而後調用B:B();scala

D:D()內,「構造標記」置0,而後調用C:C();指針

3.析構函數

  在對象D的構造函數內只會調用一次祖父類A的析構函數。

2、源碼(書中第十二章 菱形繼承源碼)

// 定義傢俱類,等同於類A  
class CFurniture{  
public:  
    CFurniture(){  
        m_nPrice = 0;  
    }  
    virtual ~CFurniture(){              // 傢俱類的虛析構函數  
    printf("virtual ~CFurniture()\r\n");  
    }  
    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()\r\n");  
    }  
    virtual int GetColor(){         // 獲取沙發顏色  
        return m_nColor;  
    }  
    virtual int SitDown(){              // 沙發能夠坐下休息  
        return printf("Sit down and rest your legs\r\n");  
    }  
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()\r\n");  
    }  
    virtual int GetArea(){                  // 獲取牀面積  
        return m_nLength * m_nWidth;  
    }  
    virtual int Sleep(){                    // 牀能夠用來睡覺  
        return printf("go to sleep\r\n");  
    }  
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()\r\n");  
  }  
    virtual int SitDown(){                  // 沙發能夠坐下休息  
        return printf("Sit down on the sofa bed\r\n");  
    }  
    virtual int Sleep(){                    // 牀能夠用來睡覺  
        return printf("go to sleep on the sofa bed\r\n");  
    }  
  virtual int GetHeight(){  
    return m_nHeight;  
  }  
protected:  
  int m_nHeight;                        // 沙發類的成員變量  
};  
void main(int argc, char* argv[]){  
    CSofaBed  SofaBed;  
} 

 

3、彙編(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.析構函數

rest

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)
相關文章
相關標籤/搜索