免费做做网站,手机平台软件开发,大连做网站哪里好,wordpress主题mirana尝试修改虚函数表
本期纯整活儿好吧#xff01;#xff01;#xff01;#xff01;
初衷
有一天我突然开始好奇虚函数表是否真的存在#xff0c;于是我开始想是否能够从C中查看或者调用虚函数表中的内容。#xff0c;于是有了下面的操作。
操作过程
起初我并没有思路…尝试修改虚函数表
本期纯整活儿好吧
初衷
有一天我突然开始好奇虚函数表是否真的存在于是我开始想是否能够从C中查看或者调用虚函数表中的内容。于是有了下面的操作。
操作过程
起初我并没有思路但是我知道每一个类对应一个虚函数表因此首先我需要一个虚函数因此我随便写了一个基类
class Base {
public:void output() {cout Class Base endl; };virtual void say() {cout Class Base endl;}
};然后写一个子类去 override 一下他的这个函数
class A : public Base {
public:void output() {cout Class A endl;}void say() override {cout Class A endl;}int x;
};然后按照同样的方式再创建一个 B 类
class B : public Base {
public:void output() {cout Class B endl;}void say() override {cout Class B endl;}
};
这样以来应该会有三个虚函数表分别是
Base基类对应的虚函数表A类对应的虚函数表B类对应的虚函数表
然后如何调用他们呢我想了好久想出这样的一个方法
int main() {A a;B b;cout As virtual table address : ((void **)(a))[0] endl;cout As virtual table address : ((void **)(b))[0] endl;return 0;
}根据理论来说C中的虚函数表应该在类内空间的第一个位置占八个字节是一个指向函数表的指针那么我们就应该这样做
((void **)(b))[0];这会返回一个虚函数表的地址。
这句话是什么意思呢首先我们要清楚对象的空间分配与结构体是一样的而根据理论来看虚函数表的指针会被编译器自动添加在对象空间的初始位置也就是说对象所在的空间的第一个单元存储的是虚函数表的地址。
如何获得这个首地址呢首先我们要像取数组首地址一样用取地址符号获得对象的首地址。然后将其强制转换为 (void **) 类型这相当于让电脑将这个对象的空间看作一个数组这个数组中存放的全部都是指向 void * 类型的数据的地址。
void * 类型是函数指针类型我们不用管最后在末尾添加[0]就相当于得到了虚函数表的地址。
尝试输出一下 嗯看起来没啥问题但是如何证明他是个虚函数表的地址呢
我能否将一个类中的修改到另一个虚函数表中然后让这个对象执行的时候出现另外一个类的动作
于是我开始了下面的尝试
int main() {A a;B b;cout Class A virtual table address : ((void **)(a))[0] endl;cout Class B virtual table address : ((void **)(b))[0] endl;((void **)(a))[0] ((void **)(b))[0]; // 把b对应的类的虚函数表覆盖到a上a.say(); // 如果虚函数表被覆盖了的话那么就会出现a执行了b的say方法的状况b.say();return 0;
}然而结果是这样的 发现结果并没有被改变这是怎么回事我百思不得其解多方询问过之后了解到是gcc编译器把我的虚函数的调用过程给优化掉了无奈我只能使用指针和引用来赋值
int main() {A a;B b;Base *ap a, *bp b;cout Class A virtual table address : ((void **)(a))[0] endl;cout Class B virtual table address : ((void **)(b))[0] endl;((void **)(a))[0] ((void **)(b))[0]; // 把b对应的类的虚函数表覆盖到a上ap-say(); // 如果虚函数表被覆盖了的话那么就会出现a执行了b的say方法的状况bp-say();return 0;
}执行成功啦
其实后面我还做了很多好玩的操作这里先不放出来写的有点累下次再凑出一篇来 :wq 拜拜