Как создать батутную функцию для крючка
Я заинтересован в подключении, и я решил посмотреть, смогу ли я подключить некоторые функции. Я не был заинтересован в использовании библиотеки как обходной путь, потому что я хочу иметь опыт делать это самостоятельно. С некоторыми источниками, которые я нашел в Интернете, я смог создать код ниже. Это просто, но работает нормально. Однако при перехвате функций, вызываемых несколькими потоками, это оказывается крайне нестабильным. Если два вызова будут сделаны почти одновременно, произойдет сбой. После некоторых исследований я думаю, что мне нужно создать функцию батута. После долгих часов поисков я не смог найти ничего, кроме общего описания того, что такое батут. Я не мог найти что-то конкретно о написании батутных функций или о том, как они действительно работают. Если бы кто-нибудь мог помочь мне написать один, опубликовать несколько источников или, по крайней мере, указать мне правильное направление, порекомендовав несколько статей, сайтов, книг и т. Д., Я был бы очень признателен.
Ниже приведен код, который я написал. Это действительно просто, но я надеюсь, что другие могут извлечь из этого уроки.
test.cpp
#include "stdafx.h"
Hook hook;
typedef int (WINAPI *tMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
DWORD hMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
hook.removeHook();
tMessageBox oMessageBox = (tMessageBox)hook.funcPtr;
int ret =oMessageBox(hWnd, lpText, "Hooked!", uType);
hook.applyHook(&hMessageBox);
return ret;
}
void hookMessageBox()
{
printf("Hooking MessageBox...\n");
if(hook.findFunc("User32.dll", "MessageBoxA"))
{
if(hook.applyHook(&hMessageBox))
{
printf("hook applied! \n\n");
} else printf("hook could not be applied\n");
}
}
hook.cpp
#include "stdafx.h"
bool Hook::findFunc(char* libName, char* funcName)
{
Hook::funcPtr = (void*)GetProcAddress(GetModuleHandleA(libName), funcName);
return (Hook::funcPtr != NULL);
}
bool Hook::removeHook()
{
DWORD dwProtect;
if(VirtualProtect(Hook::funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect))
{
WriteProcessMemory(GetCurrentProcess(), (LPVOID)Hook::funcPtr, Hook::origData, 6, 0);
VirtualProtect(Hook::funcPtr, 6, dwProtect, NULL);
return true;
} else return false;
}
bool Hook::reapplyHook()
{
DWORD dwProtect;
if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect))
{
WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::hookData, 6, 0);
VirtualProtect(funcPtr, 6, dwProtect, NULL);
return true;
} else return false;
}
bool Hook::applyHook(void* hook)
{
return setHookAtAddress(Hook::funcPtr, hook);
}
bool Hook::setHookAtAddress(void* funcPtr, void* hook)
{
Hook::funcPtr = funcPtr;
BYTE jmp[6] = { 0xE9, //jmp
0x00, 0x00, 0x00, 0x00, //address
0xC3 //retn
};
DWORD dwProtect;
if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) // make memory writable
{
ReadProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::origData, 6, 0); // save old data
DWORD offset = ((DWORD)hook - (DWORD)funcPtr - 5); //((to)-(from)-5)
memcpy(&jmp[1], &offset, 4); // write address into jmp
memcpy(Hook::hookData, jmp, 6); // save hook data
WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, jmp, 6, 0); // write jmp
VirtualProtect(funcPtr, 6, dwProtect, NULL); // reprotect
return true;
} else return false;
}
1 ответ
Если вы хотите, чтобы ваш хук был безопасным при вызове несколькими потоками, вы не хотите постоянно отсоединять и перезагружать исходный API.
Батут - это просто кусок кода, который вы генерируете, который копирует функциональность первых нескольких байтов исходного API (который вы перезаписали с помощью перехода), а затем переходит в API после байтов, которые вы перезаписали.
Вместо того, чтобы отключать API, вызывая его и повторно подключая его, вы просто называете батут.
Это довольно сложно сделать на x86, потому что вам нужен (довольно минимальный) дизассемблер, чтобы найти границы команд. Вы также должны убедиться, что код, который вы копируете в батут, не делает ничего относительно указателя инструкций (например, jmp, branch или call).
Этого достаточно, чтобы сделать вызовы хуком поточно-ориентированным, но вы не можете создать хук, если несколько потоков используют API. Для этого вам нужно перехватить функцию с помощью двухбайтового перехода к ближнему (который может быть записан атомарно). Windows API часто предшествует несколько NOP (которые могут быть перезаписаны с помощью дальнего перехода), чтобы обеспечить цель для этого быстрого перехода.
Делать это на x64 намного сложнее. Вы не можете просто пропатчить функцию с помощью 64-битного дальнего перехода (потому что его нет, а инструкции для его симуляции часто слишком длинные). И, в зависимости от того, что делает ваш батут, вам может понадобиться добавить его в стек для размотки информации ОС.
Я надеюсь, что это не слишком общее.
Стандартное руководство по подключению defacto от jbremer и доступно здесь
Вот простой обход x86 и ловушка для батута, основанная на этом руководстве с использованием функции Direct3D EndScene() в качестве примера:
bool Detour32(char* src, char* dst, const intptr_t len)
{
if (len < 5) return false;
DWORD curProtection;
VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &curProtection);
intptr_t relativeAddress = (intptr_t)(dst - (intptr_t)src) - 5;
*src = (char)'\xE9';
*(intptr_t*)((intptr_t)src + 1) = relativeAddress;
VirtualProtect(src, len, curProtection, &curProtection);
return true;
}
char* TrampHook32(char* src, char* dst, const intptr_t len)
{
// Make sure the length is greater than 5
if (len < 5) return 0;
// Create the gateway (len + 5 for the overwritten bytes + the jmp)
void* gateway = VirtualAlloc(0, len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//Write the stolen bytes into the gateway
memcpy(gateway, src, len);
// Get the gateway to destination addy
intptr_t gatewayRelativeAddr = ((intptr_t)src - (intptr_t)gateway) - 5;
// Add the jmp opcode to the end of the gateway
*(char*)((intptr_t)gateway + len) = 0xE9;
// Add the address to the jmp
*(intptr_t*)((intptr_t)gateway + len + 1) = gatewayRelativeAddr;
// Perform the detour
Detour32(src, dst, len);
return (char*)gateway;
}
typedef HRESULT(APIENTRY* tEndScene)(LPDIRECT3DDEVICE9 pDevice);
tEndScene oEndScene = nullptr;
HRESULT APIENTRY hkEndScene(LPDIRECT3DDEVICE9 pDevice)
{
//do stuff in here
return oEndScene(pDevice);
}
//just an example
int main()
{
oEndScene = (tEndScene)TrampHook32((char*)d3d9Device[42], (char*)hkEndScene, 7);
}