【c++逆向 - 3】类继承 AND 虚表解析

公有派生 ==> is-a 关系

注:以下代码没实际意义,仅仅为了学习写的
公有派生将继承父类的所以数据成员和方法,Point 类来自上一篇博客:

class Point {
private:int x;int y;friend ostream& operator<<(ostream& out, const Point &p);friend Point operator*(const Point& p, int n);friend Point operator*(int n, const Point& p);
public:Point() { cout << "parent" << endl; x = 0; y = 0; }Point(int x, int y) { cout << "parent agrv" << endl; this->x = x; this->y = y; }~Point() { cout << "parent 析构" << endl; };Point operator+(const Point& p) const;Point operator*(const Point& p) const;Point operator++(); // 前置++Point operator++(int); // 后置++void show() const;
};class Child: public Point {
private:float x;float y;
public:Child() { cout << "child" << endl; x = 2.0; y = 2.0; }Child(float x, float y) { cout << "child agrv" << endl; this->x = x; this->y; }Child(int x, int y, float fx, float fy) : Point(x, y) { cout << "child agrv" << endl; this->x = fx; this->y = fy; }~Child() { cout << "child 析构" << endl; };
};int main()
{Child cp(1, 2, 5.0, 6.0);long long* ptr = (long long*)&cp;cout << hex;cout << "0x" << sizeof(cp) << ": " << ptr << endl;cout << dec;float fx = *(float*)(ptr+1);cout << "fx = " << fx << endl;cout << cp << endl;cp.show();return 0;
}

输出结果如下:

parent agrv
child agrv
0x10: 0x7ffeefe5ad10
fx = 5
(1, 2)
(1, 2)
child 析构
parent 析构

注意点:

  • 先调用基类的构造函数,然后再调用派生类的构造函数
  • 派生类通过参数化列表向基类传递初始化参数
  • 先调用派生类的析构函数,然后再调用基类的析构函数
  • 派生类可以调用基类的非私有方法
  • 基类指针(基类引用)可以不在进行显式类型转换的情况下指向派生类对象(引用派生类对象),但是基类指针或引用只能调用基类方法

可以看到代码中我把 cp 的大小和地址打印出来了,我们调试看下:
调试截图
根据输出知道 cp 的大小为 0x10,地址为 0x7fffffffe250,可以看到这里其实存储的就是类的数据成员,前8字节是基类的数据成员,后8字节是派生类的数据成员。
这里也验证了类实例其实只包含自己的数据成员(static const除外),类方法是属于类的。

多态公有继承

简单来说多态就:对象决定方法,其实就是重写基类方法
这里利用到了虚函数:virtual 关键字进行声明
这里对上面代码稍做修改:

class Point {
private:int x;int y;friend ostream& operator<<(ostream& out, const Point &p);friend Point operator*(const Point& p, int n);friend Point operator*(int n, const Point& p);
public:Point() { cout << "parent" << endl; x = 0; y = 0; }Point(int x, int y) { cout << "parent agrv" << endl; this->x = x; this->y = y; }virtual ~Point() { cout << "parent 析构" << endl; };Point operator+(const Point& p) const;Point operator*(const Point& p) const;Point operator++(); // 前置++Point operator++(int); // 后置++virtual void show() const;
};class Child: public Point {
private:float x;float y;
public:Child() { cout << "child" << endl; x = 2.0; y = 2.0; }Child(float x, float y) { cout << "child agrv" << endl; this->x = x; this->y; }Child(int x, int y, float fx, float fy) : Point(x, y) { cout << "child agrv" << endl; this->x = fx; this->y = fy; }~Child() { cout << "child 析构" << endl; };virtual void show() const;
};int main()
{Point pp(10, 5);Child cp(1, 2, 5.0, 6.0);Point& p1 = pp;Point& p2 = cp;long long* ptr = (long long*)&cp;cout << hex <<  "0x" << sizeof(cp) << ": " << ptr << dec << endl;cout << cp << endl;p1.show();p2.show();return 0;
}void Point::show() const
{cout << "(x, y) = (" << x << ", " << y << ")\n";
}void Child::show() const
{Point::show();cout << "(fx, fy) = (" << x << ", " << y << ")\n";
}

