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

news2024/11/6 7:19:26

先理解一件事,在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++编译器实现多态的一个先提条件。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

跟无神学AI之Prompt

在大模型时代会写prompt变得很重要。 Prompt翻译为中文为提示词&#xff0c;在大模型的特定领域指的是大模型使用者给大模型提交的一种有一定格式的交互命令&#xff0c;让我们看看科大讯飞的大模型给出的答案—— Prompt是一种向人工智能模型提供的输入文本或指令&#xff0…

今日AI大热潮,明日智能风向标

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

YOLO系列(YOLO1-YOLO5)技术规格、应用场景、特点及性能对比分析

文章目录 前言一、YOLOv1-YOLOv5技术规格对比&#xff1a;二、主要应用场景和特点&#xff1a;三、性能对比分析&#xff1a;四、市场应用前景及对不同用户群体的潜在影响&#xff1a;总结 前言 YOLO&#xff08;You Only Look Once&#xff09;系列模型作为一种实时目标检测算…

【数据库学习】pg安装与运维

1&#xff0c;安装与配置 #安装 yum install https:....rpm1&#xff09;安装目录 bin目录&#xff1a;二进制可执行文件目录&#xff0c;此目录下有postgres、psql等可执行程序&#xff1b;pg_ctl工具在此目录&#xff0c;可以通过pg_ctl --help查看具体使用。 conf目录&…

Linux实现:从倒计时到进度条

文章目录 1.回车与换行2.缓冲区的概念3.倒计时4.进度条(第一版无应用场景)5.进度条(第二版有应用场景) 1.回车与换行 2.缓冲区的概念 强制刷新可以使用冲刷函数fflush #include <stdio.h> #include <unistd.h> int main() {printf("I am a \nhandsome man!&q…

HTML - 介绍

一.简介 HTML&#xff0c;超文本标记语言&#xff08;HyperText Markup Language&#xff09;&#xff0c;是一种用于创建网页的标准标记语言。我们可以使用HTML建立自己的WEB网站或特定页面。HTML运行在浏览器上&#xff0c;由浏览器解析。 ⚠️注意&#xff1a;HTML文件的后缀…

STM32连接阿里云物联网平台

文章目录 引言一、STM32连接阿里云物联网平台思路二、ESP8266烧录固件三、使用AT指令连接阿里云物联网平台四、STM32环形串口缓冲区驱动程序五、STM32连接阿里云驱动程序 引言 连续写了两篇关于阿里云连接的文章&#xff0c;都是使用Arduino ESP8266 & Arduino ESP32的方式…

【虚拟机数据恢复】异常断电导致虚拟机无法启动的数据恢复案例

虚拟机数据恢复环境&#xff1a; 某品牌R710服务器MD3200存储&#xff0c;上层是ESXI虚拟机和虚拟机文件&#xff0c;虚拟机中存放有SQL Server数据库。 虚拟机故障&#xff1a; 机房非正常断电导致虚拟机无法启动。服务器管理员检查后发现虚拟机配置文件丢失&#xff0c;所幸…

【论文阅读】GraspNeRF: Multiview-based 6-DoF Grasp Detection

文章目录 GraspNeRF: Multiview-based 6-DoF Grasp Detection for Transparent and Specular Objects Using Generalizable NeRF针对痛点和贡献摘要和结论引言模型框架实验不足之处 GraspNeRF: Multiview-based 6-DoF Grasp Detection for Transparent and Specular Objects Us…

Vulnhub靶场DC-9

攻击机192.168.223.128 靶机192.168.223.138 主机发现 nmap -sP 192.168.223.0/24 端口扫描 nmap -sV -p- -A 192.168.223.138 开启了22 80端口 访问一下web页面 有个查询界面 测试发现存在post型的sql注入 用sqlmap跑一下&#xff0c;因为是post型的&#xff0c;这里…

