HOOK-Inline Hook

本文最后更新于:2021-08-09 晚上

Inline Hook

Inline Hook是Hook技术的一种,它是通过修改机器码来实现HOOK。当我们正常调用一个API函数时,正常的流程是,call API,然后就会到函数内部执行。我们写下如下语句,在OD中打开就是如图的样子。可以看到先压入参数,然后调用MessageBoxA函数。

1
MessageBoxA(NULL, "这是本来的窗口", "未被HOOK", MB_OK);

进入函数可以看到函数的具体

Inline Hook就是了通过字节码更改了函数的流程,刚才我们看到的是正常的流程,如果调用正常函数的时候,我们修改它的执行流程,使其跳转到我们自定义的函数内部去执行就可以实现Inlie HOOK。

如何实现

通过上面我们知道了,我们需要修改的就是程序的执行流程,而改变程序的执行流程就是jmp、call等,我们用最常见的jmp来进行分析。

如图可以看到一条JMP指令,看到后面的地址是0x401254,再看旁边的机器码是E9 EF000000。我们知道E9是JMP的机器码,那么EF000000为什么和要跳转的地址不一样呢。这是因为在JMP后面使用的是一个偏移量而不是一个具体的地址。如果使用地址的话,数据重定位之后,每次都会变,但是使用偏移就不一样了。

1
00401160   . /E9 EF000000        jmp InlineHo.00401254

JMP后的偏移计算公式是:

偏移=目的地址-原地址-5

5是JMP指令的长度,如果是其他的一些指令的话,那么这里不是5,可能是6、7所以要注意要根据具体情况而来。

想要用JMP进行修改,我们就需要构造机器码修改前五个字节。来尝试HOOK一下MessageBox函数。

具体实现

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <Windows.h>

int WINAPI My_MessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
FARPROC MessageBoxAAddress;
BYTE NewData[5] = { 0xE9, 0x0, 0x0, 0x0,0 };
BYTE OldData[5] = { 0 };
void InlineHook();
void UnInlineHook();
int main() {

MessageBoxA(NULL, "原始窗口1", "未被HOOK", MB_OK);
InlineHook();
MessageBoxA(NULL, "原始窗口2", "未被HOOK", MB_OK);
UnInlineHook();
MessageBoxA(NULL, "原始窗口3", "未被HOOK", MB_OK);
return 0;
}

void InlineHook() {
MessageBoxAAddress = GetProcAddress(LoadLibraryA("user32.dll"), "MessageBoxA");
//保存原数据
memcpy(OldData, MessageBoxAAddress, 5);
//计算需要跳转的偏移
DWORD dwOffset = (DWORD)My_MessageBoxA - (DWORD)MessageBoxAAddress - 5;
//写入新数据,跳转的偏移
memcpy(&NewData[1], &dwOffset, 5);

DWORD dwOldprotect = 0;
//修改页属性
VirtualProtect(MessageBoxAAddress, 5, PAGE_EXECUTE_READWRITE, &dwOldprotect);
//写入五个字节
memcpy(MessageBoxAAddress, NewData, 5);
//恢复页属性
VirtualProtect(MessageBoxAAddress, 5, dwOldprotect, &dwOldprotect);
}

void UnInlineHook() {
MessageBoxAAddress = GetProcAddress(LoadLibraryA("user32.dll"), "MessageBoxA");
DWORD dwOldProtect = 0;
VirtualProtect(MessageBoxAAddress, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//恢复原来的字节码
memcpy(MessageBoxAAddress, OldData, 5);
VirtualProtect(MessageBoxAAddress, 5, dwOldProtect, &dwOldProtect);

}

int WINAPI My_MessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType) {
//先恢复原来的字节码,然后调用正常的MessageboxA弹窗
UnInlineHook();
int bRet = MessageBoxA(NULL, "Inline Hook\ni am b1ackie!!!", "hook", MB_OK);
InlineHook();
return bRet;
}

可以看到有三个弹窗,正常流程下肯定是1,2,3但是我们Inline Hook了MessageBoxA函数,并且将其中内容改了,如果成功的话,第二个弹窗会不一样,运行程序查看效果。

先是第一个正常的窗口

然后是第二个窗口,开始了InlineHook,可以看到并没有弹出原始窗口2,说明此时已经被HOOK了

第三个窗口再取消InlineHook后弹出正常的窗口

现在使用OD载入程序来看一下,直接查看第二个MessageBoxA函数,进入查看可以看到函数头部已经改变了,变成了JMP。

