hook 介绍
hook英文意思是钩子,hook可以在程序已经编译成bin文件,甚至执行时,修改函数流程。先上一段简单代码,让大家对hook有个简单认识。
#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;
}
例子1:hook 用户函数
void func1() {
printf("func1\n");
}
void func2() {
printf("func2\n");
}
void test_hook_my_func() {
func1();
// 使代码区可写
DWORD old_pro = 0;
BOOL ret = VirtualProtect(&func1, 4096, PAGE_EXECUTE_READWRITE, &old_pro);
// hook!
xx_setjmp(&func1, &func2);
func1();
}
执行后输出
func1
func2
例子2:hook C函数
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=1632322864
time=1234
可以看出,函数的执行逻辑已经改变了。
hook代码解析
hook的本质,就是修改原代码,在跳转到我们的函数,我们对比一下例子2中hook前后的time函数汇编代码变化。
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
可以看出,函数第一行就是跳转到我们的函数的jump语句。
我们再来回顾一下修改的代码,12行就是赋值0xe9代表是跳转语句,接下来4个字节就是跳转的offset,公式为:dst-(src+5)。5是本条jmp语句的长度。
#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技术看这一篇为什么不够
有一些问题我们还没解决
- 原函数已经被破坏,第一句改成了jmp,如果还想调用原函数怎么办?
- 64bit操作系统,如果原函数和目标函数的offset超过uint32-max怎么办?
- hook时,有线程正在执行此函数怎么办?
- linux如何hook?
- hook还有哪些用处?
别着急,接下来的文章都会介绍。
文章的代码
https://gitee.com/northeast_coder/code/tree/master/case/case8_hook_overview
全网同名,欢迎关注、转发、点赞、再看~ |