VC++ x86下通过虚表指针调用虚函数如何正确传入this参数
问题根因
Visual C++ x86 编译环境下,非静态类成员函数默认使用__thiscall调用约定,和普通C风格函数的调用规则完全不同:
- 调用成员函数前,必须将类对象的
this指针存入ECX寄存器 - 函数参数从右向左压栈,由被调用的成员函数负责清理栈空间
你之前的代码存在两个核心错误:
- 定义无参函数指针的版本,调用时不会给ECX寄存器传入对象地址,成员函数读取
a时会从ECX指向的随机内存位置取值,自然得到垃圾值 - 注释中传入Demo对象指针的版本,函数指针默认是
__cdecl调用约定,会把对象指针压入栈传递,不会存入ECX,成员函数依然无法拿到正确的this地址。
正确调用代码
按照__thiscall约定声明函数指针即可正常调用,代码如下:
#include <iostream> using namespace std; class Demo { public: int a; Demo(int a) :a(a) {}; virtual void show(); }; void Demo::show() { cout << "a: " << a << endl; } int main() { // 声明__thiscall类型的虚函数指针,this指针由编译器自动通过ECX传递 typedef void (__thiscall *func)(Demo* pThis); Demo d(100); // 取对象首地址存储的虚表指针,再取虚表偏移0位置的show()函数地址 func f = (func)*((int**)*(int*)(&d)); // 传入对象指针,编译器会自动按__thiscall规则处理传参 f(&d); return 0; }
如果不想修改函数指针声明,也可以通过x86内联汇编手动传递ECX寄存器实现调用:
int main() { typedef void (*func)(); Demo d(100); func f = (func)*((int*)*(int*)(&d)); __asm { mov ecx, &d; // 手动将this指针存入ECX call f; } return 0; }
注意事项
- 上述写法仅适配Visual C++ x86环境。x64架构下VC++使用统一的微软x64快速调用约定,指针长度为8字节,this指针通过RCX寄存器传递,虚表读取逻辑需要对应调整,不能直接套用。
- 直接读取虚表、强制转换函数指针的操作属于C标准未定义行为,仅适合用于C底层原理、逆向相关的学习场景,禁止在生产环境使用。
内容的提问来源于stack exchange,提问作者math-chen
相关产品推荐
相关产品推荐

