Hook 技術常被叫做掛鈎技術,掛鈎技術其實早在DOS時代就已經存在了,該技術是Windows系統用於替代DOS中斷機制的具體實現,鈎子的含義就是在程序還沒有調用系統函數之前,鈎子捕獲調用消息並獲得控制權,在執行系統調用之前執行自身程序,簡單來說就是函數劫持.
接着來研究一下64位程序的Hook,64位與32位系統之間無論從尋址方式,還是語法規則都與x32架構有着本質的不同,所以上面的使用技巧只適用於32位程序,注入32位進程使用,下面的內容則是64位下手動完成Hook掛鈎的一些操作手法,由於64位編譯器無法直接內嵌匯編代碼,導致我們只能調用C庫函數來實現Hook的中轉.
該筆記是針對64位Hook的簡易封裝,自己留着也沒什么意思,還是分享出來吧,轉載請加出處,謝謝!
簡單實現64位Hook去彈窗: 由於64位編譯器無法直接內嵌匯編代碼,所以在我們需要Hook時只能將跳轉機器碼以二進制字節方式寫死在程序里,如下代碼是一段簡單的演示案例,主要實現了去彈窗的功能.
#include <stdio.h>
#include <Windows.h>
BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
DWORD_PTR base;
int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
return 1;
}
int main(int argc,char * argv[])
{
HMODULE hwnd = GetModuleHandle(TEXT("user32.dll"));
DWORD_PTR base = (DWORD_PTR)GetProcAddress(hwnd, "MessageBoxW");
DWORD OldProtect;
if (VirtualProtect((LPVOID)base, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(OldCode, (LPVOID)base, 12); // 拷貝原始機器碼指令
*(PINT64)(HookCode + 2) = (INT64)&MyMessageBoxW; // 填充90為指定跳轉地址
}
memcpy((LPVOID)base, &HookCode, sizeof(HookCode)); // 拷貝Hook機器指令
MessageBoxW(NULL, L"hello lyshark", NULL, NULL);
return 0;
}
64位Hook代碼完整版: 接着我們在上面代碼的基礎上,繼續進行完善,添加恢復鈎子的功能,該功能時必須要有的,因為我們還是需要調用原始的彈窗代碼,所以要在調用時進行暫時恢復,調用結束后再繼續Hook掛鈎.
#include <stdio.h>
#include <Windows.h>
void Hook();
void UnHook();
BYTE Ori_Code[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
/* Hook 機器碼的原理如下所示
MOV RAX, 0x9090909090909090
JMP RAX
*/
static int (WINAPI *OldMessageBoxW)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) = MessageBoxW;
int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
UnHook(); // 恢復Hook
int ret = OldMessageBoxW(hWnd, TEXT("Hook Inject"), lpCaption, uType); // 調用原函數
Hook(); // 繼續hook
return ret;
}
void Hook()
{
DWORD OldProtect;
if (VirtualProtect(OldMessageBoxW, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(Ori_Code, OldMessageBoxW, 12); // 拷貝原始機器碼指令
*(PINT64)(HookCode + 2) = (INT64)&MyMessageBoxW; // 填充90為指定跳轉地址
}
memcpy(OldMessageBoxW, &HookCode, sizeof(HookCode)); // 拷貝Hook機器指令
}
void UnHook()
{
memcpy(OldMessageBoxW, &Ori_Code, sizeof(Ori_Code)); // 恢復hook原始代碼
}
int main(int argc, char *argv [])
{
Hook();
MessageBoxW(NULL, TEXT("hello lyshark"), TEXT("MsgBox"), MB_OK);
UnHook();
MessageBoxW(NULL, TEXT("hello lyshark"), TEXT("MsgBox"), MB_OK);
return 0;
}
完整版DLL注入鈎子: 最后將上面所寫的代碼進行封裝,實現一個完整的鈎子處理程序,代碼如下.
#include <stdio.h>
#include <Windows.h>
BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
void Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
DWORD OldProtect = 0;
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(OldCode, (LPVOID)FuncAddress, 12); // 拷貝原始機器碼指令
*(PINT64)(HookCode + 2) = (UINT64)lpFunction; // 填充90為指定跳轉地址
}
memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode)); // 拷貝Hook機器指令
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
}
void UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
DWORD OldProtect = 0;
UINT64 FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy((LPVOID)FuncAddress, OldCode, sizeof(OldCode));
}
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
}
int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
// 首先恢復Hook代碼
UnHook(L"user32.dll", "MessageBoxW");
int ret = MessageBoxW(0, L"hello lyshark", lpCaption, uType);
// 調用結束后,再次掛鈎
Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
return ret;
}
bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
switch (dword)
{
case DLL_PROCESS_ATTACH:
Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
break;
case DLL_PROCESS_DETACH:
UnHook(L"user32.dll", "MessageBoxW");
break;
}
return true;
}
針對Hook代碼的封裝: 上方的代碼還是基於過程化的案例,為了能更加通用,我們將其封裝成類,這樣后期可以直接調用了.
// hook.h
#pragma once
#include <Windows.h>
#ifdef __cplusplus
extern "C"{
#endif
class MyHook
{
public:
FARPROC m_pfnOrig; // 保存函數地址
BYTE m_bOldBytes[12]; // 保存函數入口代碼
BYTE m_bNewBytes[12]; // 保存Inlie Hook代碼
public:
MyHook();
~MyHook();
BOOL Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction);
BOOL UnHook(LPCWSTR lpModule, LPCSTR lpFuncName);
};
#ifdef __cplusplus
}
#endif
// hook.cpp
#include "hook.h"
MyHook::MyHook()
{
BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE NewCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
RtlMoveMemory(MyHook::m_bNewBytes, NewCode, 12);
memset(MyHook::m_bOldBytes, 0, 12);
m_pfnOrig = NULL;
}
MyHook::~MyHook()
{
m_pfnOrig = NULL;
ZeroMemory(MyHook::m_bNewBytes, 12);
ZeroMemory(MyHook::m_bOldBytes, 12);
}
BOOL MyHook::Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
DWORD OldProtect = 0;
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(m_bOldBytes, (LPVOID)FuncAddress, 12); // 拷貝原始機器碼指令
*(PINT64)(MyHook::m_bNewBytes + 2) = (UINT64)lpFunction; // 填充90為指定跳轉地址
}
memcpy((LPVOID)FuncAddress, &m_bNewBytes, sizeof(m_bNewBytes)); // 拷貝Hook機器指令
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
return TRUE;
}
BOOL MyHook::UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
DWORD OldProtect = 0;
UINT64 FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy((LPVOID)FuncAddress, m_bOldBytes, sizeof(m_bOldBytes));
}
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
return TRUE;
}
// main.cpp
#include <Windows.h>
#include "hook.h"
MyHook MsgHook;
int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
// 首先恢復Hook代碼
MsgHook.UnHook(L"user32.dll", "MessageBoxW");
int ret = MessageBoxW(0, L"Hook Inject", lpCaption, uType);
// 調用結束后,再次掛鈎
MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
return ret;
}
int main(int argc, char * argv[])
{
// 開始Hook
MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
MessageBoxW(NULL, L"hello lyshark", L"Msg", MB_OK);
// 結束Hook
MsgHook.UnHook(L"user32.dll", "MessageBoxW");
return 0;
}
第二種封裝方式: 接着我們將代碼聲明與實現合在一起,實現第二種封裝方式.
// hook.h
#include <Windows.h>
class MyHook
{
public:
static UINT64 Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
UINT64 dwAddr = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
BYTE jmp[] =
{
0x48, 0xb8, // jmp
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // address
0x50, 0xc3 // retn
};
ReadProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, MemoryAddress(), 12, 0);
UINT64 dwCalc = (UINT64)lpFunction;
memcpy(&jmp[2], &dwCalc, 8);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, jmp, 12, nullptr);
return dwAddr;
}
static BOOL UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
UINT64 dwAddr = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, MemoryAddress(), 12, 0))
return TRUE;
return FALSE;
}
static BYTE* MemoryAddress()
{
static BYTE backup[12];
return backup;
}
};
// main.cpp
#include <windows.h>
#include "hook.h"
MyHook MsgHook;
static int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
MsgHook.UnHook(L"user32.dll", "MessageBoxW");
int ret = MessageBox(hWnd, L"Hook Inject", lpCaption, uType);
MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
return ret;
}
int main(int argc, char * argv[])
{
MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
MessageBoxW(NULL, L"hello lyshark", L"MsgBox", MB_OK);
MsgHook.UnHook(L"user32.dll", "MessageBoxW");
MessageBoxW(NULL, L"hello lyshark", L"MsgBox", MB_OK);
return 0;
}