Inline_hook

InlineHook

原理介绍

上次打考核赛的时候遇到的一道题,不过当时是全凭做题经验写出来的,现在正好实现一下(都是在32位下的,64位下的没有去实现)

inline hook (内联钩子) 是一种在程序运行时修改函数执行流程的技术。他通过修改函数的原始代码,将目标函数的执行路径重定向到自定义的代码段,从而实现对目标函数的拦截与修改(也就是hook)

那该如何去实现跳转呢

这里有两种方式

第一种就是用jmp,也就是相对地址的方式,

1
2
3
__NewCode[0] = 0xE9; // jmp 相对偏移
DWORD relativeAddr = (DWORD)fun1 - (DWORD)fun2 - 5; //计算相对地址
memcpy(&__NewCode[1], &relativeAddr, 4);

也就是相当于

1
2
jmp fun1

这种的话,就需要找合适的地方有5个字节的,如果长了,就会使后面的机器码,去形成指令去配对,从而就会导致后面的内容都会乱掉,所以我们通常使用绝对寻址的方式去实现

1
2
3
4
__NewCode[0] = 0xB8;           // mov eax, JmpAddress
memcpy(&__NewCode[1], &JmpAddress, 4);
__NewCode[5] = 0xFF; // jmp eax
__NewCode[6] = 0xE0; //

也就相当于

1
2
mov eax, fun1
jmp eax

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <windows.h>
#include <cstdio>
using namespace std;
//这段代码定义了一个函数指针类型,用于指向具有特定签名的函数
//typedef:用于创建类型别名。
//void(*PFUNMSG)(LPCWSTR szMsg, LPCWSTR Title);:
//void(*):表示返回类型为 void 的函数指针。
//LPCWSTR szMsg 和 LPCWSTR Title:表示该函数有两个参数,都是 LPCWSTR(即 const wchar_t* ,用于表示宽字符字符串)。
//PFUNMSG:给这个函数指针类型取了一个别名,方便后续使用。

typedef VOID(*PFUNMSG)(LPCWSTR szMsg, LPCWSTR Title);
HMODULE hModule;
PFUNMSG Crypto;
BYTE _NewCode[7] = { 0xe9,0x0,0x0,0x0,0x0,0x0 };
BYTE _OldCode[7] = { 0 };
int fun2();
int fun1();
void InlineHook();
void InlineHook() {
//读取原始的7字节以便以后恢复
memcpy(_OldCode, fun2, 7);
DWORD JmpAddress = (DWORD)fun1;
//构造新指令:mov eax,fun1; jmp eax
_NewCode[0] = 0xB8; //mov eax,addr
memcpy(&_NewCode[1], &JmpAddress, 4);
_NewCode[5] = 0xff;
_NewCode[6] = 0xE0; //jmp eax
DWORD dwOldProtect;
VirtualProtect(fun2, 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(fun2, _NewCode, 7);
//还原内存保护
VirtualProtect(fun2, 7, dwOldProtect, &dwOldProtect);
}
int main() {
InlineHook();
fun2();
}
int fun2() {
puts("Hello world");
return 0;
}
int fun1() {
puts("Obaby girl 别叫我达芬奇");
return 0;
}

也正好写一下当时考核赛那道题的wp

写一道类似的wp

打开主函数

image-20250319214323105

在sub_4112B2 函数中

image-20250319214802425

其实我们也已经可以看见我们要自定义的代码段了,总感觉要出题的话得加点混淆

但是我们先按正常的分析,因为知道是被hook了所以我们得启用动态调试了

image-20250319215015327

下好断点

image-20250319215048171

当步入到tea这个函数时f7步入

image-20250319215140689

发现我们说的mov eax fun1 了(绝对寻址),然后再步入

image-20250319215219359

发现jmp eax ,所以下一个步入就是我们的自定义的代码段

image-20250319215254044

解开之后

image-20250319215345256

得到真正的加密的地方,然后写出代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>	
using namespace std;
#include <bits/stdc++.h>
void decrypt (uint32_t *v ,int *key){
uint32_t v0=v[0]^3;
uint32_t v1=v[1]^4;
uint32_t delta = 0x61C88647;
uint32_t sum= -(delta * 32) ;
for(int i=0;i<32;i++){
v1 -=(key[3] + (v0 >> 5)) ^ (sum + v0) ^ (key[2] + 16 * v0);
v0 -= (key[1] + (v1 >> 5)) ^ (sum + v1) ^ (*key + 16 * v1);
sum += delta;
}
v[0]=v0^1;
v[1]=v1^2;
}
int main (){
uint32_t enc[14]={0xBDF30C56,0x86C9E780,0xCD2D53F2,0x47FDE322,0x1F8B1C9F,0x159683FF,0x21816944,0x36850959,
0xB8F837D1,0xFAB82602,0x471CF06D,0x4287B2DA};
//0x560CF3BD,0X80E7C986,0XF2532DCD,0X22E3FD47,0X9F1C8B1F,0XFF839615,0X44698121,0X59098536,
// 0XD137F8B8,0X0226B8FA,0X6DF01C47,0XDAB28742
int key[4]={0x114514,0x1919810,0x5201314,0x7355608
};//0X14451100,0X10989101,0X14132005,0x08563507
for(int i=0;i<=10;i+=2){
decrypt(&enc[i],key);
}
//for(int i=0;i<4;i++){
// printf("%lx",enc[i]);
// }
cout<<(char *)enc<<endl;
return 0;
}