Hook IofCallDriver 代码收集
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写可以写得好漂亮的.
简单防止删除文件和创建文件
就是HOOK IofCallDriver.....不过只是闹着完..冰刃可以直接删除.但对付RING 3的一些病毒还是可以的....文件驱动刚学一俩天,感觉难死了,很多东西,监控指定路径老做不出....所以基本把整个C盘都防止创建文件了..防止重写文件,....应该可以防止病毒感染吧....不过偶尔蓝屏..晕...接下来努力学习........菜啊
#include "ntifs.h"
typedef NTSTATUS (FASTCALL*MyIofCallDriver)(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp);
MyIofCallDriver old_piofcalldriver;
ULONG OldCR0;
NTSTATUS Filter_IRP_MJ_CREATE(IN PIRP Irp, IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS stat;
PIO_STACK_LOCATION pStackLocation;
PFILE_OBJECT pFileObject = NULL;
PDRIVER_OBJECT pDriverObject ;
WCHAR *pstr = NULL;
POBJECT_NAME_INFORMATION fileNameInformation;
NTSTATUS status;
ULONG retSize,CreateDisposition;
UNICODE_STRING testmyProtectPath;
RtlInitUnicodeString(&testmyProtectPath,L"http://www.cnblogs.com/Safe3/admin/file://device//HarddiskVolume1");
pStackLocation=IoGetNextIrpStackLocation(Irp);
pFileObject=pStackLocation->FileObject;
pDriverObject=DeviceObject->DriverObject;
fileNameInformation = ExAllocatePool(NonPagedPool, 1024);
__try
{
pstr = wcsrchr(pDriverObject->DriverName.Buffer, L'\\');
pstr ++;
if (_wcsnicmp(pstr, L"ntfs", 4) && _wcsnicmp(pstr, L"fastfat", 7))
{
goto pass;
}
status = ObQueryNameString(pFileObject, fileNameInformation, 1024, &retSize);
if(! NT_SUCCESS(status) )
{
DbgPrint("ObQueryNameString failed %d \n", status);
}
//wcscat(fileNameInformation->Name.Buffer,pFileObject->FileName.Buffer);
//DbgPrint("file name now is: %ws \n", fileNameInformation->Name.Buffer);
if(!_wcsnicmp(fileNameInformation->Name.Buffer, testmyProtectPath.Buffer,testmyProtectPath.Length))
{
CreateDisposition = (pStackLocation->Parameters.Create.Options>> 24) & 0x000000ff;
if((CreateDisposition==FILE_CREATE)||(CreateDisposition==FILE_OPEN_IF )
||(CreateDisposition==FILE_OVERWRITE_IF))
{
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ExFreePool(fileNameInformation);
return STATUS_ACCESS_DENIED;
}
}
}
__except(1)
{
goto pass;
}
pass:
__asm
{
mov ecx,DeviceObject
mov edx,Irp
Call old_piofcalldriver
mov stat,eax
}
}
NTSTATUS FASTCALL NewpIofCallDriver(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
NTSTATUS stat;
PIO_STACK_LOCATION pStackLocation;
pStackLocation=IoGetNextIrpStackLocation(Irp);
switch(pStackLocation->MajorFunction)
{
case IRP_MJ_SET_INFORMATION: break;
case IRP_MJ_CREATE: Filter_IRP_MJ_CREATE(Irp,DeviceObject); break;
default:
{
__asm
{
mov ecx,DeviceObject
mov edx,Irp
Call old_piofcalldriver
mov stat,eax
}
return stat;
}
}
return STATUS_SUCCESS;
}
void UnHookIofCallDriver()
{
KIRQL oldIrql;
ULONG addr = (ULONG)IofCallDriver;
oldIrql = KeRaiseIrqlToDpcLevel();
__asm
{
mov eax,cr0
mov OldCR0,eax
and eax,0xffffffff
mov cr0,eax
mov eax,addr
mov esi,[eax+2]
mov eax,old_piofcalldriver
mov dword ptr [esi],eax
mov eax,OldCR0
mov cr0,eax
}
KeLowerIrql(oldIrql);
return ;
}
VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
UnHookIofCallDriver();
}
void InlineHook_IofCallDriver()
{
KIRQL oldIrql;
ULONG addr = (ULONG)IofCallDriver;
__asm
{
mov eax,addr
mov esi,[eax+2]
mov eax,[esi]
mov old_piofcalldriver,eax
}
oldIrql = KeRaiseIrqlToDpcLevel();
__asm
{
mov eax,cr0
mov OldCR0,eax
and eax,0xffffffff
mov cr0,eax
mov eax,addr
mov esi,[eax+2]
mov dword ptr [esi],offset NewpIofCallDriver
mov eax,OldCR0
mov cr0,eax
}
KeLowerIrql(oldIrql);
return ;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload=Unload;
InlineHook_IofCallDriver();
return STATUS_SUCCESS;
}