C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址

讲解之前,了解下什么是虚函数,什么是虚表指针,了解下语法,(也算复习了)

开发知识为了不码字了,找了一篇介绍比较好的,这里我扣过来了,当然也可以看原博客链接:  http://blog.csdn.net/hackbuteer1/article/details/7558868

一丶虚函数讲解(复习开发,熟悉内存模型)

1.复习开发知识

首先:强调一个概念
定义一个函数为虚函数,不代表函数为不被实现的函数。

定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。

定义一个函数为纯虚函数,才代表函数没有被实现。

定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。

1、简介
假设我们有下面的类层次:

class A
{
public:
virtual void foo()
{
cout<<"A::foo() is called"<<endl;
}
};
class B:public A
{
public:
void foo()
{
cout<<"B::foo() is called"<<endl;
}
};
int main(void)
{
A *a = new B();
a->foo(); // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!
return 0;
}

这个例子是虚函数的一个典型应用,通过这个例子,也许你就对虚函数有了一些概念。它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。
虚函数只能借助于指针或者引用来达到多态的效果。

如果看明白上面的开发知识,则我们可以从内存角度看一下虚函数是怎么样存在的.

2.从内存角度看虚函数

首先我们学习C++的时候,自学或者老师教学的时候,都有谈过一个虚表指针的概念.

那我们要知道什么是虚表指针.

 2.1

不带虚表指针的高级代码:

  

class MyTest
{
public:
MyTest();
~MyTest();
void ShowHelloWorld();
int m_Number;
}; MyTest::MyTest()
{
printf("MyTest::MyTest()\r\n");
} MyTest::~MyTest()
{
printf("MyTest::~MyTest()\r\n");
} void MyTest::ShowHelloWorld()
{
printf("Hello World!\n");
} //类声明在上 int main(int argc, char* argv[])
{
MyTest obj; //构造obj对象
obj.ShowHelloWorld();//调用成员函数
obj.m_Number = ; //成员变量赋值
return ;
}

首先看上图高级代码,为什么我们说它没有虚表指针.我们调试查看.

首先经过我们调试

1.obj在监视窗口中只有一个成员变量,且初始化为CCCCC (Debug下)

2.看对象的所在的地址中,发现只申请了4个字节空间,用来存放成员变量.

2.2带虚表指针的高级代码

高级代码还是其高级代码,唯一不同的则是在类中给成员函数加了一个关键字, virtual,让此成员函数变为一个虚函数.

内存模型:

我们发现加了之后会额外多出4个字节空间,而且监视窗口中加了一项虚表指针变量.

构造一下继续观看内存模型.

构造之后发现已经初始化了虚表指针,那么我们进去这个地址后查看有什么内容.

其内容是一个函数指针表,里面存放了虚函数的地址.不相信的话我们打开反汇编窗口,跟进去则可以看到.

总结:

  1.没有虚表指针

    1.1没有虚函数的情况下没有虚表指针

  2.有虚表指针

    2.1虚表指针的产生是看你有没有 virtual这个关键字

    2.2虚表指针存储的是虚表的首地址,虚表可以看做是一个数组

    2.3虚表中存储的是虚函数的地址.

二丶熟悉反汇编中虚表指针,以及还原

既然上面我们熟悉了内存模型,也熟悉了虚函数的原理,那么我们从反汇编的角度下看一下.

例子是我们加了虚函数的例子

Debug下的反汇编

在我们构造的时候,会填写虚表指针,然后核心代码就在上面

1.将对象存入一个局部变量保存

2.局部变量中转给eax

3.对eax取内容填写虚表地址.

总结就是一句话:  取出对象的首4个字节,填写虚表.

那么现在好办了,既然找到了虚表,则可以找到构造,析构,以及虚表中存储的所有虚函数了.

PS: 此图和上图的反汇编一样,只不过高版本的图表没法看,所以用低版本,低版本可以打开.

对其位置下一个引用图表,谁引用了我,则可以看到调用它的所有构造以及析构了,

1.构造的时候会填写虚表

2.析构的时候会填写虚表

图表:

  

可以看出分别有个构造和析构.那个是构造那个是析构,我们需要跟过去看一下.

根据以前所讲的认识构造和析构的方法,可以很简单的判别出来.

识别虚函数

  既然我们找到了虚表指针,则可以双击过去,可以找到虚函数了.

有一个虚函数,确实只有一个,我们跳转过去看看是不是我们定义的

Debug下有跳转表

里面的跳转则是我们的虚函数

总结:

  1.识别虚表指针可以在构造中或者析构中查看

  2.虚表指针双击过去则可以看到所有的虚函数的地址

  3.对虚表指针来个引用,(谁引用我)可以看到所有的构造和析构

三丶识别虚函数的调用

熟悉了虚表指针, 通过虚表指针找构造,析构,以及虚表指针指向的虚表找虚函数,那么我们看一下普通成员函数调用和虚函数调用有什么区别.

PS:类声明不截图,其普通成员函数不加关键字 virtual,其虚函数加virtual

高级代码:

  

int main(int argc, char* argv[])
{
MyTest test;
MyTest &obj = test; //可以虚调用
obj.print(); //普通成员函数
obj.ShowHelloWorld(); //虚函数调用
return ;
}

Debug下的反汇编观察.

认真观察可以看出

  1.普通成员函数调用,直接Call

  2.虚函数调用

    2.1 首先获得虚表指针

    2.2 间接调用虚表指针指向的虚表的内容(虚成员函数地址)

