完全覆盖VMT(虚拟方法表)

时间:2015-08-18 23:44:17

标签: c++ object vtable vmt

我通过解除引用在vmt上调用虚方法,直到我得到指向该方法的指针。

这一切都很好,但是如何完全更改指向对象上VM表的指针?

实施例

  

PP A; //指向其默认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;
}

2 个答案:

答案 0 :(得分:4)

由于推导另一个课程的常用方法不适合你,我可以想到三种解决方案。

  1. 更改vtable指针。这是不可移植的,并且有许多方法可以解决可怕的错误。假设vtable位于类的开头(它适用于WinAPI中的简单类),您可以将该指针替换为您自己的表。

    *(void **)A = newVtable;
    
  2. 使用适当的指向成员函数的指针定义newVtable。你必须非常谨慎地设置它。它还可能搞乱删除和异常处理。

    1. 创建自己的vtable。使用所需的指向方法函数的函数定义一个类,然后在类中将指针定义为其中一个。然后,您可以根据需要更改指向表的指针。虽然你可以定义其他成员函数来隐藏丑陋的代码,但这在调用时会更加冗长。

      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
      }
      
    2. (使用VS2015社区进行编译和测试)。这将是便携和安全的。

      1. 在类中定义方法指针,并单独更新它们。

答案 1 :(得分:0)

如果我正确理解您的问题 - 您希望在运行时替换对象的VM表。不确定为什么使用C ++语言进行这种低级修改?

无论如何,Microsoft C / C ++支持称为&#34; naked&#34;调用约定(而不是&#34; stdcall&#34;,&#34; fastcall&#34;等等,它们在如何/什么顺序传递给函数以及它们是否在堆栈上传递或不同在寄存器中)。在裸调用约定中,您可以完全控制如何传递params - 您可以编写自己的内联汇编代码片段,负责将内容放入堆栈并进行堆栈展开。

https://msdn.microsoft.com/en-us/library/5ekezyy2.aspx

例如,您可以使用构造函数的裸调用约定(如果编译器不会抱怨)并将新VM表作为&#34;隐藏&#34; param,就像&#34;这个&#34; param是一个隐藏的&#34; param传递给成员函数(在&#34; thiscall&#34;调用约定中)。你可以在内联汇编中发挥你的魔力来替换构造函数中的VM。

这整个事情似乎是一个可怕的,脆弱的想法,但因为更新其内部(通常不会暴露给您)的新版本的编译器可能会破坏您的代码。如果你真的需要某种动态机制来选择要调用的方法,那么你应该实现自己的机制,而不是在C ++的VMT机制之上作为黑客攻击。