Anschließen (hot patching) Klassen-member-Funktionen. Ändern vtable-Einträge

Ich habe 2 Fragen, nach einer ziemlich langen Präambel.

Durch die suchen-Funktion Zeiger auf void* ich bin in der Lage zu ändern, seine ersten Anweisungen, verwandeln Sie sich in eine jmp (entweder 32-bit relativ-oder 64-bit-absolut -, über r11 je auf x86/x86-64). Ich glaube, dass der Blick auf die Funktion von code als Daten ist illegal in C und C++, aber es irgendwie zu funktionieren scheint, in einer nicht unterstützten Methode, sowohl in Microsoft Visual C ++ (Win32) und GCC (OS X). Es gibt mehrere Orte im Internet, die sagen, dass das casting von Funktionszeigern zu void* illegal ist.

Man nicht einfach einen Zeiger auf eine Klasse. Ich meine, der compiler direkt wirft Fehler bei der Erstellung, wenn Sie versuchen zu schauen wie ein Zeiger in der gleichen Weise, wie ich Aussehen würde bei einer void * -, Praxis -, die scheint zu funktionieren gut für nicht-member-Funktionen.

Glücklicherweise, um die Haken Direct3D9, ich arbeite mit Sachen wie IDirect3DDevice9 die eine vtable. Mit pDev Typ IDirect3DDevice9* reicht es aus, schaue ich mir pDev als bei einem PVOID*. Dann, der erste Wert bei pDev ist die Adresse eines Arrays von Zeigern auf Funktionen (die vtable):

//IDirect3DDevice9::Present()
typedef HRESULT (CALLBACK *PRESENT_PROC)(
    LPDIRECT3DDEVICE9, const RECT*,
    const RECT*,
    HWND,
    const RGNDATA*
);

PVOID (*vPtr)[] = reinterpret_cast<PVOID (*)[]>(
    *reinterpret_cast<PVOID*>(pDev)
);
PRESENT_PROC pDevicePresent = reinterpret_cast<PRESENT_PROC>(
    (*vPtr)[17]
);

da ist der 18-Eintrag.

Die erste Antwort von hier gibt eine elegantere, höher-level-Methode, beginnend mit der Definition CINTERFACE. Ich habe es noch nicht getestet, aber laut ihm kann ich Dinge tun wie

reinterpret_cast<PVOID>(pDev->lpVtbl->Present)

ohne Fehler.

Erste problem. Bin ich nicht eine tolle C++ Programmierer; wie bekomme ich einen Zeiger, der im Allgemeinen an eine member-Funktion, so dass ich die überschreiben die ausführbare bytes der Funktion. Für ein nicht-Mitglied ich:

#include <windows.h>
#include <cstdio>
using namespace std;

const unsigned char OP_JMP = 0xE9;  //32 bit relative jmp
const SIZE_T SIZE_PATCH = 5;        //jmp dword ptr distance; 1 byte + 4 bytes
typedef void (*MyProc)();

void SimpleFunction1()
{
    printf("foo\n");
}

void SimpleFunction2()
{
    printf("bar\n");
}

int main()
{
    PBYTE foo = reinterpret_cast<PBYTE>(SimpleFunction1);
    PBYTE bar = reinterpret_cast<PBYTE>(SimpleFunction2);

    DWORD oldProtection;
    //make sure the bytes of the function are writable
    //by default they are only readable and executable
    BOOL res = VirtualProtect(
        foo,
        SIZE_PATCH,
        PAGE_EXECUTE_READWRITE,
        &oldProtection
    );
    if (!res) return 1;

    //be mindful of pointer arithmetic
    //works with PBYTE, won't with PDWORD
    DWORD distanceToNewFoo = bar - foo - SIZE_PATCH;

    *foo = OP_JMP;
    *reinterpret_cast<PDWORD>(foo + 1) = distanceToNewFoo;

    //called though the pointer instead of foo()
    //to make sure the compiler won't inline or do some other stupid stuff
    reinterpret_cast<MyProc>(foo)(); //will print "bar\n"
    return 0;
}

und etwas in der gleichen Ader für die x86-64. Für einen virtuellen member eines Objekts, bekomme ich die foo Zeiger von der vtable sich, wie ich oben gezeigt:

reinterpret_cast<FUNC_TYPE>(
    *(reinterpret_cast<void**>(
        *reinterpret_cast<void**>(objptr)) + n
    )
)

Zweite problem. Kann ich nicht erhalten, indem Sie einfach ändern Sie die Einträge aus der vtable für mein Objekt? Hier ist ein Beispiel, es ist unnötig zu sagen, es funktioniert nicht für die pDev Objekt, als käme Sie direkt aus Direct3D, aber Taksi scheint um diese Methode zu verwenden:

#include <cstdio>
using namespace std;

class BaseClass
{
public:
    BaseClass(int a = 0, int b = 0);
    int GetA();
    int GetB();
    virtual void Test();
private:
    int _a;
    int _b;
};

BaseClass::BaseClass(int a, int b) :
    _a(a),
    _b(b)
{
}

int BaseClass::GetA()
{
    return _a;
}

int BaseClass::GetB()
{
    return _b;
}

void BaseClass::Test()
{
    printf("test %d; %d\n", _a, _b);
}

void TheNewFunction(BaseClass *bc)
{
    printf("I am an intruder\n");
}

typedef void (*PROC_TYPE)(BaseClass *);

int main()
{
    BaseClass foo(5, 56);
    PROC_TYPE proc = 0;
    proc = reinterpret_cast<PROC_TYPE>(
        *reinterpret_cast<void**>(
            *reinterpret_cast<void**>(&foo)
        )
    );
    proc(&foo);
    reinterpret_cast<void**>(
        *reinterpret_cast<void**>(&foo)
    )[0] = reinterpret_cast<void*>(TheNewFunction);

    foo.Test(); //runs same old Test(); maybe due to compiler optimization?
    proc = reinterpret_cast<PROC_TYPE>(
        *reinterpret_cast<void**>(
            *reinterpret_cast<void**>(&foo)
        )
    );

    proc(&foo); //runs TheNewFunction
    BaseClass *goo = &foo;
    goo->Test(); //runs TheNewFunction
    return 0;
}
InformationsquelleAutor foxx1337 | 2011-08-08
Schreibe einen Kommentar