总结:

  识别调用普通成员函数和虚函数的特征则是

  1.普通成员函数直接调用Call

  2.虚函数会通过虚表指针指向的虚表来间接调用.

  

C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址的更多相关文章

  1. C++反汇编第二讲,反汇编中识别虚表指针,以及指向的虚函数地址

    讲解之前,了解下什么是虚函数,什么是虚表指针,了解下语法,(也算复习了) 开发知识为了不码字了,找了一篇介绍比较好的,这里我扣过来了,当然也可以看原博客链接: http://blog.csdn.net ...

  2. C++反汇编第三讲,反汇编中识别继承关系,父类,子类,成员对象

    讲解目录: 1.各类在内存中的表现形式   备注: 主要复习开发知识,和反汇编没有关系,但是是理解反汇编的前提.     2.子类继承父类 2.1 子类中有虚函数,父类中有虚函数 : 都有的情况下   ...

  3. C++函数中那些不可以被声明为虚函数的函数

    转自C++函数中那些不可以被声明为虚函数的函数 常见的不不能声明为虚函数的有:普通函数(非成员函数):静态成员函数:内联成员函数:构造函数:友元函数. 1.为什么C++不支持普通函数为虚函数? 普通函 ...

  4. C++中的动态类型与动态绑定、虚函数、运行时多态的实现

    动态类型与静态类型 静态类型 是指不需要考虑表达式的执行期语义,仅分析程序文本而决定的表达式类型.静态类型仅依赖于包含表达式的程序文本的形式,而在程序运行时不会改变.通俗的讲,就是上下文无关,在编译时 ...

  5. C++中继承 声明基类析构函数为虚函数作用,单继承和多继承关系的内存分布

    1,基类析构函数不为虚函数 #include "pch.h" #include <iostream> class CBase { public: CBase() { m ...

  6. C++中为什么要将析构函数定义成虚函数

    构造函数不可以是虚函数的,这个很显然,毕竟虚函数都对应一个虚函数表,虚函数表是存在对象内存空间的,如果构造函数是虚的,就需要一个虚函数表来调用,但是类还没实例化没有内存空间就没有虚函数表,这根本就是个 ...

  7. 《C++反汇编与逆向分析技术揭秘》之11——虚函数

    虚函数的机制 当类中定义有虚函数时,编译器会将该类中所有虚函数的首地址保存在一张地址表中,这张表被称为虚函数地址表.编译器还会在类中添加一个虚表指针. 举例: CVirtual类的构造函数中没有进行任 ...

  8. C++中虚函数实现原理揭秘

            编译器到底做了什么实现的虚函数的晚绑定呢?我们来探个究竟.      编译器对每个包含虚函数的类创建一个表(称为V TA B L E).在V TA B L E中,编译器放置特定类的虚函 ...

  9. C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象

    C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象 讲解目录: 1.各类在内存中的表现形式   备注: 主要复习开发知识,和反汇编没有关系,但是是理解反汇编的前提.     2.子类继承父 ...

随机推荐

  1. 求最小生成树——Kruskal算法

    给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法.这篇文章先介绍Kruskal算法. Kruskal算法的基本思想:先将所有边按权值从小到大排序,然后按顺 ...

  2. LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal (用中序和后序树遍历来建立二叉树)

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  3. Maximum Clique

    Maximum Clique Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) To ...

  4. 交换知识 VLAN VTP STP 单臂路由

    第1章 交换基础 1.1 园区网分层结构 层次 作用 出口层 广域网接入 出口策略 带宽控制 核心层 高速转发 服务器接入 路由选择 汇聚层 流量汇聚 链路冗余 设备冗余 路由选择 接入层 用户接入 ...

  5. Hive中常用的查询命令

    日志数据的统计处理在这里反倒没有什么特别之处,就是一些 SQL 语句而已,也没有什么高深的技巧,不过还是列举一些语句示例,以示 hive 处理数据的方便之处,并展示 hive 的一些用法. a)    ...

  6. css 3d

    Perspective  透视点,视角,CSS3D 的透视点在浏览器前方 默认值为none,是作用于子元素,指定观察者与z=0平面的距离,使具有三维位置变换的元素产生透视效果.z>0的三维元素比 ...

  7. vsftpd安装及虚拟用户配置

    服务器环境:CentOS6.9 Linux 2.6.32-696.10.1.el6.x86_64 安装vsftpd.db4.db4-utils # yum -y install vsftpd db4 ...

  8. 68、django之session与分页

    前面我们介绍了cookies,主要应用在用户登录上,保存用户登录状态,不过cookies直接放在了浏览器上,安全性较低,所以我们便引出了session功能与cookies相同,不同的是它放在了客户端, ...

  9. SEO内链操作的技巧大家又知道多少

    关于一个优质的网站而言,一套优质的内部优化链接体系是必不可少的,简略来说杰出的内链体系能让网站页面之前彼此传递权重,协助搜索引擎快速辨认网站内容的中心,可是SEO内链操作的技巧我们又知道多少?下面齐鲁 ...

  10. C# 自定义样式实现菜单和工具栏的分割线

    在做WinForm界面布局时,菜单和工具栏必不可少!但是MenuStrip和ToolStrip不能够对边框的样式直接设置,如果想实现菜单和工具栏之间的分割线就不容易实现:今天查阅了一下msdn找到了一 ...