Ring 0 Inline Hook

本文讲 用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 }
原文地址:https://www.cnblogs.com/mayingkun/p/5151539.html