内联hook
内联hook的原理是在目标函数的头部,插入一段二进制指令.一般是jmp ret 来实现跳转到hook函数,先进行hook函数的逻辑,然后再取消hook,跳回到目标函数,最后重新hook.再返回. 整体的思路是在原函数的中间,增加一层hook逻辑.增加的方式是修改函数开头部分的二进制来实现.
32位inline-hook
#define FUNC_LEN sizeof(DWORD_PTR)
static PROC GProcFunc = NULL;
static BYTE oldHead[FUNC_LEN + 1] = { 0 };
static BYTE hookHead[FUNC_LEN + 1] = { 0 };
// hook 某个函数
static BOOL Hook(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc);
// 取消hook
static BOOL UnHook();
// 重新hook
static BOOL ReHook();
// HOOK 函数
static int WINAPI HookMessageBox(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType)
{
// 先执行自定义的逻辑
printf("Hook MessageBox: %s - %s\n", lpText, lpCaption);
// 取消HOOK
UnHook();
// 调用原函数
std::string strText(lpText);
std::string strCaption(lpCaption);
strText = strText + " - Hooked";
strCaption = strCaption + " - Hooked";
int rest = MessageBoxA(hWnd, strText.c_str(), strCaption.c_str(), uType);
// 重新HOOK
ReHook();
return rest;
}
static BOOL Hook(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
// 获取需要hook的原函数地址
HMODULE hModule = GetModuleHandleA(pszModuleName);
if (hModule == NULL)
{
printf("GetModuleHandleA failed: %d\n", GetLastError());
return FALSE;
}
GProcFunc = GetProcAddress(hModule, pszFuncName);
if (GProcFunc == NULL)
{
printf("GetProcAddress failed: %d\n", GetLastError());
return FALSE;
}
// 保存原函数头5个字节
DWORD dwOldProtect;
VirtualProtectEx(GetCurrentProcess(), oldHead, FUNC_LEN + 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
SIZE_T nRead;
ReadProcessMemory(GetCurrentProcess(), GProcFunc, oldHead, FUNC_LEN + 1, &nRead);
if (nRead != (FUNC_LEN + 1))
{
printf("ReadProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
// 修改原函数头5个字节为跳转指令 jmp 0x12345678
hookHead[0] = 0xE9;
DWORD_PTR dwOffset = (DWORD_PTR)pfnHookFunc - (DWORD_PTR)GProcFunc - (FUNC_LEN + 1);
memcpy(hookHead + 1, &dwOffset, FUNC_LEN);
printf("jmp address: %llx\n", *(DWORD_PTR*)(hookHead + 1));
// *(DWORD_PTR*)(hookHead + 1) = dwOffset;
// 写入修改后的指令
WriteProcessMemory(GetCurrentProcess(), GProcFunc, hookHead, FUNC_LEN + 1, &nRead);
return TRUE;
}
static BOOL UnHook()
{
if (GProcFunc != NULL)
{
// 恢复原函数头5个字节
DWORD dwOldProtect;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), GProcFunc, FUNC_LEN + 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return FALSE;
}
SIZE_T nWrite;
WriteProcessMemory(GetCurrentProcess(), GProcFunc, oldHead, FUNC_LEN + 1, &nWrite);
if (nWrite != (FUNC_LEN + 1))
{
printf("WriteProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
// 这里直接给 可读可写可执行权限,恢复原权限 测试时候抛出了异常
// VirtualProtectEx(GetCurrentProcess(), GProcFunc, FUNC_LEN + 1, dwOldProtect, &dwOldProtect);
}
return TRUE;
}
static BOOL ReHook()
{
if (GProcFunc != NULL)
{
DWORD dwOldProtect;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), GProcFunc, FUNC_LEN + 1, PAGE_EXECUTE_READWRITE,
&dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return FALSE;
}
SIZE_T nWrite;
WriteProcessMemory(GetCurrentProcess(), GProcFunc, hookHead, FUNC_LEN + 1, &nWrite);
if (nWrite != (FUNC_LEN + 1))
{
printf("WriteProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
// VirtualProtectEx(GetCurrentProcess(), GProcFunc, FUNC_LEN + 1, dwOldProtect, &dwOldProtect);
}
return TRUE;
}
测试如下:
int main()
{
// 先进行Hook
Hook("user32.dll", "MessageBoxA", (PROC)HookMessageBox);
// 调用
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
return 0;
}
64位inline-hook
内联hook的原理如下:
在目标函数头部的插入12byte,来进行无条件跳转到hook函数.
mov rax, hook函数地址 // 48 b8 12 34 56 78 12 34 56 78 (64位地址)
push rax // 50
ret // C3
或者采用jmp rax
mov rax, hook函数地址 // 48 b8 12 34 56 78 12 34 56 78(64位地址)
jmp rax // FF E0
示例代码:
static int WINAPI HookMessageBox64(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType)
{
// 先执行自定义的逻辑
printf("Hook MessageBox: %s - %s\n", lpText, lpCaption);
// 取消HOOK
UnHook64();
// 调用原函数
std::string strText(lpText);
std::string strCaption(lpCaption);
strText = strText + " - Hooked";
strCaption = strCaption + " - Hooked";
int rest = MessageBoxA(hWnd, strText.c_str(), strCaption.c_str(), uType);
// 重新HOOK
ReHook64();
return rest;
}
static PROC GProcFunc64 = NULL;
static BYTE oldHead64[12] = { 0 };
static BYTE hookHead64[12] = { 0 };
static BOOL Hook64(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
// 获取需要hook的原函数地址
HMODULE hModule = GetModuleHandleA(pszModuleName);
if (hModule == NULL)
{
printf("GetModuleHandleA failed: %d\n", GetLastError());
return FALSE;
}
GProcFunc64 = GetProcAddress(hModule, pszFuncName);
if (GProcFunc64 == NULL)
{
printf("GetProcAddress failed: %d\n", GetLastError());
return FALSE;
}
// 保存原函数头12个字节
DWORD dwOldProtect;
VirtualProtectEx(GetCurrentProcess(), GProcFunc64, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
SIZE_T nRead;
ReadProcessMemory(GetCurrentProcess(), GProcFunc64, oldHead64, 12, &nRead);
if (nRead != 12)
{
printf("ReadProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
// 修改原函数头12个字节为跳转指令 mov rax,0x12345678
hookHead64[0] = 0x48;
hookHead64[1] = 0xB8;
// ret 跳转的是内存地址,所以这里存入的是hook函数在内存中的地址
memcpy(hookHead64 + 2, &pfnHookFunc, sizeof(DWORD_PTR));
// *(DWORD_PTR*)(hookHead + 1) = dwOffset;
hookHead64[10] = 0x50; // push rax
hookHead64[11] = 0xC3;// ret
// 写入修改后的指令
WriteProcessMemory(GetCurrentProcess(), GProcFunc64, hookHead64, 12, &nRead);
return TRUE;
}
static BOOL UnHook64()
{
if (GProcFunc64 != NULL)
{
// 恢复原函数头5个字节
DWORD dwOldProtect;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), GProcFunc64, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return FALSE;
}
SIZE_T nWrite;
WriteProcessMemory(GetCurrentProcess(), GProcFunc64, oldHead64, 12, &nWrite);
if (nWrite != 12)
{
printf("WriteProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
// 这里直接给 可读可写可执行权限,恢复原权限 测试时候抛出了异常
// VirtualProtectEx(GetCurrentProcess(), GProcFunc, FUNC_LEN + 1, dwOldProtect, &dwOldProtect);
}
return TRUE;
}
static BOOL ReHook64()
{
if (GProcFunc64 != NULL)
{
DWORD dwOldProtect;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), GProcFunc64, 12, PAGE_EXECUTE_READWRITE,
&dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return FALSE;
}
SIZE_T nWrite;
WriteProcessMemory(GetCurrentProcess(), GProcFunc64, hookHead64, 12, &nWrite);
if (nWrite != 12)
{
printf("WriteProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
// VirtualProtectEx(GetCurrentProcess(), GProcFunc, FUNC_LEN + 1, dwOldProtect, &dwOldProtect);
}
return TRUE;
}
测试代码:
int main()
{
// 先进行Hook
Hook64("user32.dll", "MessageBoxA", (PROC)HookMessageBox64);
// 调用
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
return 0;
}
HotFixHook
与内联hook的原理基本一致,区别是jmp对应的偏移是一个32位的地址.所以可以固定在函数头部偏移5byte(32位),12byte(64位)进行2次跳转来实现hook. 这种好处是对函数入侵小,仅为2byte.实际的跳转逻辑是在函数前面的5-12byte中
jmp 0X123456789 // E9 12 34 56 78
jmp -7 // EB F9
示例代码:
static PROC GProcFunc2 = NULL;
static BYTE oldHead2[2] = { 0 };
static BYTE oldHead2_jmp[5] = { 0 };
static BYTE hookHead2[2] = { 0xEB,0XF9 };// JMP -7
static BYTE hookHead2_jmp[5] = { 0xE9 }; // JMP 0x12345678
static BOOL Hook2(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc);
static BOOL UnHook2();
static BOOL ReHook2();
static int WINAPI HookMessageBox2(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType)
{
// 先执行自定义的逻辑
printf("Hook MessageBox: %s - %s\n", lpText, lpCaption);
// 取消HOOK
UnHook2();
// 调用原函数
std::string strText(lpText);
std::string strCaption(lpCaption);
strText = strText + " - Hooked";
strCaption = strCaption + " - Hooked";
int rest = MessageBoxA(hWnd, strText.c_str(), strCaption.c_str(), uType);
// 重新HOOK
ReHook2();
return rest;
}
static BOOL Hook2(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
// 获取需要hook的原函数地址
HMODULE hModule = GetModuleHandleA(pszModuleName);
if (hModule == NULL)
{
printf("GetModuleHandleA failed: %d\n", GetLastError());
return FALSE;
}
GProcFunc2 = GetProcAddress(hModule, pszFuncName);
if (GProcFunc2 == NULL)
{
printf("GetProcAddress failed: %d\n", GetLastError());
return FALSE;
}
// 保存原函数头2个字节
DWORD dwOldProtect;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), GProcFunc2, 2, PAGE_EXECUTE_READWRITE, &dwOldProtect);
SIZE_T nRead;
ReadProcessMemory(GetCurrentProcess(), GProcFunc2, oldHead2, 2, &nRead);
if (nRead != 2)
{
printf("ReadProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
// jmp -7
VirtualProtectEx(GetCurrentProcess(), GProcFunc2, 2, dwOldProtect, &dwOldProtect);
WriteProcessMemory(GetCurrentProcess(), GProcFunc2, hookHead2, 2, &nRead);
if (nRead != 2)
{
printf("WriteProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
//jmp 0x12345678
LPVOID lpvoid = (LPVOID)((DWORD_PTR)GProcFunc2 - 5);
protect = VirtualProtectEx(GetCurrentProcess(), lpvoid, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
ReadProcessMemory(GetCurrentProcess(), lpvoid, oldHead2_jmp, 5, &nRead);
if (nRead != 5)
{
printf("ReadProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
DWORD_PTR dwOffset = (DWORD_PTR)pfnHookFunc - (DWORD_PTR)GProcFunc2 - 5;
*(DWORD_PTR*)(hookHead2_jmp + 1) = dwOffset;// 等价 memcpy(hookHead2 + 1, &dwOffset, 4);
WriteProcessMemory(GetCurrentProcess(), lpvoid, hookHead2_jmp, 5, &nRead);
if (nRead != 5)
{
printf("WriteProcessMemory failed: %d\n", GetLastError());
return FALSE;
}
return TRUE;
}
static BOOL UnHook2()
{
if (GProcFunc2 != NULL)
{
// 恢复原函数头2个字节
DWORD dwOldProtect;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), GProcFunc2, 2, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return FALSE;
}
SIZE_T nWrite;
WriteProcessMemory(GetCurrentProcess(), GProcFunc2, oldHead2, 2, &nWrite);
VirtualProtectEx(GetCurrentProcess(), GProcFunc2, 2, dwOldProtect, &dwOldProtect);
// 恢复原函数前5字节
protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)((DWORD_PTR)GProcFunc2 - 5), 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx2 failed: %d\n", GetLastError());
return FALSE;
}
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((DWORD_PTR)GProcFunc2 - 5), oldHead2_jmp, 5, &nWrite);
protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)((DWORD_PTR)GProcFunc2 - 5), 5, dwOldProtect, &dwOldProtect);
}
}
static BOOL ReHook2()
{
if (GProcFunc2 != NULL)
{
// 恢复原函数头2个字节
DWORD dwOldProtect;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), GProcFunc2, 2, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return FALSE;
}
SIZE_T nWrite;
WriteProcessMemory(GetCurrentProcess(), GProcFunc2, hookHead2, 2, &nWrite);
VirtualProtectEx(GetCurrentProcess(), GProcFunc2, 2, dwOldProtect, &dwOldProtect);
// 恢复原函数前5字节
protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)((DWORD_PTR)GProcFunc2 - 5), 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx2 failed: %d\n", GetLastError());
return FALSE;
}
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((DWORD_PTR)GProcFunc2 - 5), hookHead2_jmp, 5, &nWrite);
protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)((DWORD_PTR)GProcFunc2 - 5), 5, dwOldProtect, &dwOldProtect);
}
}
测试代码:
int main()
{
// 先进行Hook
Hook2("user32.dll", "MessageBoxA", (PROC)HookMessageBox2);
// 调用
//HookMessageBox2(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
return 0;
}
X64 的思路是一致的.
IATHook(利用导入地址表进行hook)
windows在调用函数时,会通过导入地址表进行动态的内存分配与调用.我们可以修改导入表指向hook函数,来实现对目标函数的hook.
代码示例:
typedef int (WINAPI* FunTargetBox)(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
FunTargetBox oldFunc3 = NULL;
static int WINAPI HookMessageBox3(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType)
{
// 先执行自定义的逻辑
printf("Hook MessageBox: %s - %s\n", lpText, lpCaption);
// 调用原函数
std::string strText(lpText);
std::string strCaption(lpCaption);
strText = strText + " - Hooked";
strCaption = strCaption + " - Hooked";
int rest = oldFunc3(hWnd, strText.c_str(), strCaption.c_str(), uType);
return rest;
}
VOID Hook3(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc);
VOID UnHook3(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc);
VOID Hook3(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
oldFunc3 = (FunTargetBox)GetProcAddress(GetModuleHandleA(pszModuleName), pszFuncName);
// 导入表函数地址获取
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&pNtHeader->OptionalHeader;
DWORD dwImportTableOffset = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
// 导入表
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + dwImportTableOffset);
DWORD* pFirstThunk;
while (pImportTable->Characteristics && pImportTable->FirstThunk != NULL)
{
pFirstThunk = (DWORD*)(pImportTable->FirstThunk + (DWORD)pDosHeader);
while (*(DWORD*)pFirstThunk != NULL)
{
if (*(DWORD*)pFirstThunk == (DWORD)oldFunc3)
{
DWORD dwOldProtect = 0;
VirtualProtect(pFirstThunk, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
DWORD dwFuncAddr = (DWORD)pfnHookFunc;
memcpy(pFirstThunk, (DWORD*)&dwFuncAddr, 4);
VirtualProtect(pFirstThunk, 4, dwOldProtect, &dwOldProtect);
}
// 下一个函数
pFirstThunk++;
}
pImportTable++;
}
}
void UnHook3(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
if (oldFunc3 == NULL)
{
return;
}
// 导入表函数地址获取
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&pNtHeader->OptionalHeader;
DWORD dwImportTableOffset = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
// 导入表
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + dwImportTableOffset);
DWORD* pFirstThunk;
while (pImportTable->Characteristics && pImportTable->FirstThunk != NULL)
{
pFirstThunk = (DWORD*)(pImportTable->FirstThunk + (DWORD)pDosHeader);
while (*(DWORD*)pFirstThunk != NULL)
{
if (*(DWORD*)pFirstThunk == (DWORD)pfnHookFunc)
{
DWORD dwOldProtect = 0;
VirtualProtect(pFirstThunk, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
DWORD dwFuncAddr = (DWORD)oldFunc3;
memcpy(pFirstThunk, (DWORD*)&dwFuncAddr, 4);
VirtualProtect(pFirstThunk, 4, dwOldProtect, &dwOldProtect);
}
// 下一个函数
pFirstThunk++;
}
pImportTable++;
}
}
测试代码
int main()
{
// 先进行Hook
Hook3("user32.dll", "MessageBoxA", (PROC)HookMessageBox3);
// 调用
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
UnHook3("user32.dll", "MessageBoxA", (PROC)HookMessageBox3);
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
return 0;
}
X64的代码类似,只需要考虑地址位是8字节即可.
VEH-INT3(UD2/0)-Hook
我们像windows 注册一个向量异常处理程序,然后修改目标函数头部int 3或者ud2或者除0,手动触发一个异常,然后在捕获程序中,进行跳过来实现hook
这里,ESP 进行了4次push.对应的是MessageBoxA的4个参数.
这里可以看到,每次压入的是4字节,对应地址长度.字符串存储的是内存地址.
示例代码:
// 向量异常的句柄
PVOID vehExceptionHandler = NULL;
DWORD_PTR oldFun4 = NULL;
BYTE oldHead4[2] = { 0 };
// 向量异常处理函数
LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* pExceptionInfo)
{
// 获取异常代码
DWORD dwExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
if (EXCEPTION_BREAKPOINT != dwExceptionCode && EXCEPTION_ILLEGAL_INSTRUCTION != dwExceptionCode)
{
// 不是int 3 异常,继续搜索
return EXCEPTION_CONTINUE_SEARCH;
}
// 获取异常地址
if ((DWORD)pExceptionInfo->ExceptionRecord->ExceptionAddress != oldFun4)
{
// 不是目标地址,继续搜索
return EXCEPTION_CONTINUE_SEARCH;
}
// 这里需要解析目标函数,来进行取参,对应messagebox函数. X86 与X64 解析规则不一样,可以针对函数进行调试
// EIP是指令地址,ESP 可以用来获取参数
DWORD_PTR* stackPointer = (DWORD_PTR*)(pExceptionInfo->ContextRecord->Esp);
// stackPointer[4] ==> uType
// stackPointer[3] ==> lpCaption
// stackPointer[2] ==> lpText
// stackPointer[1] ==> hWnd
LPCSTR lpText = (char*)(stackPointer[2]);
std::string strText(lpText);
LPCSTR lpCaption = (char*)(stackPointer[3]);
std::string strCaption(lpCaption);
printf("Hook MessageBox: %s - %s\n", lpText, lpCaption);
const char* newText = strText.c_str();
*(stackPointer + 2) = (DWORD_PTR)(&"hook-Msg");
const char* newCap = strCaption.c_str();
*(stackPointer + 3) = (DWORD_PTR)(&"hook-Title");
// 跳过插入的int 3 的指令
pExceptionInfo->ContextRecord->Eip += 2;
// 继续执行,指向异常发生的指令,重新尝试执行
return EXCEPTION_CONTINUE_EXECUTION;
}
VOID Hook4(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
oldFun4 = (size_t)GetProcAddress(GetModuleHandleA(pszModuleName), pszFuncName);
// 修改函数第1个字节为0xCC
DWORD dwOldProtect = 0;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)oldFun4, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return;
}
SIZE_T nRead = 0;
ReadProcessMemory(GetCurrentProcess(), (LPVOID)oldFun4, oldHead4, 2, &nRead);
//oldHead4[0] = *(UCHAR*)oldFun4;
//oldHead4[1] = *((UCHAR*)oldFun4 + 1);
//*(UCHAR*)oldFun4 = 0xCC;//0xCC int 3 是visual 调试器拦截的异常.
BYTE ud2[2] = { 0x0F, 0x0B };// ud2 触发EXCEPTION_ILLEGAL_INSTRUCTION 异常.
WriteProcessMemory(GetCurrentProcess(), (LPVOID)oldFun4, ud2, 2, &nRead);
protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)oldFun4, 1, dwOldProtect, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return;
}
// 插入异常处理
vehExceptionHandler = AddVectoredExceptionHandler(TRUE, ExceptionHandler);
}
VOID UnHook4(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
if (oldFun4 == NULL)
{
return;
}
// 移除异常处理
if (vehExceptionHandler != NULL)
{
RemoveVectoredExceptionHandler(vehExceptionHandler);
}
// 恢复函数的第一个字节
DWORD dwOldProtect = 0;
BOOL protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)oldFun4, 2, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (protect == FALSE)
{
printf("VirtualProtectEx failed: %d\n", GetLastError());
return;
}
((UCHAR*)oldFun4)[0] = oldHead4[0];
((UCHAR*)oldFun4)[1] = oldHead4[1];
protect = VirtualProtectEx(GetCurrentProcess(), (LPVOID)oldFun4, 2, dwOldProtect, &dwOldProtect);
}
测试代码
int main()
{
// 先进行Hook
Hook4("user32.dll", "MessageBoxA", (PROC)HookMessageBox3);
// 调用
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
UnHook4("user32.dll", "MessageBoxA", (PROC)HookMessageBox3);
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
return 0;
}
VEH-Debug Registers (利用调试器断点进行hook)
cpu有4个调试寄存器:
- DR0:通常用于第一个硬件断点。
- DR1:通常用于第二个硬件断点。
- DR2:通常用于第三个硬件断点。
- DR3:通常用于第四个硬件断点。
利用SetThreadContext 设置线程的断点调试.
DR0-DR3:用于设置断点地址。(在何处设置断点)DR6和DR7:用于获取调试状态和控制调试行为。
DR7 含义(转为二进制):
- 位 0 - 1:
DR0(启用/禁用) - 位 1 - 0:
DR1(启用/禁用) - 位 2 - 1:
DR2(启用/禁用) - 位 3 - 0:
DR3(启用/禁用) - 位 16-17 - 00:
DR0(执行断点/不执行断点) - 位 18-19 - 00:
DR1(执行断点/不执行断点) - 位 20-21 - 00:
DR2(执行断点/不执行断点) - 位 22-23 - 00:
DR3(执行断点/不执行断点)
示例代码
// 利用调试器进行Hook
DWORD_PTR oldFun5 = NULL;
PVOID vehExceptionHandler2 = NULL;
LONG WINAPI ExceptionHandler2(EXCEPTION_POINTERS* pExceptionInfo)
{
// 获取异常代码
DWORD dwExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
if (EXCEPTION_BREAKPOINT != dwExceptionCode && EXCEPTION_SINGLE_STEP != dwExceptionCode)
{
// 不是int 3 异常,继续搜索
return EXCEPTION_CONTINUE_SEARCH;
}
// 获取异常地址
if ((DWORD_PTR)(pExceptionInfo->ExceptionRecord->ExceptionAddress) != oldFun5)
{
// 不是目标地址,继续搜索
return EXCEPTION_CONTINUE_SEARCH;
}
DWORD_PTR* stackPointer = (DWORD_PTR*)(pExceptionInfo->ContextRecord->Esp);
LPCSTR lpText = (char*)stackPointer[2];
std::string strText(lpText);
LPCSTR lpCaption = (char*)stackPointer[3];
std::string strCaption(lpCaption);
printf("Hook MessageBox: %s - %s\n", lpText, lpCaption);
const char* newText = strText.c_str();
*(stackPointer + 2) = (DWORD_PTR)(&"hook-Msg");
const char* newCap = strCaption.c_str();
*(stackPointer + 3) = (DWORD_PTR)(&"hook-Title");
// 跳过插入的int 3 的指令
pExceptionInfo->ContextRecord->Eip += 2;
// 继续执行,指向异常发生的指令,重新尝试执行
return EXCEPTION_CONTINUE_EXECUTION;
}
// 设置断点
VOID SetThreadBreakpoints(HANDLE hThread, DWORD_PTR funcAddr)
{
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(hThread, &ctx);
// 设置函数入口处断点
ctx.Dr0 = funcAddr;
ctx.Dr7 = 0x1;// DR0 寄存器设置断点
SetThreadContext(hThread, &ctx);
}
// 取消设置断点
VOID RemoveThreadBreakpoints(HANDLE hThread, DWORD_PTR funcAddr)
{
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(hThread, &ctx);
// 设置函数入口处断点
ctx.Dr0 = funcAddr;
ctx.Dr7 = 0x0;// DR0 寄存器设置断点
SetThreadContext(hThread, &ctx);
}
void Hook5(const char* pszModuleName, const char* pszFuncName, PROC pfnHookFunc)
{
oldFun5 = (DWORD_PTR)GetProcAddress(GetModuleHandleA(pszModuleName), pszFuncName);
// add VEH异常处理
vehExceptionHandler2 = AddVectoredExceptionHandler(TRUE, ExceptionHandler2);
// 创建快照
HANDLE hThreadShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
THREADENTRY32* ThreadInfo = new THREADENTRY32;
ThreadInfo->dwSize = sizeof(THREADENTRY32);
HANDLE hThread = NULL;
// 遍历线程
Thread32First(hThreadShot, ThreadInfo);
while (Thread32Next(hThreadShot, ThreadInfo))
{
if (GetCurrentProcessId() == ThreadInfo->th32OwnerProcessID)
{
// 打开线程
hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadInfo->th32ThreadID);
// 设置断点
SetThreadBreakpoints(hThread, oldFun5);
CloseHandle(hThread);
}
}
}
测试代码
int main()
{
// 先进行Hook
Hook5("user32.dll", "MessageBoxA", (PROC)HookMessageBox3);
// 调用
MessageBoxA(NULL, "测试box", "啊啊啊", MB_OK | MB_HELP);
return 0;
}
剩下的还有内核层的hook, 虚拟化hook,未完待续.....