如何像编译器一样获取每个虚函数索引?

时间:2010-06-17 14:38:36

标签: c++ assembly polymorphism virtual-functions

是否有一些插件或工具可以读取.h文件(或简单地修改Intellisense本身)并吐出每个函数及其虚函数表索引?有一种模式,我还没有想到与多态性有关,当你开始有5个或更多的类相互衍生时,它会变得更难。但是,无论如何,MSVC ++编译器在编译从C ++到Assembly的虚函数调用时总是吐出正确的虚函数表索引。必须有一种更好的方法来获取索引而不加载,断点,读取偏移量和重写代码,对吧?

谢谢!

4 个答案:

答案 0 :(得分:8)

使用隐藏的Microsoft C / C ++编译器选项“/ d1 reportAllClassLayout”。这将打印出所有类的内存布局和vtable。

答案 1 :(得分:0)

您可以尝试编写一个为您确定它的hack - 这是实现定义的,但您通常可以在类内存的开头找到指向虚函数表的指针。如果你按照这个表的方式在内存中会有一个函数指针列表(但你不知道有多少)。但是,通过在函数指针表中搜索您知道的函数,可以识别其索引。

答案 2 :(得分:0)

在MSVC中,您无法在运行时浏览vtable并与给定的成员函数指针进行比较,因为它们不相同。一个是真正的指针,另一个是指向真实指针的指针。

然而,使用这个编译器,你可以在运行时使用我发现的另一个hack来完成它。

创建一个类(例如,名为IndexFinder),在其中声明与类中可以使用的最大虚拟方法一样多的实例方法。这些方法中的每一个都必须返回一个从0到最大值的唯一整数值​​。

创建一个虚假的虚方法表并存储方法指针,以便它们返回的整数与存储它们的索引相匹配(返回0的方法将是假vtable中的第一个)。 当你想要找到任何虚方法的索引时,你必须对方法成员指针进行一些脏转换,以指向IndexFinder方法指针。

原理很简单:对于虚方法,编译器将生成代码,该代码使用具有良好索引的vtable来实现真实方法。当您用伪造的vtable替换编译器生成的vtable时,它将跳转到您的vtable而不是假设的vtable。当你的方法返回它所存储的索引时,你只需得到返回并获得索引。

这是一个更明确的代码(我重复它是一个编译器依赖的黑客,那些不喜欢它的人,不读它^^)。但我尝试了它,它完美地工作,因为它只是一个重定向黑客(我正在寻找GCC的技巧,但我还没有找到它)。

它可能取决于调用约定,我现在还没有在所有情况下都尝试过它。 这个技巧的一个优点是你不需要构造一个类的实例来找到它的一个虚拟方法的索引。

// In the header .h
class IndexFinder
{
    typedef int (IndexFinder::*method_pointer)();
public:
    template<typename _MethodPtr>
    int getIndexOf(_MethodPtr ptr) { 
        return (reinterpret_cast<IndexFinder*>(&fake_vtable_ptr)->**((IndexFinder::method_pointer*)(&ptr)))() 
    }
protected:
    int method0() { return 0; }
    int method1() { return 1; }        
    int method2() { return 2; }
    int method3() { return 3; }
protected:
typedef method_pointer fake_vtable_t [4]; 
    static fake_vtable_t   fake_vtable;
    void*                  fake_vtable_ptr;  
};

// In the cpp file
IndexFinder::fake_vtable_t IndexFinder::fake_vtable = {
    &IndexFinder::method0 ,
    &IndexFinder::method1 ,
    &IndexFinder::method2 ,
    &IndexFinder::method3 
};
void* IndexFinder::fake_vtable_ptr = &IndexFinder::fake_vtable;

// to use it : 
int index = IndexFinder::getIndexOf(&YourClass::yourVirtualMethod);

答案 3 :(得分:0)

您可以使用开发人员命令提示符

cl /d1 reportSingleClassLayoutXXX filename

XXX->类名

示例

class CBase
{
public:
    CBase() {
    };
    virtual void Walk() { cout << "CBase:Walk" << endl; }
    virtual void Jump() { cout << "CBase:Jump" << endl; }
    void Run(int speed) { cout << "CBase:Run:" << "Speed=" << speed << endl; }
};
class CDerivedA : public CBase
{
public:
    CDerivedA() {
    };
    void Walk() { cout << "CDerivedA:Walk" << endl; }
    void Jump() { cout << "CDerivedA:Jump" << endl; }
    void Run(int speed) { cout << "CDerivedA:Run" << "Speed=" << speed << endl; }
};

布局

D:\nicolas>cl /d1 reportSingleClassLayoutCDerivedA nicolas.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25019 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

class CDerivedA size(4):
        +---
 0      | +--- (base class CBase)
 0      | | {vfptr}
        | +---
        +---

CDerivedA::$vftable@:
        | &CDerivedA_meta
        |  0
 0      | &CDerivedA::Walk
 1      | &CDerivedA::Jump

CDerivedA::Walk this adjustor: 0
CDerivedA::Jump this adjustor: 0