hook 介紹
hook英文意思是鉤子,hook可以在程序已經(jīng)編譯成bin文件,甚至執(zhí)行時(shí),修改函數(shù)流程。先上一段簡(jiǎn)單代碼,讓大家對(duì)hook有個(gè)簡(jiǎn)單認(rèn)識(shí)。
#define XX_JMP_OPCODE 0xE9#pragma pack(push, 1)struct xxhook_jmp32 { uint8_t opcode ; int32_t offset;};#pragma pack(pop)// 設(shè)置鉤子static bool xx_setjmp(void* src, void* dst) { xxhook_jmp32* jmp = (xxhook_jmp32*)src; jmp->opcode = 0xE9; jmp->offset = (char*)dst - ((char*)src + sizeof(xxhook_jmp32)) ; return true;}
例子1:hook 用戶函數(shù)
void func1() { printf("func1\n");}void func2() { printf("func2\n");}void test_hook_my_func() { func1(); // 使代碼區(qū)可寫 DWORD old_pro = 0; BOOL ret = VirtualProtect(&func1, 4096, PAGE_EXECUTE_READWRITE, &old_pro); // hook! xx_setjmp(&func1, &func2); func1();}
執(zhí)行后輸出
func1func2
例子2:hook C函數(shù)
static __inline time_t __CRTDECL my_time(_Out_opt_ time_t* const _Time) { return 1234;}void test_hook_cfunc() { printf("time=%u\n",time(NULL)); DWORD old_pro = 0; BOOL ret = VirtualProtect(&time, 4096, PAGE_EXECUTE_READWRITE, &old_pro); xx_setjmp(&time, &my_time); printf("time=%u\n", time(NULL));}
輸出
time=1632322864time=1234
可以看出,函數(shù)的執(zhí)行邏輯已經(jīng)改變了。
hook代碼解析
hook的本質(zhì),就是修改原代碼,在跳轉(zhuǎn)到我們的函數(shù),我們對(duì)比一下例子2中hook前后的time函數(shù)匯編代碼變化。
hook前
static __inline time_t __CRTDECL time( _Out_opt_ time_t* const _Time ) {00007FF6DA4B1290 48 89 4C 24 08 mov qword ptr [rsp+8],rcx 00007FF6DA4B1295 48 83 EC 28 sub rsp,28h return _time64(_Time);00007FF6DA4B1299 48 8B 4C 24 30 mov rcx,qword ptr [_Time] 00007FF6DA4B129E FF 15 FC 1E 00 00 call qword ptr [__imp__time64 (07FF6DA4B31A0h)] }00007FF6DA4B12A4 48 83 C4 28 add rsp,28h 00007FF6DA4B12A8 C3 ret
hook后
static __inline time_t __CRTDECL time( _Out_opt_ time_t* const _Time ) {00007FF6DA4B1290 E9 AB FD FF FF jmp my_time (07FF6DA4B1040h) 00007FF6DA4B1295 48 83 EC 28 sub rsp,28h return _time64(_Time);00007FF6DA4B1299 48 8B 4C 24 30 mov rcx,qword ptr [_Time] 00007FF6DA4B129E FF 15 FC 1E 00 00 call qword ptr [__imp__time64 (07FF6DA4B31A0h)] }00007FF6DA4B12A4 48 83 C4 28 add rsp,28h 00007FF6DA4B12A8 C3 ret
可以看出,函數(shù)第一行就是跳轉(zhuǎn)到我們的函數(shù)的jump語(yǔ)句。
我們?cè)賮砘仡櫼幌滦薷牡拇a,12行就是賦值0xe9代表是跳轉(zhuǎn)語(yǔ)句,接下來4個(gè)字節(jié)就是跳轉(zhuǎn)的offset,公式為:dst-(src+5)。5是本條jmp語(yǔ)句的長(zhǎng)度。
#define XX_JMP_OPCODE 0xE9#pragma pack(push, 1)struct xxhook_jmp32 { uint8_t opcode ; int32_t offset;};#pragma pack(pop)static bool xx_setjmp(void* src, void* dst) { xxhook_jmp32* jmp = (xxhook_jmp32*)src; jmp->opcode = 0xE9; jmp->offset = (char*)dst - ((char*)src + sizeof(xxhook_jmp32)) ; return true;}
hook技術(shù)看這一篇為什么不夠
有一些問題我們還沒解決
原函數(shù)已經(jīng)被破壞,第一句改成了jmp,如果還想調(diào)用原函數(shù)怎么辦?
64bit操作系統(tǒng),如果原函數(shù)和目標(biāo)函數(shù)的offset超過uint32-max怎么辦?
hook時(shí),有線程正在執(zhí)行此函數(shù)怎么辦?
linux如何hook?
hook還有哪些用處?