C++ 虚指针、成员变量与类对象的偏移地址
先给出一段代码实现
#include <iostream>
using namespace std;
class animal
{
protected:
int age;
public:
virtual void print_age(void) = 0;
};
class dog : public animal
{
public:
dog() {this -> age = 2;}
~dog() { }
virtual void print_age(void) {cout<<"Wang, my age = "<<this -> age<<endl;}
};
class cat: public animal
{
public:
cat() {this -> age = 1;}
~cat() { }
virtual void print_age(void) {cout<<"Miao, my age = "<<this -> age<<endl;}
};
int main(void)
{
cat kitty;
dog jd;
animal * pa;
int * p = (int *)(&kitty);
int * q = (int *)(&jd);
p[0] = q[0];
pa = &kitty;
pa -> print_age();
return 0;
}
【源代码输出】
代码输出是 Wang, my age = 1;
假设将 p[0] = q[0]; 换为 p[1] = q[1]; 则输出为 Miao, my age = 2。
【源代码分析】
首先,这是一个取巧的改变虚表指针的办法,它利用了C++的对象模型的特点。我们知道,一个类有了虚函数后。它会有一个虚表来维护虚函数和一个虚表指针__vptr来指向它。而这个程序利用的即是改变虚指针的指向。它首先&kitty,而且转换为int*,获得cat类的虚表首地址,相同&jd获得dog类的虚表地址,而p[0] = q[0]令指向cat的虚表首地址。一下就变成了指向dog类的虚表首地址,然后基类获取到了这个指向dog类的kitty,调用虚方法则自然调用到了dog的print_age,然后这里的age则依旧保留的是cat的。由于你仅仅是改变了虚指针指向的虚表地址,不影响member
data。
重中之重,记住一个点:类对象的首地址是虚函数指针地址,其次是变量地址。改变对象指针类型,将改变实函数,改变对象指针变量,将改变虚函数与成员变量。
其次,这代码不但依赖某些C++编译器的行为,还依赖平台的指针宽度是32位。
int * p = (int *)(&kitty);
int * q = (int *)(&jd);
p[0] = q[0];
这几句不应该用int*,而应该用intptr_t*才对。这样才干保证拷贝的是一个指针宽度的数据。而不是一个int宽度的数据。
在32位平台上。int一般是32位,而指针是32位,所以正好匹配了,程序能正常执行;
在64位平台上,假设是流行的LP64模型。int是32位而指针是64位,这里实际上仅仅拷贝了指针的一半。程序是否能正常执行就看运气了。
假设是在一个64位且小端(little endian)的平台上,那这代码拷贝的是指针的低32位。非常可能会运气好能正常执行,由于dog类与cat类的vtable可能正好在内存里处于非常近的位置,它们的地址的高32位可能正好同样,地址不同的地方都在低32位。这样这个程序就运气好能正常执行。
假设是在一个64位且大端(big endian)的平台上。那这段代码拷贝的是指针的高32位,那就全然达不到效果了。
最后。这样的题还有非常多玩法。比如说一种简单的玩法是像这样:
#include <iostream>
using namespace std;
class animal
{
protected:
int age;
public:
virtual void print_age(void) = 0;
};
class dog : public animal
{
public:
dog() {this -> age = 2;}
~dog() { }
virtual void print_age(void) {cout<<"Wang, my age = "<<this -> age<<endl;}
};
class cat: public animal
{
public:
cat() {this -> age = 1;}
~cat() { }
virtual void print_age(void) {cout<<"Miao, my age = "<<this -> age<<endl;}
};
int main(void)
{
cat kitty;
dog jd;
animal * pa;
int * p = (int *)(&kitty);
int * q = (int *)(&jd);
p[0] = q[0];
pa = &kitty;
pa -> print_age();
return 0;
}
直接整个vtable伪造出来然后想往里面填啥就填啥。
C++ 虚指针、成员变量与类对象的偏移地址的更多相关文章
- C++空类以及没有成员变量的类的大小
关于C++中空类的大小为1,我们大家都有所了解,但是除了空类之外的其他一些没有成员变量的类的大小,还是有很多不明之处的. 我们来看如下一个例子: #include<iostream> us ...
- static 成员变量、static 成员函数、类/对象的大小
一.static 成员变量 对于特定类型的全体对象而言,有时候可能需要访问一个全局的变量.比如说统计某种类型对象已创建的数量. 如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量 ...
- static成员变量与返回对象的引用
(1)用static修饰类成员变量(属性),表明该变量是静态的,无论创建多少对象,都只创建一个一个静态属性副本,也就是对象们共享同一个静态属性,这个方法常用的一个用途就是用来计算程序调用了多少次这个类 ...
- Runtime之成员变量&属性&关联对象
上篇介绍了Runtime类和对象的相关知识点,在4.5和4.6小节,也介绍了成员变量和属性的一些方法应用.本篇将讨论实现细节的相关内容. 在讨论之前,我们先来介绍一个很冷僻但又很有用的一个关键字:@e ...
- 『无为则无心』Python面向对象 — 51、私有成员变量(类中数据的封装)
目录 1.私有成员变量介绍 (1)私有成员变量概念 (2)私有成员变量特点 (3)私有成员变量体验 2.属性私有化工作原理 3.定义成员变量的标识符规范 4.私有成员变量的获取和设置方式 1.私有成员 ...
- Python的类变量和成员变量、类静态方法和类成员方法
先说明几个相关的术语:attribute.function.method. attribute:类对象的数据成员.我们经常会在Python代码出错时遇到:“AttributeError: 'My_Cl ...
- java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值
在上一篇文章中.我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下.如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证 ...
- Pyton:类变量,实例变量,类对象,实例对象
https://www.cnblogs.com/crazyrunning/p/6945183.html
- php 对象赋值后改变成员变量影响赋值对象
话不多说看代码 打印结果 对obj1的操作 直接影响了obj2 , 对obj2的操作 直接影响了obj1
随机推荐
- 常见的XSS攻击代码
第一类: <tag on*=*/> 在html标签事件中触发,典型的是on*事件,但是这种触发模式的缺陷在于不能直接触发所以更多的需要配合使用. eg: 1.使html元素占据整个显示页面 ...
- day3--深入学习命令总结
1.查看命令帮助的几种方法 a.[命令] --help 适用于一般命令,非内置命令 b.man [命令] 适用于一般命令,非内置命令 c.help [命令] 适用于内置命令 d ...
- JMeter基础教程3:脚本录制篇
对于一些JMeter初学者来说,录制脚本可能是最容易掌握的技能之一.虽然我不建议录制性能脚本(因为录制的脚本比较混乱,必须要通过二次处理才可正常使用),但有时做总比不做要好,是吧?下面我们详细介绍使用 ...
- File I/O
File I/O Introduction We'll start our discussion of the UNIX System by describing the functions ...
- OpenTSDB - 分布式可扩展的监控系统
OpenTSDB - A Distributed, Scalable Monitoring System http://opentsdb.net/getting-started.html http:/ ...
- (五)solr7.1.0之solrJ的使用
(五)solr7.1.0之solrJ的使用 下面是solr7的官网API介绍: 网页翻译的不是很准确,只能了解个大概,基本能获取如下信息: 一.构建和运行SolrJ应用程序 对于用Maven构建的项目 ...
- react入门到进阶(一)
一.何为react Facebook在F8会议上首次提出这个概念,一套全新的框架就此诞生. React 不是一个完整的 MVC.MVVM 框架,其只负责 View 层 React 跟 Web Comp ...
- CNCC2017中的深度学习与跨媒体智能
CNCC2017中的深度学习与跨媒体智能 转载请注明作者:梦里茶 目录 机器学习与跨媒体智能 传统方法与深度学习 图像分割 小数据集下的深度学习 语音前沿技术 生成模型 基于贝叶斯的视觉信息编解码 珠 ...
- win下搭建python3+PyQt5+eric6环境
一.安装python3 1.下载python3的安装包,默认安装即可,注意勾选 Add Python 3.6 to Path .但是这样默认安装的路径太长,不太方便找到,可选择定制安装,自己定义安装路 ...
- 再学习之Spring(依赖注入)
一.概述 Spring框架是以 简化Java EE应用程序的开发 为目标而创建的.Spring可以实现很多功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入和面向切面编程.几乎Sprin ...