Inline Hook IofCallDriver 截获所有IRP
首先声明这个是菜鸟—我的学习日记,不是什么高深文章,高手们慎看.
前段时间搞了一些Inline HOOK API的demo,例如对NtQueryDirectoryFile Inline HOOK 进行文件的隐藏,(恰好NtQueryDirectoryFile 在SSDT有导出,也可以采用改SSDT来实现HOOK.,只不过Inline HOOK 隐蔽性好点).而NtQueryDirectoryFile是调用IofCallDriver的(我猜的),那么HOOK IofCallDriver过滤掉关于MajorFunction == IRP_MJ_QUERY_INFORMATION的IO包就应该能隐藏文件的(我猜的,到底是不是这个MajorFunction咧,不知道),一开始就说明了我是个菜鸟,所以怎么过滤掉特定的IO包没写出来,只写了HOOK IofCallDriver的方法.流氓作者们有本事你就A,能A是你们的本事,反正我不怕流氓.
因为在SSDT里面是不存在这个函数的,那么就要Inline HOOK IofCallDriver, 我上网找了好久都没有现成的代码(或许是我愚笨找不到),So自己动手.
这里说明一下IoCallDriver不是函数,是一个宏指向IofCallDriver的.恰好IofCallDriver已经导出,我们不用MmGetSystemRoutineAddress来去IofCallDriver的地址了.
要用WinDbg来看下IofCallDriver的入口是什么东西:
由于微软对单核和多核的CPU编写了不同的内核, ntoskrnl.exe用于单核CPU的机器,而ntkrnlpa.exe则为多核,由于我的VPC是虚拟出单核的,所以本文主要围绕ntoskrnl.exe的IofCallDriver来写.(如果现实中的机器有两台就好了,可以调试ntkrnlpa.exe的IofCallDriver.)
The beginning of the IofCallDriver function
---------------------------------------------------------------------------------------
nt!IoCallDriver: if ntoskrnl.exe
804e47c5 ff2500395580 jmp dword ptr [nt!KeTickCount+0x1480 (80553900)]<-Detour here
804e47cb 90 nop
804e47cc 90 nop
804e47cd 90 nop
804e47ce 90 nop
804e47cf 90 nop
804e47d0 fe4a23 dec byte ptr [edx+23h] I try to detour here yet,and work.
804e47d3 8a4223 mov al,byte ptr [edx+23h] But I think is not well.
804e47d6 84c0 test al,al
804e47d8 0f8e40840300 jle nt!IoSetFileOrigin+0x3ce2 (8051cc1e)
804e47de 8b4260 mov eax,dword ptr [edx+60h]
804e47e1 83e824 sub eax,24h
804e47e4 56 push esi
804e47e5 894260 mov dword ptr [edx+60h],eax
804e47e8 894814 mov dword ptr [eax+14h],ecx
804e47eb 0fb600 movzx eax,byte ptr [eax]
804e47ee 8b7108 mov esi,dword ptr [ecx+8]
804e47f1 52 push edx
804e47f2 51 push ecx
804e47f3 ff548638 call dword ptr [esi+eax*4+38h]
---------------------------------------------------------------------------------------
nt!IofCallDriver: if ntkrnlpa.exe
804ef09c ff2580475580 jmp dword ptr [nt!KeTickCount+0x1780 (80554780)]<-Detour here
804ef0a2 cc int 3
804ef0a3 cc int 3
804ef0a4 cc int 3
804ef0a5 cc int 3
804ef0a6 cc int 3
804ef0a7 cc int 3
这里要用WinDbg来跟踪IofCallDriver的走向,我也是刚刚学会这么做,不会的朋友可以参考 “使用VPC进行Windows内核调试”这篇文章,在这里
804e47c5 ff2500395580 jmp dword ptr [nt!KeTickCount+0x1480 (80553900)]
设置断点,并单步跟进,这时发现运行流程是这样的:
V-- 804e47c5 ff2500395580 jmp dword ptr [nt!KeTickCount+0x1480 (80553900)]
| 804e47cb 90 nop
| 804e47cc 90 nop
| 804e47cd 90 nop
| 804e47ce 90 nop
| 804e47cf 90 nop
--> 804e47d0 fe4a23 dec byte ptr [edx+23h]
804e47d3 8a4223 mov al,byte ptr [edx+23h]
804e47d6 84c0 test al,al
804e47d8 0f8e40840300 jle nt!IoSetFileOrigin+0x3ce2 (8051cc1e)
804e47de 8b4260 mov eax,dword ptr [edx+60h]
我尝试过两种做法都可行,这里假设0xFFFFFFFF是自己函数的地址(最后给出的代码将会实现第二种做法):
l 804e47d0 fe4a23 dec byte ptr [edx+23h]
804e47d3 8a4223 mov al,byte ptr [edx+23h]
804e47d6 84c0 test al,al
改成: 0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x90 刚好8个字节
l 804e47c5 ff2500395580 jmp dword ptr [nt!KeTickCount+0x1480 (80553900)]
804e47cb 90 nop
改成:0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x00 刚好7个字节
然后在自己的函数中回跳到 804e47d7 或 804e47cc :
_emit 0xEA
_emit 0xd7 or cc
_emit 0x47
_emit 0x4e
_emit 0x80
_emit 0x80
_emit 0x00
好,至此思路和原理已经有了,下面是实现Inline Hook IofCallDriver的代码(实现上面第二种方法)
inline_hook_IofCallDriver.c///
#include <ntddk.h>
#define SystemModuleInformation 11
typedef struct _SYSTEM_MODULE_INFORMATION {//Information Class 11
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION,*PSYSTEM_MODULE_INFORMATION;
typedef struct _MODULES{
ULONG dwNumberOfModules;
SYSTEM_MODULE_INFORMATION smi;
} MODULES, *PMODULES;
NTSYSAPI
NTSTATUS
NTAPI
NtQuerySystemInformation(
IN ULONG SysInfoClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG RetLen
);
ULONG nCountCPU;
PDEVICE_OBJECT _DeviceObject;
PIRP _Irp;
PIO_STACK_LOCATION _Iosl;
ANSI_STRING Ansi;
// assembles to jmp far 0008:FFFFFFFF where FFFFFFFF is address of
// our detour function, plus one NOP to align up the patch
char DetourCode[] = { 0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x00 };
char BackupCode[] = { 0xFF, 0x25, 0x00, 0x39, 0x55, 0x80, 0x90 };
__declspec(naked) NTSTATUS __fastcall
Detour_IofCallDriver(IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp)
{
__asm
{
pushad
pushfd
}
__asm
{
push ecx
pop dword ptr _DeviceObject
push edx
pop dword ptr _Irp
}
_Iosl = IoGetNextIrpStackLocation(_Irp);
RtlUnicodeStringToAnsiString(&Ansi,&_DeviceObject->DriverObject->DriverName,TRUE);
DbgPrint("Irp: 0x%02X - Driver: %s/n", _Iosl->MajorFunction, Ansi.Buffer);
RtlFreeAnsiString(&Ansi);
__asm
{
popfd
popad
}
__asm
{
cmp nCountCPU, 2
jae MoreCPU
// jmp FAR 0x08:FFFFFFFF
_emit 0xEA
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0x08
_emit 0x00
MoreCPU:
// 由于不会调试,所以ntkrnlpa.exe的IofCallDriver的hook就放在这里先啦
_emit 0xEA
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0x08
_emit 0x00
}
}
VOID InterruptEnable()
{
__asm
{
MOV EAX, CR0 //move CR0 register into EAX
OR EAX, 10000H //enable WP bit
MOV CR0, EAX //write register back
STI //enable interrupt
}
}
VOID InterruptDisable()
{
__asm
{
CLI //dissable interrupt
MOV EAX, CR0 //move CR0 register into EAX
AND EAX, NOT 10000H //disable WP bit
MOV CR0, EAX //write register back
}
}
NTSTATUS DetourFunctionIofCallDriver()
{
int i = 0;
MODULES pModules;
//Get kernel filename,ntoskrnl.exe if single CPU,else ntkrnlpa.exe
NtQuerySystemInformation(SystemModuleInformation, &pModules, sizeof(MODULES), NULL);
if(memcmp(pModules.smi.ModuleNameOffset+pModules.smi.ImageName, "ntoskrnl.exe", sizeof("ntoskrnl.exe")) == 0)
nCountCPU = 1;
else if(memcmp(pModules.smi.ModuleNameOffset+pModules.smi.ImageName, "ntkrnlpa.exe", sizeof("ntkrnlpa.exe")) == 0)
nCountCPU = 2;
else return STATUS_UNSUCCESSFUL;
// now, stamp in the return jmp into our detour function
while(++i)
{
// we found the address 0xFFFFFFFF stamp it w/ the correct address
if(!memcmp((unsigned char *)Detour_IofCallDriver + i, &DetourCode[1], 4))
{
*( (unsigned long *)((unsigned char *)Detour_IofCallDriver + i) ) = (unsigned long)IofCallDriver + 0x0B;
break;
}
}
// stamp in the target address of the far jmp
*( (unsigned long *)(&DetourCode[1]) ) = (unsigned long)Detour_IofCallDriver;
//overwrite the bytes in the kernel function
//to apply the detour jmp
InterruptDisable();
memcpy(BackupCode, IofCallDriver, 7);
memcpy(IofCallDriver, DetourCode, 7);
InterruptEnable();
return STATUS_SUCCESS;
}
VOID UnDetourFunctionIofCallDriver()
{
//recovery kernel function
InterruptDisable();
memcpy(IofCallDriver, BackupCode, 7);
InterruptEnable();
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
UnDetourFunctionIofCallDriver();
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
DriverObject->DriverUnload = OnUnload;
if(STATUS_SUCCESS != DetourFunctionIofCallDriver())
{
DbgPrint("Detour Failure on IofCallDriver!/n");
return STATUS_UNSUCCESSFUL;
}
else return STATUS_SUCCESS;
}
至此,文章都写完了,其实都没什么高深的知识,只是我觉得难而已^_^.在hook这个函数的过程中遇到了好多困难,也去提过问题,这里要感谢hopy大哥.也参考了一些文章,例如: “IRP 乱杂谈”“【原创】从内核层保护文件不被删除- 看雪软件安全论坛”“使用VPC进行Windows内核调试”“天书夜读(完整版)”等.非常感谢作者们的技术共享精神,同时我也鄙视那些怕人A你代码而不公开代码的人,技术不共享是不能进步的,同时就算有流氓利用的话,杀软的查杀也会进步的,相反没公开的技术被小部分人利用,后果好恐怖.
原来用Word 写可以写得好漂亮的.