C++逆向分析--虚函数(多态的前置)

先理解一件事,在intel汇编层面来说,直接调用和间接调用的区别。

直接调用语法: call 地址   硬编码为 :e8

间接调用语法:   call [ ...]     硬编码为:    FF

那么在C++语法中,实现多态的前提是父类需要实现多态的成员方法前面加入virtual。我们先来看一个例子,这次试验用的是windows平台下的VS编译器不同编译器的细节是不一样的原理大差不差。

#include<iostream>

using namespace std;

class Base {
public:
    void func1() {
        printf("这是func1");
    }

    virtual void func2() {
        printf("这是func2");
    }
    

};



int main() {
    Base b1;
    b1.func1();
    b1.func2();



    return 0;
}

  这个代码示例中我们只写了一个类,并且用对象去调用成员方法。运行结果如下:


我们观察下汇编代码:

此时均为e8-call,属于直接调用。接下来我们换一种调用方式,我们通过指针的方式去调用对象方法:

#include<iostream>

using namespace std;

class Base {
public:
    void func1() {
        printf("这是func1");
    }

    virtual void func2() {
        printf("这是func2");
    }
    

};


int main() {

    Base b1;
    Base* p;
    p = &b1;
    p->func1();
    p->func2();


    return 0;
}


运行结果没有变化,但是此时我们观察反汇编我们就发现了不一样的地方:


fun1就是普通的成员函数,反汇编依然是那样。但是我们看加入关键字的virtual变成虚函数后,汇编代码变化了。真正调用的其实是call eax这句汇编代码,我们看到他的硬编码是FF D0。说明他是个间接调用。我们可以理解这段反汇编代码,p是指向对象的首地址的也就是this指针。将this指针的第一项放入eax,又将eax的第一项复制给edx,此时将this指针给ecx(如果是linux平台g++编译this指针是赋值给rdi的 _x64情况下)最后将edx的第一项赋值给eax。最后去执行。这段翻译可能有点绕。可以自己去理解下。

现在我们知道虚函数,如果是通过对象去调用,和普通成员方法没什么区别,但是如果通过指针或者引用去调用。那么他就是个间接调用。一些细节后面再讲。

下面我们再分析一个问题,这个类到底有多大。

#include<iostream>

using namespace std;

class Base {
public:
    int a;
    int b;

    void func1() {
        printf("这是func1");
    }

    void func2() {
        printf("这是func2");
    }
    

};



int main() {
    //Base b1;
    //Base* p;
    //p = &b1;
    //p->func1();
    //p->func2();
    printf("Base结构体大小为=%d", sizeof(Base));

    return 0;
}

这个问题应该很简单,只算数据大小,int类型是占4个字节因此这个Base对象的大小应该是8,因为成员方法是在代码区的,这里其实跟C语言的结构体没什么不一样的:


那如果现在我们将其中一个成员方法改成虚函数呢?


神奇的一幕发生了,变成了12。

那是不是说类中每多一个虚函数,就会多4字节大小呢?(32位而言,64位就是8)

我们将fun1也变成虚函数:


我们看到没有变化。也就是说,跟你在类中定义多少个虚函数木的关系。那这多出来的4字节是个什么鬼东西呢?这就是我们接下来要探究的东西。


我们观察。现在没有任何函数的情况下,我们的类是这样布局的。 0x00cffb80也就是b1对象的首地址。

下面我们加入一个虚函数:


我们观察此时对象的内存布局:


什么都没有,接着往下走:


再往下走:


再往下走:


我们发现对象首地址存的不再是1。而是一个地址。那这个地址是什么呢?这就是我们接下来要探究的东西。写一个demo:

#include<iostream>

using namespace std;

class Base {
public:
    int a;
    int b;

    virtual void func1() {
        printf("这是func1");
    }


};



int main() {
    Base b1;
    b1.a = 1;
    b1.b = 2;

    Base* p;
    p = &b1;
    p->func1();

    return 0;
}

我们跟过去反汇编:



我们现在看这两行汇编的意思就很明朗了。

1.mov eax [p]   //将对象的this指针放入eax

2.mov edx [eax] //将this指针首地址里面存的虚函数表放入edx

3.mov ecx [p]   //将this指针传给ecx

3,mov eax ,[edx] //将虚函数表里的第一项放入eax

4.call eax //调用fun1函数。


