完全覆盖VMT(虚拟方法表)
Completely overriding the VMT (Virtual Method Table)
我通过解引用来调用vmt上的虚拟方法,直到我得到指向该方法的指针。
这一切都很好,然而,我如何完全改变指针VM表上的对象?
例子:
页;//指向默认的VM表
PP B;//指向一个完全不同的VM表
A->MethodOne()//调用如上所述
B->MethodOne()//调用一个完全不同的方法,因为我们将它指向VM表的指针覆盖到一个具有不同方法指针的替代表
我该如何做到这一点?
我的代码:
#include <Windows.h>
#include <iostream>
class PP
{
public:
PP() { }
~PP() { }
virtual void MethodOne() { std::cout << "1" << std::endl; }
virtual void MethodTwo() { std::cout << "2" << std::endl; }
};
typedef void (*MyFunc)(void);
int main()
{
PP* A = new PP();
//(*(void(**)(void))(*(DWORD*)A + (4*1)))();
( *(MyFunc*) ( *(DWORD*)A + (4*0) ) )(); // call index 0 (4bytes*0)
A->MethodOne();
A->MethodTwo();
system("PAUSE");
delete A;
return 0;
}
由于派生另一个类的常用方法对您不起作用,因此我可以想到三种解决方案。
-
修改虚表指针。这是不可携带的,有很多地方会出严重的问题。假设虚函数表位于类的开头(WinAPI中对于简单类是这样),您可以将该指针替换为指向您自己的表的指针。
*(void **)A = newVtable;
,用适当的指向成员函数的指针定义newVtable。你必须非常小心地设置它。它还可能搞砸删除和异常处理。
-
创建自己的虚表。用所需的指向方法函数的指针定义一个类,然后在类中定义指向其中一个方法函数的指针。然后,您可以根据需要更改指向表的指针。虽然可以定义其他成员函数来隐藏丑陋的代码,但在调用时这样做会有点冗长。
class vtable; class PP { public: PP(); ~PP() { } void MethodOne() { std::cout << "1" << std::endl; } void MethodTwo() { std::cout << "2" << std::endl; } const vtable *pVtable; }; class vtable { public: void (PP::*MethodOne)(); }; vtable One = {&PP::MethodOne}; vtable Two = {&PP::MethodTwo}; PP::PP(): pVtable(&One) { } void main() { PP* A = new PP(); A->pVtable = &One; // call with (A->*(A->pVtable->MethodOne))(); // calls MethodOne A->pVtable = &Two; (A->*(A->pVtable->MethodOne))(); // calls MethodTwo }
- 在类中定义方法指针,并单独更新。
(在VS2015社区中编译和测试)。这样既方便又安全。
如果我正确理解你的问题-你想在运行时替换对象的VM表。不确定为什么要使用c++语言进行如此低级的修改?
无论如何,Microsoft C/c++支持所谓的"裸"调用约定(与"stdcall","fastcall"等不同的是参数传递给函数的方式/顺序以及它们是在堆栈上传递还是在寄存器中传递)。在裸调用约定中,你可以绝对控制如何传递参数——你编写自己的内联汇编代码片段,负责将内容放入堆栈和展开堆栈。
https://msdn.microsoft.com/en-us/library/5ekezyy2.aspx 例如,你可以对构造函数使用裸调用约定(如果编译器不会报错),并将新的VM表作为"隐藏"参数传入,就像"this"参数是传递给成员函数的"隐藏"参数一样(在"thiscall"调用约定中)。您还可以在内联汇编中使用魔法来替换构造函数中的VM。这似乎是一个可怕的,脆弱的想法,因为一个新版本的编译器改变了它的内部(通常不暴露给你)可能会破坏你的代码。如果你真的需要某种动态机制来选择调用什么方法,听起来你应该实现你自己的机制,而不是在c++的VMT机制之上附加一个hack。
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 如何编写 operator= 用于使用虚拟方法与非平凡成员的匿名联合
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 使用模板而不是虚拟方法的管道模式
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 为什么调用没有正文的纯虚拟方法不会导致链接器错误?
- 出于什么目的,非虚拟方法将与C++一起使用?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 如何重写继承的嵌套类中存在的虚拟方法
- 私有虚拟方法有什么用?
- 派生类中纯虚拟基方法的专业化
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 确保模拟的 GTest 方法覆盖虚拟方法
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 如何调用孩子的方法:虚拟关键字不起作用