输出如下:

parent agrv
parent agrv
child agrv
0x18: 0x7fff68a99bb0
(1, 2)
(x, y) = (10, 5)
(x, y) = (1, 2)
(fx, fy) = (5, 6)
child 析构
parent 析构
parent 析构

可以看到这里输出的 cp 的大小变成了 0x18,这是咋回事呢?先来 IDA 里面看看:

可以看到最后调用 p2.show() 时有点奇怪,看下 cp 类的结构:

可以看到这里的类实际不仅保存了数据成员,还保存着一个虚指针,该虚指针指向虚表,这里我们在对代码改一改:

int main()
{Child cp1(1, 2, 5.0, 6.0);Child cp2(3, 4, 6.0, 7.0);Point& p1 = cp1;Point& p2 = cp2;long long* ptr = (long long*)&cp1;cout << hex <<  "0x" << sizeof(cp1) << ": " << ptr << dec << endl;ptr = (long long*)&cp2;cout << hex <<  "0x" << sizeof(cp2) << ": " << ptr << dec << endl;p1.show();p2.show();return 0;
}

调试可以知道,不同对象实例指向的虚表是相同的,当然这也是当然的,因为之前说过,类方法属于类,而不属于类实例:

再改一改:

int main()
{Point pp1(1, 2);Child cp2(3, 4, 6.0, 7.0);Point& p1 = pp1;Point& p2 = cp2;long long* ptr = (long long*)&pp1;cout << hex <<  "0x" << sizeof(pp1) << ": " << ptr << dec << endl;ptr = (long long*)&cp2;cout << hex <<  "0x" << sizeof(cp2) << ": " << ptr << dec << endl;p1.show();p2.show();return 0;
}

从调试结果知道,所有的虚函数都放在同一个虚表中,不同的类对象(有继承关系)指向的是不同的位置,比如这里的 PointChild 指向的就是不同的位置:

这里只是有个印象,关于虚表的更多知识,在后面会进行解析

抽象基类

当类包含纯虚函数时,该类只能用作基类,不能创建实例对象
纯虚函数:virtual func() = 0;,即函数声明最后加上 = 0 即可
todo

虚表解析

在上面,我们讲到了虚函数,其主要用来实现多态,当类中包含虚函数时,实例对象的头8字节则包含一个虚表指针。考虑如下代码:

#include <iostream>using std::cout;
using std::endl;class A {
private:long x;double y;
public:A() { x = 10; y = 1.; }virtual void foo() const;virtual void bar() const;};int main()
{A a;cout << std::hex << &a << std::dec << endl;return 0;
}void A::foo() const {}
void A::bar() const {}

直接放进 IDA 看下:

可以看到构造函数中多了一段对 _vptr_A 赋值的代码,这其实是编译器做的:

虚表被放到了 data 段上,并且可以看到其实其前面还有 0x10 的数据,接下来看下实例对象:

可以看到实例对象的头8字节就是虚表指针,其指向的是虚表的对应函数的位置,并不是虚表头部,调试也可以看出来:

这里可以看到如果我们可以修改实例对象的虚表指针指向我们伪造的虚表,那么如果程序要调用虚表中的函数时就会去执行我们伪造的函数了。
那么什么时候执行虚函数会去找虚表呢?基类指针或引用操作派生对象
考虑如下代码:

#include <iostream>
#include <cstring>using std::cout;
using std::endl;class A {
private:long x;double y;
public:A() { x = 10; y = 1.; }virtual void foo() const;virtual void bar() const;~A() {}
};class B : public A {
public:virtual void foo() const;void bar() const;
};void hacker()
{puts("Hacker");
}int main()
{long long* ptr = new long long[0x100];ptr[0] = (long long)hacker;ptr[1] = (long long)hacker;A a;cout << std::hex << &a << std::dec << endl;a.bar();A* pb = new B;cout << std::hex << &pb << std::dec << endl;*(long long*)pb = (long long)ptr;pb->bar();return 0;
}void A::foo() const {}
void A::bar() const {}
void B::foo() const {}
void B::bar() const {}