C#使用RabbitMQ-1_Docker部署并在c#中实现简单模式消息代理

介绍 RabbitMQ是一个开源的消息队列系统&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;。 &#x1f340;RabbitMQ起源于金融系统&#xff0c;现在广泛应用于各种分布式系统中。它的主要功能是在应用程序之间提供异步消息传递&#xff0c;实现系统间的解耦和…

RabbitMQ问题总结

:::info 使用场景 异步发送&#xff08;验证码、短信、邮件。。。&#xff09;MySQL 和 Redis、ES 之间的数据同步分布式事务削峰填谷… ::: 如何保证消息不丢失 上图是消息正常发送的一个过程&#xff0c;那在哪个环节中消息容易丢失&#xff1f;在哪一个环节都可能丢失 生…

Ubuntu20.4 Mono C# gtk 编程习练笔记(四)

连续实时绘图 图看上去不是很清晰&#xff0c;KAZAM录屏AVI尺寸80MB&#xff0c; 转换成gif后10MB, 按CSDN对GIF要求&#xff0c;把它剪裁缩小压缩成了上面的GIF&#xff0c;图像质量大不如原屏AVI&#xff0c;但应该能说明原意&#xff1a;随机数据随时间绘制在 gtk 的 drawin…

使用OpenCV实现一个简单的实时人脸跟踪

简介&#xff1a; 这个项目将通过使用OpenCV库来进行实时人脸跟踪。实时人脸跟踪是一项在实际应用中非常有用的技术&#xff0c;如视频通话、智能监控等。我们将使用OpenCV中的VideoCapture()函数来读取视频流&#xff0c;并使用之前加载的Haar特征级联分类器来进行人脸跟踪。 …

浅谈Python两大爬虫库——urllib库和requests库区别

目录 一、urllib库 1、使用方法 2、功能 3、效率 二、requests库 1、使用方法 2、功能 3、效率 三、总结与建议 在Python中&#xff0c;网络爬虫是一个重要的应用领域。为了实现网络爬虫&#xff0c;Python提供了许多库来发送HTTP请求和处理响应。其中&#xff0c;url…

MySql8的简单使用(1.模糊查询 2.group by 分组 having过滤 3.JSON字段的实践)

MySql8的简单使用&#xff08;1.模糊查询 2.group by 分组 having过滤 3.JSON字段的实践&#xff09; 一.like模糊查询、group by 分组 having 过滤 建表语句 create table student(id int PRIMARY KEY,name char(10),age int,sex char(5)); alter table student add height…

【Linux C | 网络编程】入门知识:TCP协议、TCP客户端、TCP服务端

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

MySQL 数据库 JDBC 简化 JDBCTemplate

JDBCTemplate编程 场景&#xff1a;使用JdbcTemplate技术向MySQL中插入一条数据 表结构如下 create table t_person(person_id int primary key auto_increment,person_name varchar(20),age tinyint,sex enum(男,女,奥特曼),height decimal(4,1),birthday datetime ) AP…

勤学苦练“prompts“,如沐春风“CodeArts Snap“

前言 CodeArts Snap 上手一段时间了&#xff0c;对编程很有帮助。但是&#xff0c;感觉代码编写的不尽人意。 我因此也感到困惑&#xff0c;想要一份完整的 CodeArts Snap 手册看看。 就在我感觉仿佛"独自彷徨在这条悠长、悠长又寂寥的雨巷"时&#xff0c;我听了大…

Ubuntu+Apache+MySQL+PHP+phpstorm+xdebug下的debug环境搭建(纯小白笔记)

关键词 一、win10双系统装Ubuntu&#xff08;Deepin&#xff09; 参考文章&#xff1a; https://www.cnblogs.com/masbay/p/11627727.html 1.1 首先需要确定自己操作系统的引导方式 1.1.1 查看BIOS模式 “winr"快捷键进入"运行”&#xff0c;输入"msinfo32…