見字如面,我是東北碼農。
上文我們介紹了硬體斷點,大家可以回顧一下。本文将介紹使用硬體斷點+veh,實作硬體斷點hook。
關注後,聊天框回複“硬體斷點hook”,可以擷取本文源碼。
1、不修改原函數hook
硬體斷點這個手法,我還是分析外挂時學到的。開始做反外挂時候,隻會用xuetr、pchunter等工具檢視遊戲程序的inlinehook,分析修改了遊戲的哪些代碼,然後再防禦。然後有一天,我發現了一個外挂不按套路出牌,沒有修改任何代碼實作了透視功能,我就很納悶這個外挂是怎麼實作的呢,不改代碼我怎麼防啊?
後來經過不斷學習,才知道hook的方式有很多種,硬體斷點hook就是其中一種比較隐蔽的hook方式,可以在不修改源函數的前提實作hook,這一點比inlinehook強。
2、VEH(Vectored Exception Handling)
2.1、VEH介紹
我們先來介紹一下VEH,先放一段微軟官方描述。
參考資料:https://docs.microsoft.com/en-us/windows/win32/debug/vectored-exception-handling
Vectored exception handlers are an extension to structured exception handling. An application can register a function to watch or handle all exceptions for the application. Vectored handlers are not frame-based, therefore, you can add a handler that will be called regardless of where you are in a call frame. Vectored handlers are called in the order that they were added, after the debugger gets a first chance notification, but before the system begins unwinding the stack.
大概意思是,程式可以注冊異常回調函數連結清單。當程式觸發異常時,回調函數就會按照使用者指定的順序依次調用。
2.2、VEH 使用
向VEH鍊注冊一個異常處理函數,可以使用windows提供的API,
PVOID AddVectoredContinueHandler(
ULONG First,
PVECTORED_EXCEPTION_HANDLER Handler
);
- First:是否插入VEH鍊頭部。
- Handler:異常處理函數。
接下來看一下異常處理函數,PVECTORED_EXCEPTION_HANDLER 的定義:
LONG PvectoredExceptionHandler(
[in] _EXCEPTION_POINTERS *ExceptionInfo
)
唯一的參數_EXCEPTION_POINTERS指向異常資訊,定義如下:
定義如下:
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;// 異常資訊記錄
PCONTEXT ContextRecord;// 寄存器資訊
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
異常處理函數的傳回值很重要,控制着異常處理的後續行為:
To return control to the point at which the exception occurred, return EXCEPTION_CONTINUE_EXECUTION (0xffffffff). To continue the handler search, return EXCEPTION_CONTINUE_SEARCH (0x0).
- 傳回-1:異常已處理,繼續執行;
- 傳回0:繼續調用VEH鍊的其它處理函數。
3、硬體斷點 hook 實戰代碼
介紹完硬體斷點和veh後,我們就可以實作硬體斷點hook了。我們先介紹一下思路:
- 設定VEH鍊異常處理函數。
- 設定硬體斷點,監控原函數的執行事件。
- 執行原函數時,會觸發異常調用我們的異常處理函數。
- 異常處理函數判斷,如果是原函數位址,則修改rip寄存器,跳轉到hook函數。
3.1、設定VEH鍊異常處理函數
異常處理函數,xx_hw_bp_veh的實作一會再介紹。
static bool xx_init_hwbp_hook() {
return xx_add_veh(CALL_FIRST, xx_hw_bp_veh);
}
3.2、設定硬體斷點
class xx_hw_bp
{
public:
struct bp_info {
void* src_ = nullptr;
void* dst_ = nullptr;
};
public:
void hook(void* src,void* dst,HANDLE thread,int idx)
{
//記錄原函數與hook函數關系。
// TODO idx check
info_[idx].src_ = src;
info_[idx].dst_ = dst;
// 設定硬體斷點
xx_set_hw_bp(thread, idx, src, RW_EXE);
}
// 擷取原函數與hook函數映射
void* get_dst(void* src) {
for (int i = 0; i < 4; ++i) {
if (src == info_[i].src_)
{
return info_[i].dst_;
}
}
return nullptr;
}
bp_info info_[4];
};
xx_hw_bp g_hwbp_;// 全局類,友善異常處理函數通路
先記錄一下原函數和hook函數的映射關系,再使用上文硬體斷點封裝好的xx_set_hw_bp設定硬體斷點。
3.3、異常處理函數實作
LONG NTAPI xx_hw_bp_veh(
struct _EXCEPTION_POINTERS* ExceptionInfo)
{
// 擷取異常位址
void* src = ExceptionInfo->ExceptionRecord->ExceptionAddress;
// 查找對應關系
void* dst = g_hwbp_.get_dst(src);
if (nullptr != dst) {
// 修改rip 跳轉
memcpy(&ExceptionInfo->ContextRecord->Rip, &dst, sizeof(void*));
return EXCEPTION_CONTINUE_EXECUTION;
}
// 其它異常,繼續veh鍊處理
return EXCEPTION_CONTINUE_SEARCH;
}
先通過異常位址,找到對應的hook函數位址。若找到,則修改Rip(指令寄存器)實作跳轉;找不到則不是我們關心的異常,交給VEH鍊繼續處理吧。
4、demo驗證
int WINAPI My_MessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType)
{
printf("[call %s]!\n", __FUNCTION__);
auto ori = xx_trampoline_to_func(&MessageBoxA, trampoline);
return (*ori)(hWnd, lpText, lpCaption, uType);
}
void test_hook() {
// 制作跳闆,隻需要複制1條指令,跳過硬體斷點即可
xx_mem_unprotect(trampoline, 1024);
xx_make_trampoline(&MessageBoxA, trampoline, 4);
xx_init_hwbp_hook();
g_hwbp_.hook(&MessageBoxA, &My_MessageBoxA,GetCurrentThread(), 0);
::MessageBoxA(0, "aaa", "bbb", 0);
}
使用時先制作跳闆,再hook。制作跳闆複用inlinehook的代碼。由于不修改代碼,是以跳闆值需要複制一條指令即可。複制的代碼少,是以也基本不用考慮重定位問題。
歡迎大家點贊、轉發、再看、留言交流~