输出:这里成功劫持了虚表

0x7ffe6d3fca60
0x7ffe6d3fca50
Hacker

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

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

相关文章

(1)(1.11) SiK Radio v2(二)

文章目录 前言 4 连接无线电 5 使用Mission Planner进行配置 6 更新固件 7 预期范围 8 提高范围 9 支持不同国家/地区 10 3DR无线电讨论论坛 前言 SiK 遥测无线电是在自动驾驶仪和地面站之间建立遥测连接的最简单方法之一。本文提供了如何连接和配置无线电的基本用户指…

【赠书第13期】边缘计算系统设计与实践

文章目录 前言 1 硬件架构设计 2 软件框架设计 3 网络结构设计 4 安全性、可扩展性和性能优化 5 推荐图书 6 粉丝福利 前言 边缘计算是一种新兴的计算模式&#xff0c;它将计算资源推向网络边缘&#xff0c;以更好地满足实时性、低延迟和大规模设备连接的需求。边缘计算…

16.Redis 高级数据类型 + 网站数据统计

目录 1.Redis 高级数据类型 2.网站数据统计 2.1 业务层 2.2 表现层 2.2.1 记录数据 2.2.2 查看数据 1.Redis 高级数据类型 HyperLogLog&#xff1a;采用一种基数算法&#xff0c;用于完成独立总数的统计&#xff1b;占据空间小&#xff0c;无论统计多少个数据&#xff0…

最新技术整理3款开源免费直播推流工具,实现实时视频推流、视频拉流,目标端可以是服务器、云平台、移动设备等(附源码)

最新技术整理3款开源免费直播推流工具&#xff0c;实现实时视频推流、视频拉流&#xff0c;目标端可以是服务器、云平台、移动设备等&#xff08;附源码&#xff09;。 什么是推流&#xff1f; 视频推流是指将实时的视频数据从一个源端发送到一个或多个目标端的过程。推流的源…

数据库原理及应用·存储过程和触发器

12.1 T-SQL中的变量 12.1.1 T-SQL概述 SQL&#xff08;Structured Query Language&#xff09;结构化查询语言&#xff0c;是一种数据库查询和程序设计语言&#xff0c;用于存取数据以及查询、更新和管理关系数据库系统。 Transact-SQL即事务SQL&#xff0c;也简称为T-SQL&a…

Gateway集成方法以及拦截器和过滤器的使用

前提&#xff1a;请先创建好一个SpringBoot项目 1. 引入依赖 SpringCloud 和 alibabaCloud 、 SpringBoot间对版本有强制要求&#xff0c;我使用的springboot是3.0.2的版本。版本对应关系请看&#xff1a;版本说明 alibaba/spring-cloud-alibaba Wiki GitHub <dependency…

