挂钩(热补丁)类成员函数.修改vtable条目

fox*_*337 2 c++ hook winapi direct3d

经过相当长的序言,我有两个问题.

通过查看任何函数指针,void*我可以修改它的第一条指令,将它们转换为a jmp(32位相对或64位绝对,via r11,取决于x86/x86-64).我认为在数据中查看函数代码在C和C++中都是非法的,但它在MSVC(Win32)和GCC(OS X)中以某种方式似乎都以不受支持的方式工作.互联网上有几个地方说铸造功能指针void* 是非法的.

一个人不仅仅是获得一个指向类成员的指针.我的意思是,编译器在构建时直接抛出错误,试图以与我看待的方式相同的方式查看这样的指针void *,这对于非成员函数来说似乎工作正常.

幸运的是,以钩的Direct3D9,我用的东西那样工作IDirect3DDevice9具有vtable.对于pDev类型IDirect3DDevice9*,我看到的就足够pDevPVOID*.然后,第一个值at pDev是函数指针数组的地址(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]
);
Run Code Online (Sandbox Code Playgroud)

因为现在是第18次入场.

这里的第一个答案给出了一个更优雅,更高级的方法,从定义开始CINTERFACE.我还没有测试过,但根据它,我可以做类似的事情

reinterpret_cast<PVOID>(pDev->lpVtbl->Present)
Run Code Online (Sandbox Code Playgroud)

没有错误.

第一个问题.我不是一个很棒的C++程序员; 一般来说,如何获得指向成员函数的指针,以便我可以覆盖该函数的可执行字节.对于非会员我做:

#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;
}
Run Code Online (Sandbox Code Playgroud)

和x86-64同样的东西.对于一个对象的虚拟成员,我foovtable上面看到了自己的指针:

reinterpret_cast<FUNC_TYPE>(
    *(reinterpret_cast<void**>(
        *reinterpret_cast<void**>(objptr)) + n
    )
)
Run Code Online (Sandbox Code Playgroud)

第二个问题.我不能简单地修改vtable我的对象中的条目吗?这是一个例子,不用说,它不适用于pDev直接来自Direct3D 的对象,但Taksi似乎使用这种方法:

#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;
}
Run Code Online (Sandbox Code Playgroud)

And*_*son 6

执行这个丑陋的演员(成员函数void *)的最快方法将是臭名昭着的union_cast<>:

template <class T1, class T2>
T1 union_cast(T2 v)
{
  static_assert(sizeof(T1) >= sizeof(T2), "Bad union_cast!");
  union UT {T1 t1; T2 t2;} u {};
  u.t2 = v;
  return u.t1;
}
Run Code Online (Sandbox Code Playgroud)

使用这样:

class MyClass
{
public:
  void foo(int);
};

auto p = union_cast<void *>(&MyClass::foo);
Run Code Online (Sandbox Code Playgroud)

现在,我已经给你一把安全关闭的装枪.请小心使用......


val*_*ldo 5

正如您所指出的,您的方法不是"可移植的",但它实际上适用于您的特定情况.恕我直言,没有问题.

第一个答案:

这是使用成员函数指针的语法:

class SomeClass {
    int SomeFunc(int, int);
};

int (SomeClass::* pfn)(int, int); // variable pfn is a pointer to a SomeClass's member function

pfn = &SomeClass::SomeFunc; // assign this variable to the member function with the adequate prototype.

SomeClass obj; // instance of this class

int res = (obj.*pfn)(1, 2); // call the member function pointer
Run Code Online (Sandbox Code Playgroud)

第二个答案:

您可以vtable直接修改成员,但是您应该知道,通过这种方式,您可以继承此类的所有对象(可能还有一些派生类).

如果要仅子类化一个特定的obj,则应创建另一个函数表,并覆盖该对象vtable以指向新的函数表.