相比看懂了上面的流程就明白了。总结图如下:


那我们能验证虚函数表中的函数就是我们的想要调用的函数嘛?demo如下:

#include<iostream>

using namespace std;

class Base {
public:
    int a;
    int b;

    virtual void func1() {
        printf("这是func1\n");
    }


};



int main() {
    Base b1;
    b1.a = 1;
    b1.b = 2;

    Base* p;
    p = &b1;
    p->func1();
    printf("b1对象的地址=%p\n", &b1);
    printf("虚函数表地址=%p\n", *((int*)&b1));
    printf("func1函数地址=%p\n", *((int*)*((int*)&b1)));

    int p2 = *((int*)*((int*)&b1));

    _asm {
        call p2;
    }

    return 0;
}

这里因为我是用32位写的demo因此我们用内联汇编测试下。代码逻辑就是取出虚函数表中的第一项,然后用汇编调用,看是否和我们用指针调用的是同一个函数运行结果如下:


通过这个实验我们确实验证了虚函数表中存的就是我们的虚函数的地址。这也是C++编译器实现多态的一个先提条件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/344018.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入分析 Linux 网络丢包问题

热门IT课程【视频教程】-华为/思科/红帽/oraclehttps://xmws-it.blog.csdn.net/article/details/134398330 所谓丢包&#xff0c;是指在网络数据的收发过程中&#xff0c;由于种种原因&#xff0c;数据包还没传输到应用程序中&#xff0c;就被丢弃了。这些被丢弃包的数量&#…

【分布式技术专题】「分布式技术架构」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)

探索Tomcat技术架构设计模式的奥秘 Tomcat系统架构分析Tomcat 整体结构Tomcat总体结构图以 Service 作为“婚姻”1) Service 接口方法列表 2) StandardService 的类结构图方法列表 3) StandardService. SetContainer4) StandardService. addConnector 以 Server 为“居”1) Ser…

nvm 配置淘宝镜像失效,以及安装node后 npm-v 无效

win11 nvm版本 1.1.4 和1.1.7和1.1.12&#xff08;目前最新版本24年 一月二十三日&#xff09; 以上nvm版本都会出现一下问题&#xff0c; 从https://github.com/coreybutler/nvm-windows/releases 下载nvm安装包如下图 傻瓜式安装后&#xff0c;不用去配置环境变量&#…

冰箱和小型制冷系统毛细管选型计算软件介绍

这是一款比较实用的软件&#xff0c;可以快速根据制冷剂类型、制冷量、蒸发温度、冷凝温度、回气温度大致确定毛细管内径和长度。可以根据不同的内径选择对应的长度&#xff0c;通常内径越小长度也会越短。可以选择的冷媒类型 可以设置个人喜好的单位 对于初学者可以使用该软件…

router4j--SpringCloud动态路由利器

前言 本文介绍Java的动态路由中间件&#xff1a;router4j。router4j用于SpringCloud项目&#xff0c;它可以将某个url请求路由到指定的机器上&#xff0c;也可以将所有请求强制转到指定机器。 问题描述 Java后端在开发SpringCloud项目时如果同一个应用起了多个实例&#xff…

UE5.2、CesiumForUnreal实现加载GeoJson绘制单面

文章目录 前言一、实现目标二、实现过程1.实现原理2.数据读取3.三角剖分3.具体代码 4.蓝图测试 前言 UE5、CesiumForUnreal实现加载GeoJson绘制单面&#xff08;Polygon&#xff09;功能&#xff08;StaticMesh方式&#xff09; 一、实现目标 通过读取本地的Geojson数据&…

6.go 库源码文件

目录 概述总结例子代码结构代码执行结果 结束 概述 库源码文件是不能被直接运行的源码文件&#xff0c;它仅用于存放程序实体&#xff0c;这些程序实体可以被其他代码使用&#xff08;只要遵从 Go 语言规范的话&#xff09; 那么程序实体是什么呢&#xff1f;在 Go 语言中&…

深度强化学习Task3:A2C、A3C算法

本篇博客是本人参加Datawhale组队学习第三次任务的笔记 【教程地址】 文章目录 Actor-Critic 算法提出的动机Q Actor-Critic 算法A2C 与 A3C 算法广义优势估计A3C实现建立Actor和Critic网络定义智能体定义环境训练利用JoyRL实现多进程 练习总结 Actor-Critic 算法提出的动机 蒙…

