我对挂钩感兴趣,我决定看看是否可以挂钩一些功能。我对使用类似弯路的图书馆不感兴趣,因为我想拥有自己做的经验。通过我在互联网上找到的一些资料,我能够在下面创建代码。这是基本的,但它可以正常工作。但是当挂钩由多个线程调用的函数时,它被证明是非常不稳定的。如果几乎同时进行两次通话,它就会崩溃。经过一些研究,我认为我需要创建一个蹦床功能。在寻找了几个小时后,我无法找到任何其他关于蹦床的概述。我找不到任何具体的关于编写蹦床功能,或者它们是如何工作的。如果有人可以帮我写一个,发布一些消息来源,或者至少通过推荐一些文章,网站,书籍等方式指出我正确的方向。我将非常感激。
以下是我写的代码。这是非常基本的,但我希望其他人可以从中学习。
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;
}
答案 0 :(得分:8)
如果你希望你的钩子在被多个线程调用时是安全的,你不希望不断地解开并重新连接原始的API。
蹦床只是你生成的一些代码,它复制了原始API的前几个字节的功能(你用跳转覆盖了它),然后在你覆盖的字节后跳转到API。
不是取消API,调用它并重新编写它,而只需调用蹦床。
在x86上执行起来相当复杂,因为您需要(一个相当小的)反汇编程序来查找指令边界。您还需要检查复制到蹦床中的代码是否与指令指针(如jmp,分支或调用)无关。
这足以调用hook线程安全,但如果多个线程正在使用API,则无法创建钩子。为此,您需要使用两字节近跳(可以原子方式写入)来挂钩函数。 Windows API经常在几个NOP之前(可以用远程跳转覆盖),以便为这个近乎跳转提供目标。
在x64上执行此操作太多更复杂。您不能简单地使用64位远程跳转来修补该功能(因为没有一个,并且模拟它的指令通常太长)。而且,根据您的蹦床的作用,您可能需要将其添加到操作系统的堆栈展开信息中。
我希望这不是太笼统。
答案 1 :(得分:1)
事实上的标准钩子教程来自jbremer,here可用
这里是一个简单的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);
}