C++逆アセンブリ-菱形継承
30434 ワード
「C++逆アセンブリと逆分析技術暴露」読書ノート.もうすぐ出張なので、帰ってから書きましょう.
一、概説
菱形継承は最も複雑なオブジェクト構造であり、菱形構造は単一の継承と多重の継承を組み合わせます.菱形継承の概略は次のとおりです.
class A;
class B : virtual public A;
class C : virtual public A;
class D : public B, public C;
ここで菱形継承には虚継承メカニズムが用いられる.虚継承は、継承定義にvirtualキーワードの継承関係が含まれていると定義されます.虚継承の提案は多重継承を解決するためである.ダミー継承を使用しない場合、DクラスオブジェクトはベースクラスAの2つのコピーを保存します.DクラスオブジェクトがベースクラスAメンバー変数またはメンバー関数を使用する場合、二義性が現れます.しかし、ダミー継承を採用すると、DクラスオブジェクトはベースクラスAのコピーを1部しか保持しませんが、コンパイラはどのようにしてこのメカニズムを実現しますか?
まず、単純な単一クラス継承から、派生クラスBの虚継承ベースクラスAのように説明する.
コンパイラは派生クラスBのオブジェクトにベースクラスAのコピーを保存し、クラスBのオブジェクトに変数を追加します.この変数はクラスAに関するデータのクラスBのオブジェクトにおけるオフセット量(offset)です.実際にクラスBのオブジェクトにはオフセット量(offset)の値を直接保存するのではなく、ポインタ(pvbtable)を保存します.このポインタはvbtableテーブル(virtual base table、ダミーベースクラステーブル)を指し、vbtableテーブルには2つの項目があります.第1項vbtable[1]:属するクラス(すなわち派生クラスB)に対応するダミーテーブルポインタのpvbtableに対するオフセット値; 第2項vbtable[2]:ダミーベースクラス(すなわちベースクラスA)のダミーテーブルポインタのpvbtableに対するオフセット値. すべてのクラスBのオブジェクトがテーブルを共有するため、各クラスBのオブジェクトは、テーブルを指す個別のポインタ(pvbtable)を有する.
同様に,クラスCのオブジェクトにも同様にポインタ変数(pvbtable)があり,1つのvbtableテーブルを指す.
1.派生クラスDのオブジェクトメモリ配列:クラスBのダミーテーブルポインタ(pvftabe_B) クラスBのダミーベースクラステーブルのポインタ(pvbtable_B) クラスBのメンバーデータ クラスCのダミーテーブルポインタ(pvftabe_C) クラスCのダミーベースクラステーブルのポインタ(pvbtable_C) クラスCのメンバデータ クラスDのメンバデータ クラスAのダミーテーブルポインタ(pvftabe_A) クラスAのメンバデータ クラスDのオブジェクトには、ベースクラスAデータのコピーが1部しか存在せず、以下の式があることは明らかである
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のコンストラクタが呼び出される.
二、ソースコード(本の第十二章菱形継承ソースコード)
三、アセンブリ(VS 2010コンパイル)
1.メモリ配列
2.コンストラクタ
3.構造関数
略
4.関数呼び出し
一、概説
菱形継承は最も複雑なオブジェクト構造であり、菱形構造は単一の継承と多重の継承を組み合わせます.菱形継承の概略は次のとおりです.
class A;
class B : virtual public A;
class C : virtual public A;
class D : public B, public C;
ここで菱形継承には虚継承メカニズムが用いられる.虚継承は、継承定義にvirtualキーワードの継承関係が含まれていると定義されます.虚継承の提案は多重継承を解決するためである.ダミー継承を使用しない場合、DクラスオブジェクトはベースクラスAの2つのコピーを保存します.DクラスオブジェクトがベースクラスAメンバー変数またはメンバー関数を使用する場合、二義性が現れます.しかし、ダミー継承を採用すると、DクラスオブジェクトはベースクラスAのコピーを1部しか保持しませんが、コンパイラはどのようにしてこのメカニズムを実現しますか?
まず、単純な単一クラス継承から、派生クラスBの虚継承ベースクラスAのように説明する.
コンパイラは派生クラスBのオブジェクトにベースクラスAのコピーを保存し、クラスBのオブジェクトに変数を追加します.この変数はクラスAに関するデータのクラスBのオブジェクトにおけるオフセット量(offset)です.実際にクラスBのオブジェクトにはオフセット量(offset)の値を直接保存するのではなく、ポインタ(pvbtable)を保存します.このポインタはvbtableテーブル(virtual base table、ダミーベースクラステーブル)を指し、vbtableテーブルには2つの項目があります.
同様に,クラスCのオブジェクトにも同様にポインタ変数(pvbtable)があり,1つのvbtableテーブルを指す.
1.派生クラスDのオブジェクトメモリ配列:
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()\r
");
}
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
");
}
virtual int GetColor(){ //
return m_nColor;
}
virtual int SitDown(){ //
return printf("Sit down and rest your legs\r
");
}
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
");
}
virtual int GetArea(){ //
return m_nLength * m_nWidth;
}
virtual int Sleep(){ //
return printf("go to sleep\r
");
}
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
");
}
virtual int SitDown(){ //
return printf("Sit down on the sofa bed\r
");
}
virtual int Sleep(){ //
return printf("go to sleep on the sofa bed\r
");
}
virtual int GetHeight(){
return m_nHeight;
}
protected:
int m_nHeight; //
};
void main(int argc, char* argv[]){
CSofaBed SofaBed;
}
三、アセンブリ(VS 2010コンパイル)
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)