学无止尽,积土成山,积水成渊-《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 = ;
}
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 = ;
m_nColor = ;
}
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 = ;
m_nLength = ;
m_nWidth = ;
}
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 = ;
}
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;
}

三、汇编(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 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 CBed.m_nLength
ebp-10h 002BF968 CBed.m_nWidth
ebp-0Ch 002BF96C CSofaBed.m_nHeight
ebp-08h 002BF970 00CD4834 pvftable3---->const CSofaBed::`vftable'{for `CFurniture'}
ebp-04h 002BF974 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 ;第一个虚基表(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 ;第二个虚表(pvftable2):
;const CSofaBed::`vftable'{for `CBed'} ;基类CBed的虚表
00CD4844 00CD10FA CBed::GetArea(void)
00CD4848 00CD1091 CSofaBed::Sleep(void)
00CD484C ;第二个虚基表(vbtable2):
;const CSofaBed::`vbtable'{for `CBed'}
00CD4864 FFFFFFFC ;vbtable2[1]=-4, address[pvbtable2]+vbtable2[1]=address[pvftable2]
00CD4868 ;vbtable2[2]=16, address[pvbtable2]+vbtable2[2]=address[pvftable3]
00CD486C ;第三个虚表(pvftable3):
;const CSofaBed::`vftable'{for `CFurniture'} ;虚基类CFurniture的虚表
00CD4834 00CD1028 CSofaBed::`scalar deleting destructor'(uint)
00CD4838 00CD1145 CFurniture::GetPrice(void)
00CD483C 00000000

2.构造函数

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

C++反汇编-菱形继承的更多相关文章

  1. C++反汇编第五讲,认识多重继承,菱形继承的内存结构,以及反汇编中的表现形式.

    C++反汇编第五讲,认识多重继承,菱形继承的内存结构,以及反汇编中的表现形式. 目录: 1.多重继承在内存中的表现形式 多重继承在汇编中的表现形式 2.菱形继承 普通的菱形继承 虚继承 汇编中的表现形 ...

  2. C++反汇编第四讲,认识多重继承,菱形继承的内存结构,以及反汇编中的表现形式.

    目录: 1.多重继承在内存中的表现形式 多重继承在汇编中的表现形式 2.菱形继承 普通的菱形继承 虚继承 汇编中的表现形式 一丶多重继承在内存中的表现形式 高级代码: class Father1 { ...

  3. C++逆向分析----多重继承和菱形继承

    多重继承 多重继承是指C++类同时继承两个类或两个以上的类. class Test { public: int num1; Test() { num1 = 1; } virtual void Proc ...

  4. inheritance,菱形继承, 虚继承,virtual

    //菱形继承   |||||||   虚继承 #include <iostream> using namespace std; class R {     int r; public:   ...

  5. C++中的类继承(4)继承种类之单继承&多继承&菱形继承

    单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或以上直接父类时称这个继承关系为多继承.这种继承方式使一个子类可 ...

  6. python 菱形继承问题究极版

    如果只是正常的菱形继承,经典类(python2中最后一个父类不继承object类)是深度优先,即会从左边父类开始一路走到底 新式类(最后一个父类继承了object类)是广度优先,即从左边父类开始继承, ...

  7. c++之菱形继承问题

    昨天面试问了菱形继承的问题,回答的稀巴烂,回来赶快好好学习一波!!!!! 菱形继承如下图: 上一段代码: #include<bits/stdc++.h> using namespace s ...

  8. C++中的类继承之单继承&多继承&菱形继承

     C++中的类继承之单继承&多继承&菱形继承 单继承是一般的单一继承,一个子类只 有一个直接父类时称这个继承关系为单继承.这种关系比较简单是一对一的关系: 多继承是指 一个子类有两个或 ...

  9. C++学习之路(九):从菱形继承引入的对象模型

    一.单继承 class A {int a;}; class B : public A {int b;}; 普通的单继承关系,类的大小是由其虚表指针和非静态成员函数大小决定.故上述sizeof(A)的大 ...

随机推荐

  1. html-表格和列表

    一:表格标签 表格 描述 <table> 定义表格 <caption> 定义表格标题. <th> 定义表格的表头. <tr> 定义表格的行. <t ...

  2. IPv4的核心管理功能/proc/sys/net/ipv4/*

    I /proc/sys/net/ipv4/tcp_syncookies SYN Cookies模块可以在系统随机端口(1024:65535)即将用完时自动启动,用来应对Dos攻击.当启动SYN Coo ...

  3. LCT解读(1)

    蒟蒻的LCT解读(1) 前段时间本蒟蒻自学了一下LCT,但是网上的很多资料并不很全,而且作为一个数组选手,我看指针代码真的很麻烦,所以就在这里写一篇数组选手能看懂的代码. LCT的初步了解 LCT全称 ...

  4. Git简明教程二、开始进行版本管理

    上一篇介绍了Git中的一些基本概念.本篇来实际看一看如何通过几个常用命令来快速上手Git,完成版本管理的日常操作(核心操作). 0. 准备工作 安装Git后,请先在你的电脑上新建或选择一个目录作为测试 ...

  5. day1作业:编写登录窗口一个文件实现

    思路: 1.参考模型,这个作业我参考了linux的登录认证流程以及结合网上银行支付宝等锁定规则: 1)认证流程参考的是Linux的登录:当你输入完用户名密码后再验证用户名是否存在用户是否被锁定,然后在 ...

  6. 【LOJ】#2059. 「TJOI / HEOI2016」字符串

    题解 我们冷静一下,先画一棵后缀树 然后发现我们要给c和d这一段区间在[a,b]这一段开头的串里找lcp 而lcp呢,就是c点的祖先的到根的一段,假如这个祖先的子树里有[a,b - dis[u] + ...

  7. 【LOJ】#2038. 「SHOI2015」超能粒子炮・改

    题解 用lucas随便分析一波就出来了 \(\binom{n}{k} = \binom{n % p}{k % p}\binom{n / p}{k / p}\) 那么对于一个余数r,如果r <= ...

  8. 【LOJ】#2445. 「NOI2011」道路修建

    题解 看完题目我的第一个反应是--要求最小花费的方案?!怎么求??? 然后我把题读完了.好吧. 记录一下size就行,比NOIP普及组还要不如的题= = 代码 #include <iostrea ...

  9. HDU 1028 HDU 1398 (母函数)

    题意:输入一个n  给出其所有组合数 如: 4 = 4;  4 = 3 + 1;  4 = 2 + 2;  4 = 2 + 1 + 1;  4 = 1 + 1 + 1 + 1; 重复不算 母函数入门题 ...

  10. Python爬虫个人记录(四)利用Python在豆瓣上写一篇日记

    涉及关键词:requests库 requests.post方法 cookies登陆 version 1.5(附录):使用post方法登陆豆瓣,成功! 缺点:无法获得登陆成功后的cookie,要使用js ...