基于springboot+vue的网上租赁系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

浪花 - 查询队伍列表

一、接口设计 1. 请求参数&#xff1a;封装 TeamQuery package com.example.usercenter.model.dto;import com.example.usercenter.common.PageRequest; import lombok.Data;/*** author 乐小鑫* version 1.0* Date 2024-01-22-20:14*/ Data public class TeamQuery extends …

助力医疗数字化转型,贝锐x医百科技案例解析

在医疗数字化这个历史进程的大浪潮中&#xff0c;医药企业扮演着重要的角色&#xff0c;其重要程度恐怕仅次于医疗机构本身。同时&#xff0c;数字化转型对于医药企业的赋能作用也是十分明显的&#xff0c;尤其在营销端&#xff0c;一系列的数字化管理、数字化推广方案已经成为…

Windows下安装达梦8开发版数据库

达梦数据库属于国产主流数据库之一&#xff0c;本文记录WIndows下安装最新的达梦8数据库的过程。   达梦官网&#xff08;参考文献1&#xff09;下载开发版&#xff08;X86平台&#xff09;版安装包&#xff0c;如下图所示&#xff1a; 解压安装包后&#xff0c;其中包含ISO文…

Neo4j基本用法

Neo4j基本用法 找到电影的示例点开 跳到2&#xff0c;然后点击提供的示例代码&#xff0c;会自动复制粘贴到上方的控制台&#xff0c;然后点击执行按钮 当我们点击了执行以后&#xff0c;会得到如下的基本图模型 将指示器跳到3&#xff0c;然后点击第一个查询语句 同样地&…

虚拟机网络配置及Moba工具的使用

A、设置IP和网关 1、设置IP [roothadoop00 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 &#xff08;修改如下标红内容&#xff0c;没有的就添加&#xff09; DEVICEeth0 HWADDR08:00:27:BD:9D:B5 #不用改 TYPEEthernet UUID53e4e4b6-9724-43ab-9da7-68792e611031…

人类行为动作数据集大合集

最近收集了一大波关于人类行为动作的数据集&#xff0c;主要包括&#xff1a;动作识别、行为识别、活动预测、动作行为分类等数据集。废话不多说&#xff0c;接下来就给大家介绍这些数据集&#xff01;&#xff01; 1、用于自动视频编辑的视频Blooper数据集 用于自动视频编辑…

助力工业焊缝质量检测,YOLOv7【tiny/l/x】不同系列参数模型开发构建工业焊接场景下工件表面焊接缺陷检测识别分析系统

焊接是一个不陌生但是对于开发来说相对小众的场景&#xff0c;在工件表面焊接场景下常常有对工件表面缺陷智能自动化检测识别的需求&#xff0c;工业AI结合落地是一个比较有潜力的场景&#xff0c;在我们前面的博文开发实践中也有一些相关的实践&#xff0c;感兴趣的话可以自行…

python-分享篇-时钟

文章目录 代码效果 代码 Function:Python制作简易时钟 Author:Charles 微信公众号:TONOWimport turtle import datetime悬空移动 def move(distance):turtle.penup()turtle.forward(distance)turtle.pendown()创建表针turtle def createHand(name, length):turtle.reset()move(…

【开源】基于JAVA的班级考勤管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统基础支持模块2.2 班级学生教师支持模块2.3 考勤签到管理2.4 学生请假管理 三、系统设计3.1 功能设计3.1.1 系统基础支持模块3.1.2 班级学生教师档案模块3.1.3 考勤签到管理模块3.1.4 学生请假管理模块 3.2 数据库设…

C++PythonC# 三语言OpenCV从零开发(3):图像读取和显示

文章目录 相关链接前言Mat是什么读取图片CC#Python 灰度处理CCSharpPython 打印图像信息CCsharpPython 总结 相关链接 C&Python&Csharp in OpenCV 专栏 【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程&#xff08;附带课程课件资料课件笔记&#xff09; …

C#winform上位机开发学习笔记11-串口助手接收数据用波形显示功能添加

1.功能描述 接收串口数据&#xff0c;并将收到的十六进制数据用坐标系的方式将数据波形展示出来 2.代码部分 步骤1&#xff1a;定义链表&#xff0c;用于数据保存 //数据结构-线性链表private List<byte> DataList new List<byte>(); 步骤2&#xff1a;定义波…