您的位置:首页 > 其它

Ring 0 Inline Hook

2016-01-22 15:58 295 查看
本文讲 用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的序号







完整代码

#include "ntddk.h"

extern "C"
{
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase; //仅适用于checked build版本
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
void PageProtectOn()//恢复内存保护
{
__asm
{
mov  eax,cr0
or   eax,10000h
mov  cr0,eax
sti
}
}

void PageProtectOff()//去掉内存保护
{
__asm{
cli
mov  eax,cr0
and  eax,not 10000h
mov  cr0,eax
}
}
ULONG    g_ntopenkey;
ULONG    g_jmp_orig_ntopenkey;
UCHAR    g_orig_funcode[5];

char *pp="my de inline hook";
void FilterNtOpenFile(char* p1)
{
KdPrint(("kk:%s",pp));
KdPrint(("kk:%s",(char*)PsGetCurrentProcess()+0x16c));
}

//__declspec(naked) 告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码
__declspec(naked) void NewNtOpenFile()
{
__asm{
PUSHAD//保存所有寄存器
push pp
call    FilterNtOpenFile
POPAD//还原所有寄存器
xor        ecx,ecx
mov     eax,esi
inc     ecx
jmp        g_jmp_orig_ntopenkey
}
}

void HookNtOpenKey()
{
UCHAR    jmp_code[5]="";//存放修改指令的数组
ULONG changeva=48;//相对于函数入口偏移48的位置hook
g_ntopenkey = KeServiceDescriptorTable.ServiceTableBase[179];//NtOpenKey的入口地址
g_jmp_orig_ntopenkey = g_ntopenkey + changeva+ 5;//调回的地址, 29从入口地址偏移29的地方改写指令, 指令长度5
ULONG u_jmp_temp = (ULONG)NewNtOpenFile - g_ntopenkey - changeva - 5;//计算 newNtOpenKey相对于NtOpenKey的偏移量
jmp_code[0] = 0xE9;//0xE9对应jmp指令。  (上一篇使用E8(call)指令跳转, 需要在pop eax, jmp不需要)
*(ULONG*)&jmp_code[1] = u_jmp_temp;//生成完整的修改指令
PageProtectOff();//解锁内存
RtlCopyMemory(g_orig_funcode,(PVOID)(g_ntopenkey+changeva), 5);//备份 原始指令, 已便还原
RtlCopyMemory((PVOID)(g_ntopenkey+changeva), jmp_code, 5);//修改指令
PageProtectOn();//锁定内存
}

void UnHookOpenFile()
{
PageProtectOff();
RtlCopyMemory((PVOID)(g_ntopenkey+48),g_orig_funcode,5);//还原指令, 卸载hook
PageProtectOn();
}

VOID MyUnload(PDRIVER_OBJECT pDriverObject)
{
UnHookOpenFile();//卸载
}

NTSTATUS DriverEntry(PDRIVER_OBJECT    pDriverObject,PUNICODE_STRING Reg_Path)
{
HookNtOpenKey();
g_ntopenkey = KeServiceDescriptorTable.ServiceTableBase[179];//179==NtOpenFile 函数的 ssdt表的数组下标
pDriverObject->DriverUnload = MyUnload;
return STATUS_SUCCESS;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: