Привет. Сегодня разберем как хукать d3d9 для рисования квадратов или своего меню.
Что нам нужно хукануть для полноценного рисования?
- Present/EndScene (Вывод на экран информации)
- Reset (Перезагрузка интерфейса)
Подключаем заголовки:
C++:
#include <Windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
Создаем прототип и экземпляр:
C++:
typedef HRESULT(WINAPI* _EndScene)(IDirect3DDevice9*); //прототип
_EndScene oEndScene; //экземпляр прототипа, для возврата оригинала
typedef HRESULT(WINAPI* _Reset)(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*);
_Reset oReset;
Функции подмены:
C++:
HRESULT WINAPI myEndScene(IDirect3DDevice9* m_pDevice)
{
return oEndScene(m_pDevice); // возврат оригинала
}
HRESULT WINAPI myReset(IDirect3DDevice9* m_pDevice, D3DPRESENT_PARAMETERS *pPresentationParameters)
{
auto result = oReset(m_pDevice, pPresentationParameters);
return result;
}
C++:
void InitHook()
{
void** vTableDevice = *(void***)(*(DWORD*)0xC97C28); // адрес Device
VMTHookManager* vmtHooks = new VMTHookManager(vTableDevice);
oEndScene = (_EndScene)vmtHooks->Hook(42, (void*)myEndScene); // 42 - номер EndScene
oReset = (_Reset)vmtHooks->Hook(16, (void*)myReset);// 16 - номер Reset
}
C++:
int WINAPI Thread()
{
while (*(DWORD*)0xC8D4C0 != 9) // проверка на статус загрузки игры
Sleep(100);
InitHook();
return 0;
}
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)Thread, NULL, NULL, NULL);
return TRUE;
}
C++:
void DrawRect(LPDIRECT3DDEVICE9 m_pDevice, int X, int Y, int L, int H, D3DCOLOR color)
{
D3DRECT rect = { X, Y, X + L, Y + H };
m_pDevice->Clear(1, &rect, D3DCLEAR_TARGET, color, 0, 0);
}
HRESULT WINAPI myEndScene(IDirect3DDevice9* m_pDevice)
{
DrawRect(m_pDevice, 250, 250, 10, 10, 0xFFFF0000);
return oEndScene(m_pDevice);
}
Класс VMTHookManager
C++:
VMTHookManager::VMTHookManager(void** vTable) : m_vTable(vTable)
{
m_numberFuncs = GetNumberOfFunctions(); //getting the number of virtual functions
m_originalFuncs = new void*[m_numberFuncs]; // allocating memory so we can save here the addresses of the original functions
for (unsigned short i = 0; i < m_numberFuncs; i++) //loop to fill our array with the original addresses
{
m_originalFuncs[i] = GetFunctionAddyByIndex(i); // saving the address of the original functions
}
}
VMTHookManager::~VMTHookManager()
{
UnhookAll(); //unhooking all the functions
delete[] m_originalFuncs; //we need to free the allocated memory
}
void* VMTHookManager::GetFunctionAddyByIndex(unsigned short index)
{
if (index < m_numberFuncs) //check if the index is in the range of the vtable
{
return m_vTable[index]; //return the address
}
return nullptr; //if not, return nullptr
}
void* VMTHookManager::Hook(unsigned short index, void* ourFunction)
{
uintptr_t bufferOriginalFunc = NULL; //buffer to store the address
if (!toHook(index, true, ourFunction, &bufferOriginalFunc)) //checking if it failed
{
return nullptr; //didn't work, return nullptr
}
return reinterpret_cast<void*>(bufferOriginalFunc); //worked, return the original address
}
bool VMTHookManager::Unhook(unsigned short index)
{
if (!toHook(index))// checking if it failed
{
return false; // return false, didn't work
}
return true; // return true, worked
}
void VMTHookManager::UnhookAll()
{
for (int index = 0; index < m_numberFuncs; index++) //loop to unhook all the functions
{
if (m_vTable[index] == m_originalFuncs[index]) //checking if the function isn't hooked
{
continue; //if not hooked skip this index
}
Unhook(index); //unhook
}
}
unsigned short VMTHookManager::GetNumberOfFunctions() //private function
{
unsigned short index = 0; //index
MEMORY_BASIC_INFORMATION mbiBuffer = { 0 }; //buffer to store the information of some memory region
while (true)
{
if (!m_vTable[index]) //if vtable[index] is null
{
break; //exit loop
}
if (!VirtualQuery(m_vTable[index], &mbiBuffer, sizeof(mbiBuffer))) //if VirtualQuery failed
{
break; //exit loop
}
#define CAN_EXECUTE (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)
if ((mbiBuffer.State != MEM_COMMIT) || (mbiBuffer.Protect & (PAGE_GUARD | PAGE_NOACCESS)) || !(mbiBuffer.Protect & CAN_EXECUTE)) //if the region isn't commited and/or executable
{
break; //exit loop
}
++index;
}
return index;
}
bool VMTHookManager::toHook(unsigned short index, bool hook, void* ourFunction, uintptr_t* bufferOriginalFunc)
{
DWORD OldProtection = NULL; //it will save the protection here
if (index < m_numberFuncs)
{
VirtualProtect(m_vTable + index, 0x4, PAGE_EXECUTE_READWRITE, &OldProtection);//changing the protection
if (hook)
{
if (!ourFunction || !bufferOriginalFunc)
{
VirtualProtect(m_vTable + index, 0x4, OldProtection, &OldProtection); //restore original protection and return
return false;
}
*bufferOriginalFunc = (uintptr_t)m_vTable[index]; // saving the original address in our buffer so we can call the function
m_vTable[index] = ourFunction; //change the address to our function
VirtualProtect(m_vTable + index, 0x4, OldProtection, &OldProtection); //restore protection
return true;
}
else
{
m_vTable[index] = m_originalFuncs[index]; //copy the original address to the vtable[index]
VirtualProtect(m_vTable + index, 0x4, OldProtection, &OldProtection); // restore protection
return true;
}
}
return false;
}
C++:
class VMTHookManager
{
public:
VMTHookManager(void** vTable); //constructor
~VMTHookManager(); //destructor
void* GetFunctionAddyByIndex(unsigned short index);// getting the address of a function in the vtable by index
void* Hook(unsigned short index, void* ourFunction); // hooking the virtual function by index
bool Unhook(unsigned short index); // unhooking the virtual function by index
void UnhookAll(); //unhook all the functions
private:
//member variables
void** m_vTable; // the vtable of some object
unsigned short m_numberFuncs; // number of virtual functions
void** m_originalFuncs = nullptr; // we'll save the original addresses here
unsigned short GetNumberOfFunctions(); //get the number of virtual functions
bool toHook(unsigned short index, bool hook = false, void* ourFunction = nullptr, uintptr_t* bufferOriginalFunc = nullptr); //function used to hook/unhook
};