(windows2012共享文件夹和防火墙设置

windows2012共享文件夹和防火墙设置 1.windows2012文件夹共享1.共享和高级共享的区别![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/0d815cc6862a4c7a99be11442fb5d950.png#pic_center) 2.windows的防火墙设置1.防火墙设置8080端口让tomot可以在主机可以访问1.新建…

中间件系列 - Redis入门到实战(高级篇-最佳实践)

前言 学习视频&#xff1a; 黑马程序员Redis入门到实战教程&#xff0c;深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目中间件系列 - Redis入门到实战本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除学习目标 Redis键值设计批处理优化服…

电商数据分析-03-电商数据采集

参考 最最最全数据仓库建设指南&#xff0c;速速收藏&#xff01;&#xff01; 第1章 数据仓库概念 数据仓库规划 1.1 数仓搭建 我们这里所说的数据仓库&#xff0c;是基于大数据体系的&#xff0c;里面包含标签类目&#xff0c;区别于传统的数据仓库。下面我们来将这张图分解…

STM32 支持IAP的bootloader开发,使用串口通过Ymodem协议传输固件

资料下载: https://download.csdn.net/download/vvoennvv/88658447 一、概述 关于IAP的原理和Ymodem协议&#xff0c;本文不做任何论述&#xff0c;本文只论述bootloader如何使用串口通过Ymodem协议接收升级程序并进行IAP升级&#xff0c;以及bootloader和主程序两个工程的配置…

dxbuilder关于开发一款国产数据库建模软件的思考与行动

一、背景 随着一声紧急的呼叫&#xff0c;快快快。把你们有安装PownerDesigner的软件都卸载掉&#xff0c;公司被发律师函了&#xff0c;这是来自于领导的呼喊。 我们公司大部分的软件的数据结构&#xff0c;都是用PownerDesigner来进行设计的。以便进行后期的管理与维护。不…

Vue学习之第一、二章——Vue核心与组件化编程

第一章. Vue核心 1.1 Vue简介 1.1.1 官网 英文官网: https://vuejs.org/中文官网: https://cn.vuejs.org/ 1.1.2 Vue特点 遵循 MVVM 模式编码简洁, 体积小, 运行效率高, 适合移动/PC 端开发它本身只关注 UI, 也可以引入其它第三方库开发项目 1.2 初始Vue 这里可以参考&a…

ios 之 数据库、地理位置、应用内跳转、推送、制作静态库、CoreData

第一节&#xff1a;数据库 常见的API SQLite提供了一系列的API函数&#xff0c;用于执行各种数据库相关的操作。以下是一些常用的SQLite API函数及其简要说明&#xff1a;1. sqlite3_initialize:- 初始化SQLite库。通常在开始使用SQLite之前调用&#xff0c;但如果没有调用&a…

“京东”数据包暴雷——李逵还是李鬼?

大家好&#xff0c;我是吴军&#xff0c;一家软件技术开发公司的产品经理。 前几个月市面上出现了一个京东数据包的项目&#xff0c;乍一听还蛮正规的&#xff0c;强子不卖货&#xff0c;去做数据服务了&#xff1f;他究竟是怎么一个盈利方式&#xff1f;到底是李逵还是李鬼&a…

【新资讯】《网络安全事件报告管理办法(征求意见稿)》正在公开征求意见

近年来网络安全事故频发&#xff0c;造成了不少损失和危害。为了减少网络安全事故的发生&#xff0c;规范网络安全事件的报告&#xff0c;国家互联网信息办公室根据《中华人民共和国网络安全法》等法律法规起草了《网络安全事件报告管理办法&#xff08;征求意见稿&#xff09;…

释放创意,点亮视频!红巨星Magic Bullet Looks带给您绚丽的色彩魔法

Red Giant Magic Bullet Looks 是一款适用于Mac的视频后期处理软件。它是由Red Giant公司开发的一款专业级颜色校正和调色工具&#xff0c;旨在帮助电影制作人、视频编辑和摄影师实现令人惊叹的视觉效果。 Magic Bullet Looks 提供了一个直观而强大的用户界面&#xff0c;使用…

Linux操作系统(Crontab计划任务+NTP时间同步服务器)

如何修改linux系统时间 与时间相关的命令&#xff0c;查看当前的时间 运行 date 即可 cal 查看当前月份的日历 运行 timedatectl 查看时间详细参数 &#xff08; NTP&#xff1a; network time protocol 网络时间协议 &#xff09; &#xff08; local time : 本地时间 &#x…

大创项目推荐 深度学习LSTM新冠数据预测

文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 …

智能优化算法应用:基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鱼鹰算法4.实验参数设定5.算法结果6.参考文献7.MA…

6、IDEA集成GitHub/码云

这里写目录标题 1、设置GitHub账号2、分享工程到GitHub3、Push推送本地库到远程库4、Pull拉取远程库到本地库5、Clone克隆远程库到本地6、码云简介 1、设置GitHub账号 可以采用两种登录方式&#xff1a;账户密码登入、口令登入。 2、分享工程到GitHub 先在GitHub中创建一个远…