本文讲 用inline hook的方式修改NtOpenKey函数的一个例子
hook 计算机里面一般是指 挂钩某函数,也可以是替换掉原来的函数。
inline hook 是直接在以前的函数替里面修改指令,用一个跳转或者其他指令来达到挂钩的目的。 这是相对普通的hook来说,因为普通的hook只是修改函数的调用地址,而不是在原来的函数体里面做修改。
一般来说 普通的hook比较稳定使用。 inline hook 更加高级一点,一般也跟难以被发现。所以很多人比如病毒制作者都比较推崇inline hook。
SSDT的全称是System Services Descriptor Table,系统服务描述符表
一般来说此表与链接系统内核的API密切相关,对此有一项应用就是杀毒软件的主动防御,当然病毒也可以通过修改主动防御的SSDT来绕过杀软的主动防御。SSDT hook一般是用来隐藏进程运行程序的,前面已经讲了。
在函数头部inline hook太容易被发现, 在函数中间inline 不容易被发现, 下面demo实现在函数中间inline
hook NtOpenFile函数, 使用PCHunter查看, 发现NtOpenFile已经被QQ的QQFrmMgr.sys hook了
取ssdt表NtOpenFile的当前地址(QFrmMgr.sys hook地址), 在函数头+48的地方替换5个字节的 机器码 来跳转, 在QFrmMgr.sys里面加了一个inline hook
模块执行流程 ntkrnlpa.exe ---> QQFrmMgr.sys ---> myinline.sys
先用PCHunter查看下NtOpenKey的序号
完整代码
1 #include "ntddk.h" 2 3 4 extern "C" 5 { 6 #pragma pack(1) 7 typedef struct ServiceDescriptorEntry { 8 unsigned int *ServiceTableBase; 9 unsigned int *ServiceCounterTableBase; //仅适用于checked build版本 10 unsigned int NumberOfServices; 11 unsigned char *ParamTableBase; 12 } ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t; 13 #pragma pack() 14 __declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable; 15 void PageProtectOn()//恢复内存保护 16 { 17 __asm 18 { 19 mov eax,cr0 20 or eax,10000h 21 mov cr0,eax 22 sti 23 } 24 } 25 26 void PageProtectOff()//去掉内存保护 27 { 28 __asm{ 29 cli 30 mov eax,cr0 31 and eax,not 10000h 32 mov cr0,eax 33 } 34 } 35 ULONG g_ntopenkey; 36 ULONG g_jmp_orig_ntopenkey; 37 UCHAR g_orig_funcode[5]; 38 39 char *pp="my de inline hook"; 40 void FilterNtOpenFile(char* p1) 41 { 42 KdPrint(("kk:%s",pp)); 43 KdPrint(("kk:%s",(char*)PsGetCurrentProcess()+0x16c)); 44 } 45 46 //__declspec(naked) 告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码 47 __declspec(naked) void NewNtOpenFile() 48 { 49 __asm{ 50 PUSHAD//保存所有寄存器 51 push pp 52 call FilterNtOpenFile 53 POPAD//还原所有寄存器 54 xor ecx,ecx 55 mov eax,esi 56 inc ecx 57 jmp g_jmp_orig_ntopenkey 58 } 59 } 60 61 void HookNtOpenKey() 62 { 63 UCHAR jmp_code[5]="";//存放修改指令的数组 64 ULONG changeva=48;//相对于函数入口偏移48的位置hook 65 g_ntopenkey = KeServiceDescriptorTable.ServiceTableBase[179];//NtOpenKey的入口地址 66 g_jmp_orig_ntopenkey = g_ntopenkey + changeva+ 5;//调回的地址, 29从入口地址偏移29的地方改写指令, 指令长度5 67 ULONG u_jmp_temp = (ULONG)NewNtOpenFile - g_ntopenkey - changeva - 5;//计算 newNtOpenKey相对于NtOpenKey的偏移量 68 jmp_code[0] = 0xE9;//0xE9对应jmp指令。 (上一篇使用E8(call)指令跳转, 需要在pop eax, jmp不需要) 69 *(ULONG*)&jmp_code[1] = u_jmp_temp;//生成完整的修改指令 70 PageProtectOff();//解锁内存 71 RtlCopyMemory(g_orig_funcode,(PVOID)(g_ntopenkey+changeva), 5);//备份 原始指令, 已便还原 72 RtlCopyMemory((PVOID)(g_ntopenkey+changeva), jmp_code, 5);//修改指令 73 PageProtectOn();//锁定内存 74 } 75 76 void UnHookOpenFile() 77 { 78 PageProtectOff(); 79 RtlCopyMemory((PVOID)(g_ntopenkey+48),g_orig_funcode,5);//还原指令, 卸载hook 80 PageProtectOn(); 81 } 82 83 VOID MyUnload(PDRIVER_OBJECT pDriverObject) 84 { 85 UnHookOpenFile();//卸载 86 } 87 88 NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING Reg_Path) 89 { 90 HookNtOpenKey(); 91 g_ntopenkey = KeServiceDescriptorTable.ServiceTableBase[179];//179==NtOpenFile 函数的 ssdt表的数组下标 92 pDriverObject->DriverUnload = MyUnload; 93 return STATUS_SUCCESS; 94 } 95 }