跳过来看,就是我们自己编写的My_MessageBoxA函数

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
00401170 >/.  55            push ebp
00401171 |. 8BEC mov ebp,esp
00401173 |. 83EC 08 sub esp,0x8
00401176 |. A1 04304100 mov eax,dword ptr ds:[__security_cookie_fai>
0040117B |. 33C5 xor eax,ebp
0040117D |. 8945 FC mov dword ptr ss:[ebp-0x4],eax
00401180 |. 56 push esi
00401181 |. 68 4C1C4100 push InlineHo.00411C4C ; /MessageBoxA
00401186 |. 68 581C4100 push InlineHo.00411C58 ; |/user32.dll
0040118B |. FF15 04D04000 call dword ptr ds:[<&KERNEL32.LoadLibraryA>>; |\LoadLibraryA
00401191 |. 50 push eax ; |hModule = 00000001
00401192 |. FF15 08D04000 call dword ptr ds:[<&KERNEL32.GetProcAddres>; \GetProcAddress
00401198 |. 8B35 00D04000 mov esi,dword ptr ds:[<&KERNEL32.VirtualPro>; kernel32.VirtualProtect
0040119E |. 8D4D F8 lea ecx,dword ptr ss:[ebp-0x8]
004011A1 |. 51 push ecx ; /pOldProtect = 049BE914
004011A2 |. 6A 40 push 0x40 ; |NewProtect = PAGE_EXECUTE_READWRITE
004011A4 |. 6A 05 push 0x5 ; |Size = 0x5
004011A6 |. 50 push eax ; |Address = 00000001
004011A7 |. A3 A0424100 mov dword ptr ds:[MessageBoxAAddressfailure>; |
004011AC |. C745 F8 00000>mov dword ptr ss:[ebp-0x8],0x0 ; |
004011B3 |. FFD6 call esi ; \VirtualProtect
004011B5 |. 8B0D A0424100 mov ecx,dword ptr ds:[MessageBoxAAddressfai>; user32.MessageBoxA
004011BB |. A1 98424100 mov eax,dword ptr ds:[OldDatan_table]
004011C0 |. 8901 mov dword ptr ds:[ecx],eax
004011C2 |. A0 9C424100 mov al,byte ptr ds:[0x41429C]
004011C7 |. 8841 04 mov byte ptr ds:[ecx+0x4],al
004011CA |. 8D45 F8 lea eax,dword ptr ss:[ebp-0x8]
004011CD |. 50 push eax ; /pOldProtect = 00000001
004011CE |. FF75 F8 push dword ptr ss:[ebp-0x8] ; |NewProtect = PAGE_NOACCESS|PAGE_WRITECOPY|PAGE_EXECUTE|PAGE_EXECUTE_READWRITE|MEM_COMMIT|400400
004011D1 |. 6A 05 push 0x5 ; |Size = 0x5
004011D3 |. 51 push ecx ; |Address = 049BE914
004011D4 |. FFD6 call esi ; \VirtualProtect
004011D6 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
004011D8 |. 68 641C4100 push InlineHo.00411C64 ; |hook
004011DD |. 68 6C1C4100 push InlineHo.00411C6C ; |Inline Hook\ni am b1ackie!!!
004011E2 |. 6A 00 push 0x0 ; |hOwner = NULL
004011E4 |. FF15 10D14000 call dword ptr ds:[<&USER32.MessageBoxA>] ; \MessageBoxA
004011EA |. 8BF0 mov esi,eax
004011EC |. E8 CFFEFFFF call InlineHo.InlineHook_ansi_nolocketaryso>
004011F1 |. 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
004011F4 |. 8BC6 mov eax,esi
004011F6 |. 33CD xor ecx,ebp
004011F8 |. 5E pop esi ; InlineHo.0040103C
004011F9 |. E8 06000000 call InlineHo.__security_check_cookiepresen>
004011FE |. 8BE5 mov esp,ebp
00401200 |. 5D pop ebp ; InlineHo.0040103C
00401201 \. C2 1000 retn 0x10

注入方式实现inline hook

我们可以将inline hook写成一个dll文件,然后通过远线程注入的方式,将其注入到进程中。

远程线程的具体实现:注入技术-远程线程注入

编写一个弹窗,实现效果如下

当注入成功后,内容更改

总结

本篇只介绍了32位的Inline Hook,但是64位原理相同,只是修改的字节数不同,还有跳转方式也只介绍了基于JMP的,还有其他几种跳转方式,感兴趣可以看《加密与解密第四版》其中有较为详细的介绍。