本文最后更新于:2021-08-09 晚上
Inline Hook
Inline Hook是Hook技术的一种,它是通过修改机器码来实现HOOK。当我们正常调用一个API函数时,正常的流程是,call API,然后就会到函数内部执行。我们写下如下语句,在OD中打开就是如图的样子。可以看到先压入参数,然后调用MessageBoxA函数。
| MessageBoxA(NULL, "这是本来的窗口", "未被HOOK", MB_OK);
|
进入函数可以看到函数的具体
Inline Hook就是了通过字节码更改了函数的流程,刚才我们看到的是正常的流程,如果调用正常函数的时候,我们修改它的执行流程,使其跳转到我们自定义的函数内部去执行就可以实现Inlie HOOK。
如何实现
通过上面我们知道了,我们需要修改的就是程序的执行流程,而改变程序的执行流程就是jmp、call等,我们用最常见的jmp来进行分析。
如图可以看到一条JMP指令,看到后面的地址是0x401254,再看旁边的机器码是E9 EF000000。我们知道E9是JMP的机器码,那么EF000000为什么和要跳转的地址不一样呢。这是因为在JMP后面使用的是一个偏移量而不是一个具体的地址。如果使用地址的话,数据重定位之后,每次都会变,但是使用偏移就不一样了。
| 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) { 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的,还有其他几种跳转方式,感兴趣可以看《加密与解密第四版》其中有较为详细的介绍。