У меня есть 2 вопроса после довольно длинной преамбулы.
Глядя на любой указатель функции как на void*
, я могу изменить его первые инструкции, преобразовать их в jmp
(либо32-битный относительный или 64-битный абсолютный, через r11
, в зависимости от x86 / x86-64).Я полагаю, что просмотр кода функции как данных недопустим как в C, так и в C ++, но, похоже, он каким-то образом не поддерживается, как в MSVC (Win32), так и в GCC (OS X).В Интернете есть несколько мест, где говорят, что приведение указателей на void*
является недопустимым .
. Просто не получить указатель на члена класса.Я имею в виду, что компилятор напрямую выдает ошибки во время сборки при попытке взглянуть на такой указатель так же, как я бы посмотрел на void *
, практика, которая, кажется, работает отлично для функций, не являющихся членами.
К счастью, чтобы подключить Direct3D9, я работаю с такими вещами, как IDirect3DDevice9
, у которых есть vtable
.С pDev
типа IDirect3DDevice9*
достаточно, чтобы я посмотрел на pDev
как на PVOID*
.Тогда первое значение в 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]
);
, поскольку Present является 18-й записью.
Первый ответ здесь дает более элегантный метод более высокого уровня, начиная с определения CINTERFACE
.Я еще не проверял, но в соответствии с этим я могу делать такие вещи, как
reinterpret_cast<PVOID>(pDev->lpVtbl->Present)
без ошибок.
Первая проблема. Я не удивительныйПрограммист на 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;
}
и что-то в том же духе для x86-64.Для виртуального члена объекта я получаю указатель foo
от самого vtable
, как я показал выше:
reinterpret_cast<FUNC_TYPE>(
*(reinterpret_cast<void**>(
*reinterpret_cast<void**>(objptr)) + n
)
)
Вторая проблема. Не могу япросто изменить записи из vtable
для моего объекта?Вот пример, само собой разумеется, что он не работает для объекта pDev
, как взято непосредственно из Direct3D, но Такси , кажется, использует этот метод:
#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;
}