HOOK是一种通过更改程序的数据结构或代码结构从而改变程序运行路线的一种方法。其中包括用户模式的HOOK和内核模式的HOOK(Windows 64bit 系统有SG保护,有些HOOK不能这样简单的实现,此处仅仅记录方法)。
用户层的HOOK:IAT-HOOK, 远程线程DLL注入,远程线程代码注入,内联HOOK(可用mHOOK库)
其中IAT-HOOK主要是修改程序PE文件中的IAT表,改变函数的调用位置,但用的比较多的还是DLL注入的方法。DLL注入的方法主要是通过创建远程线程的方法将自定义的DLL文件通过系统函数LoadLibrary加载到目标进程,然后可以通过使用热键的方式,启动和关闭目标进程的HOOK。
内核层的HOOK:SSDT-HOOK, IDT-HOOK,INLINE-HOOK
SSDT--- 系统服务描述符表
IDT--- 中断描述符表
INLINE--- 内联
其实HOOK的思想都是差不多的,基本上都是
1、先保存要HOOK位置原来的数据。
2、修改内存访问属性。
3、写入HOOK数据到要HOOK的位置。
其实HOOK就是跟二进制的机器码打交道,所以还需要懂得一些机器码的基本知识。E8---call E9----jmp 等等。。
其实学习内核HOOK主要是为了学习使用windbg的内核调试,在驱动编写时难免会出现这样那样的问题,windbag可谓是驱动调试的一把利剑。同时也学习一些hook的基本思路和方法。(内核HOOK在xp系统下)
首先内核HOOK必须在内核模式下,所以用驱动来实现HOOK。此处采用NT模式驱动,实现OpenProcess的SSDT-HOOK 。
一、驱动编写的框架
#pragma code_seg("INIT")
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryPath)
{
// __asm int 3
NTSTATUS status = STATUS_SUCCESS;
//注册驱动函数及其他入口
pDriverObj->DriverUnload = UnloadDriver;
pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_READ] = DispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_WRITE] = DispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControlRoutine;
//nt 式的驱动中需要在DriverEntry中创建设备对象
status = CreateDevice(pDriverObj);
//HookNtOpenProcess();//此处实现HOOK
KdPrint(("Driver Load Success! +++++++\n"));
return status;
}
此处为驱动的入口函数,类似于main函数,加载驱动的时候首先会调用此函数。然后注册相应的IRP对应的函数调用例程,类似于回调函数或者消息相应的函数更为贴切。其中IRP_MJ_DEVICE_CONTROL是通过IOCTROL的方式来与应用层进行通信。所以HOOK函数最简单的实现方式就是写在这个函数里面。
二、HOOK函数
1、通过OD跟踪或者KD或者xuetr等工具都可以查找到OpenProcess在SSDT表的index为257,所以就直接用了
2、KeServiceDescriptorTable是xp系统已经导出的结构
3、UserOpenProcess是我自己定义的HOOK函数
4、NtOpenProcessAddr是保存原先函数地址的全局变量
void HookNtOpenProcess()
{
ULONG index = 257;
PULONG SSDTOpenProcessAddr;
ULONG UserOpenProcessAddr = (ULONG)UserOpenProcess;
__asm
{
push eax
push ebx
mov eax, KeServiceDescriptorTable
mov ebx, [eax]
mov eax, index
lea eax, [ebx + 4*eax]
mov SSDTOpenProcessAddr, eax
pop ebx
pop eax
}
//去掉页面保护
__asm
{
cli
mov eax, cr0
and eax, not 10000h //and eax,0FFFEFFFFh
mov cr0, eax
}
NtOpenProcessAddr = *SSDTOpenProcessAddr;
*SSDTOpenProcessAddr = UserOpenProcessAddr;
//恢复页面保护
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
KdPrint(("Hook NtOpenProcess\n"));
}
到此出已经实现了一个简单的SSDT-HOOK,要想UNHOOK只需要把原先的地址再写入到相应的SSDT中即可。
三、思考 怎样就可以知道SSDT是否被HOOK呢
有一个内核函数MmGetSystemRoutineAddress可以得到原先函数的地址,与SSDT中得到的函数地址比较,一致则表示没有被SSDT-HOOK,有了这些知识,那么INLINE-HOOK的实现就简单许多了。