How to hack the virtual table?

此生再无相见时 提交于 2019-12-20 09:19:01

问题


I would like to know how to change the address of Test which is in the virtual table with that of HackedVTable.

void HackedVtable()
{
    cout << "Hacked V-Table" << endl;
}

class Base
{    
public:
    virtual Test()  { cout <<"base";    }
    virtual Test1() { cout << "Test 1"; }
    void *prt;
    Base(){}
};

class Derived : public Base
{
public: 
    Test()
    {
        cout <<"derived";
    }
};

int main()
{    
    Base b1;

    b1.Test(); // how to change this so that `HackedVtable` should be called instead of `Test`?

    return 0;
}

Answer will be greatly appreciated.

Thanks in advance.


回答1:


This works for 32-bit MSVC builds (it's a very simplified version of some production code that's been in use for well over a year). Note that your replacement method must explicitly specify the this parameter (pointer).

// you can get the VTable location either by dereferencing the
// first pointer in the object or by analyzing the compiled binary.
unsigned long VTableLocation = 0U;
// then you have to figure out which slot the function is in. this is easy
// since they're in the same order as they are declared in the class definition.
// just make sure to update the index if 1) the function declarations are
// re-ordered and/or 2) virtual methods are added/removed from any base type.
unsigned VTableOffset = 0U;
typedef void (__thiscall Base::*FunctionType)(const Base*);
FunctionType* vtable = reinterpret_cast<FunctionType*>(VTableLocation);

bool hooked = false;
HANDLE process = ::GetCurrentProcess();
DWORD protection = PAGE_READWRITE;
DWORD oldProtection;
if ( ::VirtualProtectEx( process, &vtable[VTableOffset], sizeof(int), protection, &oldProtection ) )
{
    vtable[VTableOffset] = static_cast<FunctionType>(&ReplacementMethod);

    if ( ::VirtualProtectEx( process, &vtable[VTableOffset], sizeof(int), oldProtection, &oldProtection ) )
        hooked = true;
}



回答2:


The V-Table is an implementation detail.

The compiler is not required to use one (it just happens to be the easiest way to implement virtual functions). But saying that each compiler can (and does) implement it slightly differently as a result there is no answer to your question.

If you ask how do I hack a vtable for a program built with:

Compiler <X> Version <Y> Build <Z>

Then somebody may know the answer.




回答3:


void HackedVtable()
{
    cout << "Hacked V-Table" << endl;
}

class Base
{

public:
       virtual Test()  { cout <<"base";    }
       virtual Test1() { cout << "Test 1"; }
       void *prt;
       Base(){}
};

class Derived:public Base
{
    public: 
           Test() 
           {
                   cout <<"derived";
           }
};

typedef void (*FUNPTR)();
typedef struct
{
   FUNPTR funptr;
} VTable;


int main()
{

    Base b1;
    Base *b1ptr = &b;

    VTable vtable;
    vtable.funptr = HackedVtable;

    VTable *vptr = &vtable;
    memcpy ( &b1, &vptr, sizeof(long) );

    b1ptr->Test();

    //b1.Test(); // how to change this so that HackedVtable() should be called instead of Test()

    return 0;
}



回答4:


I don't think there is a portable way. Mostly because of compiler optimization and different architecture ABI between every target.

But C++ provides you with that exact same capability, why not use it?

void HackedVtable()
{
    cout << "Hacked V-Table" << endl;
}

class Base
{
public:
       virtual Test()  { cout <<"base";    }
       virtual Test1() { cout << "Test 1"; }
       void *prt;
       Base(){}
};

class Derived : public Base
{
    public: 
           Test() 
           {
                HackedVtable(); // <-- NOTE
           }
};

int main()
{
    Derived b1; // <-- NOTE

    b1.Test();

    return 0;
}



回答5:


Another way to achieve the same thing is by cheking similar code:

GObject:

http://en.wikipedia.org/wiki/Gobject

GLib:

http://en.wikipedia.org/wiki/GLib

Vala:

http://en.wikipedia.org/wiki/Vala_%28programming_language%29

Those guys wanted to work with a object and class oriented programming language, but "C++", didn't fit their requisites. Then , they took "plain C", and simulate objects withn records & pointers, including Virtual Method Tables. Eventually got a similar language called "Vala", their own "C++" alike language (with their own V.M.T.).

Another related links:

http://en.wikipedia.org/wiki/Virtual_method_table

http://www.artima.com/insidejvm/ed2/jvmP.html

Cheers.




回答6:


Under Mac OS X 10.10.3 + gcc 4.8.3, following code works well.

void HackedVtable()
{
    cout << "Hacked V-Table" << endl;
}

class Base
{    
public:
    virtual void Test()  { cout << "base" << endl;    }
    virtual void Test1() { cout << "Test 1" << endl; }
    void *prt;
    Base(){}
};

class Derived : public Base
{
public: 
    void Test()
    {
        cout << "derived" << endl;
    }
};

int main()
{    
    Base b1;
    Base* pb1 = &b1;

    *(*(void***)pb1) = (void*) HackedVtable;
    pb1->Test();

    //It works for all the Base instance
    Base b2;
    Base* pb2 = &b2;
    pb2->Test();

    //But Derived's virtual function table is separated from Base's
    Derived d1;
    Derived* pd1 = &d1;
    pd1->Test();
    *(*(void***)pd1) = (void*) HackedVtable;
    pd1->Test();

    return 0;
}

Its output:

$ g++ h.cpp; ./a.out
Hacked V-Table
Hacked V-Table
derived
Hacked V-Table

I test the same code under Ubuntu 12.04 + g++ 4.9.0. However, it does not work and arises segmentation fault. It seems Linux assigns the virtual function table in a read only area (e.g. rodata) to forbid hacking.




回答7:


well its quite easy to figure out. Find hte VTAble pointer (In visual studio its the first 4/8 bytes). Then step into a normal call of Test (into the assembler) and you'll see it jump to the Vtable and then to your test function. To override test just replace the pointer where you jumped from in the VTable.




回答8:


This is usually called "virtual table hooking" or something like that. If you are going to use it much, then I suggest the SourceHook library. It was developed for hacking closed source game engines in game mods. For instance, it was used in The Dark Mod before idTech4 became open source.




回答9:


I don't think the vTable is in read only area because is it dynamically populated. The only way it can fail is when the compiler is absolutely sure which implementation will be called in compile time and skip the vTable lookup with direct function call(de-virtualization).

EDIT: As @groovyspaceman pointed out, I see that I used wrong wording. The vTable class member pointer is mutable, the vTable itself is compiler generated and it depends on the system and the compiler if it can, or cannot be modified.



来源:https://stackoverflow.com/questions/1542108/how-to-hack-the-virtual-table

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!