Inline Hook IofCallDriver 截获所有IRP

来源:互联网 发布:算法谜题 英文版 pdf 编辑:程序博客网 时间:2024/04/29 05:00

前段时间搞了一些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你代码而不公开代码的人,技术不共享是不能进步的,同时就算有流氓利用的话,杀软的查杀也会进步的,相反没公开的技术被小部分人利用,后果好恐怖.

原创粉丝点击