You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VC++ x86下通过虚表指针调用虚函数如何正确传入this参数

问题根因

Visual C++ x86 编译环境下,非静态类成员函数默认使用__thiscall调用约定,和普通C风格函数的调用规则完全不同:

  • 调用成员函数前,必须将类对象的this指针存入ECX寄存器
  • 函数参数从右向左压栈,由被调用的成员函数负责清理栈空间

你之前的代码存在两个核心错误:

  1. 定义无参函数指针的版本,调用时不会给ECX寄存器传入对象地址,成员函数读取a时会从ECX指向的随机内存位置取值,自然得到垃圾值
  2. 注释中传入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 09:33:29