首页
论坛
专栏
课程

[原创][分享]c++虚函数表的调用与修改

2019-10-9 21:27 1649

[原创][分享]c++虚函数表的调用与修改

2019-10-9 21:27
1649

在c++中虚函数继承会生成一个虚函数表,来确定父类指针指向子类对象时所需要调用的具体的方法,用以实现多态。那么可不可以像静态方法一样直接调用类中的虚函数呢,答案是可行的

虚函数表在内存中的分布

这个东西可以去百度

 

大致原理如下:

 

原理

通过虚函数表的地址直接调用虚函数

如题,在子类对象中,首地址存在一个指向虚函数表的地址,虚函数表中保存着子类具体需要调用的函数地址,代码如下

class A {
public:
    virtual void fun() {
        std::cout << "A::fun\n";
    }
};
class B1 :public A {
public:
    void fun() {
        std::cout << "B1::fun\n";
    }
};
class B2 :public A {
public:
    void fun() {
        std::cout << "B2::fun\n";
    }
};

我们先定义一个父类和两个子类,父类中有一个虚函数,子类分别重写这个虚函数

int main() {
    A* pObj[3] = {
        new A,
        new B1,
        new B2
    };
    for (A* pTmp : pObj) {
        pTmp->fun();
    }
    return 0;
}

定义一个数组,数组中有一个父类对象和两个子类对象,循环调用数组元素的fun()函数,结果如下
正常结果

 

现在我们了解了虚函数表在内存中的位置,那么可以通过特定的方法直接调用虚函数

int objAddress = (int)&pObj[0];     //objAddress的地址是对象的地址
int vtfAddress = *(int*)objAddress; //vtfAddress保存的即为虚函数表的地址
int funAddress = *(int*)vtfAddress; //funAddress保存的是虚函数表中真正调用的函数的地址
void(*pfun)();                      //函数指针,指向虚函数表中真正调用的地址
pfun = (void(*)())(*(int*)funAddress);
pfun();

没有通过类对象,也可以调用到类内的函数

 

函数指针调用

 

同样,还有一种更为高级的写法(来自韦老师的代码)

uint32_t objAddress = (uint32_t)pObj[0];
uint32_t vftAddress = *(uint32_t*)objAddress;
uint32_t funAddress = *(uint32_t*)vftAddress;
using fn = int(*)(int dire);
((fn)funAddress)(0);

上述代码也可以不通过类对象调用类内函数

修改虚函数表改变函数调用

我们知道,如果可以直接调用函数,那么应该有方法来替换掉虚函数表中函数的地址,把他改成我们想要执行的函数

 

现在,我们先来创建一个想要被执行的函数

void g_fun() {
    std::cout << "g_fun\n";
}

把原来要执行的虚函数替换成我们想要让他执行的函数(来自韦老师的代码)

//获取虚函数表的地址
int* pVirtualFunctionTable = (int*)*(int*)pObj[0];
DWORD old;
//修改内存属性
VirtualProtect(pVirtualFunctionTable, 4, PAGE_READWRITE, &old);
pVirtualFunctionTable[0] = (int)g_fun;
pObj[0]->fun();
VirtualProtect(pVirtualFunctionTable, 4, old, &old);

通过上述代码可以达到修改虚函数表的目的
修改虚函数表

 

小弟博客:knocked.github.io大佬求关照!勿喷万分感谢



[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最后于 2019-10-9 21:30 被Knocked编辑 ,原因:
最新回复 (13)
嵩山石 2019-10-9 21:36
2
0
写的不错,看了以后学到了新东西,点赞
zylyy 2019-10-9 23:22
3
0
,以前做XNF得游戏外挂时,修改人物得虚函数表实现某些功能。
方解 2019-10-10 08:53
4
0
总结的很好,上课没懂的在你这看懂了
DlyWtF700 2019-10-10 09:34
5
0
韦一笑, 哈哈
这种通过虚表的直接调用方式, 如果是类似静态函数类型<函数内部没有使用该类的成员变量或者成员函数>, 是可行的...
但如果函数内部有用到 this 指针的话, 自己构造函数指针时应该怎么传 this 参数呢?
难道用内联汇编修改ecx? 调用完成后再改回来
张新琪 1 2019-10-10 10:13
6
0
最后改虚表哪里.可以理解为虚表HOOK. using语法 新的C++语法.
Sprite雪碧 1 2019-10-10 15:38
7
0
DlyWtF700 韦一笑, 哈哈 这种通过虚表的直接调用方式, 如果是类似静态函数类型, 是可行的... 但如果函数内部有用到 this 指针的话, 自己构造函数指针时应该怎么传 this 参数呢? 难道用内联汇 ...
方法很简单,自己写一个class,写一个成员函数,取到函数指针再进行替换即可。
当然内联也不是不可以。
sunsjw 1 5天前
8
0
有缺点指出下,这个方法,没有传this指针,如果访问成员变量就要出错了。
敏而好学 5天前
9
0
能不能讲讲运算符重载?
WinMainE 5天前
10
0
关键就是获取虚函数表在内存中的位置
Knocked 5天前
11
0
敏而好学 能不能讲讲运算符重载?
上一篇就是运算符重载
让利润奔跑 5天前
12
0
请问一下为什么要加上最后一句呢?VirtualProtect(pVirtualFunctionTable, 4, old, &old);  测试的时候如果没有这句也是可以的
Knocked 5天前
13
0
让利润奔跑 请问一下为什么要加上最后一句呢?VirtualProtect(pVirtualFunctionTable, 4, old, &old); 测试的时候如果没有这句也是可以的
把内存属性改回来,不改当然也行啊
让利润奔跑 4天前
14
0
Knocked 把内存属性改回来,不改当然也行啊
好的,谢谢您
游客
登录 | 注册 方可回帖
返回