|
原作者: czcqq ,我只是应用在VB上了。
作者原文:对于 在WINDOWS启动的时候加载 和 感染系统文件 我们暂时不讨论,玩么只讨论动态加载
一般的加载流程,是这样的:打开服务管理器->创建服务->启动服务->(系统加载驱动)
这个过程系统最终会调用NtLoadDriver来加载驱动(也可以用Ntdll.dll里面的NtSetSystemInformation来加载)
而NtLoadDriver 会向系统插入一个作业,然后等待另外一个系统线程来加载驱动,并等待驱动的加载完成(NtSetSystemInformation也是一样的),然后返回
这样我们就可以HOOK NtLoadDriver和NtSetSystemInformation来阻止驱动加载,但是这个方法已经用烂了,这里我HOOK NtCreateSection来阻止驱动加载
为什么HOOK NtCreateSection呢???
因为在另外一个线程取得消息加载驱动的时候会调用NtCreateSection来映射驱动到内核内存空间
(流程:大概是这样 IoCreateFile(打开驱动文件,将它的第二个参数设置为FILE_EXECUTE | SYNCHRONIZE) -> NtCreateSection(为驱动在内核内存空间创建一个节) ->NtMapViewOfSection(映射驱动到内核内存空间) -> 寻找驱动的DriverEntry,并调用 -> ZwClose(关闭文件句柄) ->然后通知NtLoadDriver(或者NtSetSystemInformation)驱动加载完成->NtLoadDriver(或者NtSetSystemInformation)返回用户层,并通知用户驱动加载完成)
在驱动加载流程中,我们可以看到我们有很多机会劫持驱动的加载
我们可以HOOK NtCreateSection 或者 NtMapViewOfSection 来阻止驱动加载
这里我采用HOOK NtCreateSection的办法阻止驱动加载
以下为驱动代码:
- #include <ntifs.h>
- //声明用到的头文件和结构 宏等
- #include "NtCreateSection.h"
- #if DBG
- #define DriversUnload(Address, p) \
- Address->DriverUnload=p;
- #else
- #define DriversUnload(Address, p) \
- Address->DriverUnload=NULL;
- #endif
- typedef int BOOL;
- typedef unsigned int UINT;
- typedef unsigned long DWORD;
- typedef unsigned short WORD;
- typedef void *LPVOID;
- typedef unsigned char BYTE;
- typedef DWORD *PDWORD;
- typedef BYTE *PBYTE;
- typedef WORD *PWORD;
- #define PAGE_NOACCESS 0x01
- #define PAGE_READONLY 0x02
- #define PAGE_READWRITE 0x04
- #define PAGE_WRITECOPY 0x08
- #define PAGE_EXECUTE 0x10
- #define PAGE_EXECUTE_READ 0x20
- #define PAGE_EXECUTE_READWRITE 0x40
- #define PAGE_EXECUTE_WRITECOPY 0x80
- #define PAGE_GUARD 0x100
- #define PAGE_NOCACHE 0x200
- #define PAGE_WRITECOMBINE 0x400
- #define MEM_COMMIT 0x1000
- #define MEM_RESERVE 0x2000
- #define MEM_DECOMMIT 0x4000
- #define MEM_RELEASE 0x8000
- #define MEM_FREE 0x10000
- #define MEM_PRIVATE 0x20000
- #define MEM_MAPPED 0x40000
- #define MEM_RESET 0x80000
- #define MEM_TOP_DOWN 0x100000
- #define MEM_4MB_PAGES 0x80000000
- #define SEC_FILE 0x800000
- #define SEC_IMAGE 0x1000000
- #define SEC_VLM 0x2000000
- #define SEC_RESERVE 0x4000000
- #define SEC_COMMIT 0x8000000
- #define SEC_NOCACHE 0x10000000
- #define MEM_IMAGE SEC_IMAGE
- PVOID WriteAddress=NULL;
- PMDL pMdl=NULL;
- //声明所需要的函数
- NTSYSAPI
- NTSTATUS
- NTAPI
- ZwYieldExecution(
- VOID
- );
- PVOID NTAPI GetJmpAddress(PVOID Fun,BOOL *Call_Code);
- NTSTATUS
- NTAPI
- CallBack_NtCreateSection (
- OUT PHANDLE SectionHandle,
- IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
- IN PLARGE_INTEGER MaximumSize OPTIONAL,
- IN ULONG SectionPageProtection,
- IN ULONG AllocationAttributes,
- IN HANDLE FileHandle OPTIONAL
- );
- NTSTATUS
- NTAPI
- OldNtCreateSection (
- OUT PHANDLE SectionHandle,
- IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
- IN PLARGE_INTEGER MaximumSize OPTIONAL,
- IN ULONG SectionPageProtection,
- IN ULONG AllocationAttributes,
- IN HANDLE FileHandle OPTIONAL
- );
- void makejmp(LPVOID Fun1,LPVOID Fun2,LPVOID jmp);
- #pragma alloc_text(PAGE,makejmp)
- #pragma alloc_text(PAGE,OldNtCreateSection)
- #pragma alloc_text(PAGE,CallBack_NtCreateSection)
- #pragma alloc_text(PAGE,GetJmpAddress)
- //所有声明结束
- //驱动入口
- NTSTATUS DriverEntry(
- IN PDRIVER_OBJECT DriverObject,
- IN PUNICODE_STRING RegistryPath
- )
- {
- NTSTATUS Status = 0;
- PDEVICE_OBJECT pDeviceObject = NULL;
- //创建设备,这个就不讲解了,大家明白就好,我重点讲解HOOK过程
- Status = IoCreateDevice(
- DriverObject,
- 0,
- NULL,
- FILE_DEVICE_UNKNOWN,
- 0,
- FALSE,
- &pDeviceObject
- );
- if ( NT_SUCCESS(Status) ) {
- KIRQL oldIrql;
- PVOID HookAddress = NULL;
- BOOL Hook=0;
- PVOID JmpData=ExAllocatePool(NonPagedPool,5);//申请内存,用来保存内容为Jmp CallBack_NtCreateSection的代码
- DriversUnload(DriverObject,Unload);//设置DriverObject->DriverUnload = Unload;这个宏只有在调试版本的时候才会设置
- //DriverObject->DriverUnload = Unload;如果不是调试版本,就会设置DriverObject->DriverUnload = NULL;
- if(JmpData==NULL)
- {
- DbgPrint("HOOK NtCreateSection失败! 内存申请失败\n");
- return Status;
- }
- memset(JmpData,0x90,5);//初始化JmpData内容为NOP
- //将NtCreateSection的头7个字节复制到OldNtCreateSection中来
- pMdl=IoCreateWriteMdlForAddress(OldNtCreateSection,&WriteAddress,7);
- if(pMdl==NULL)
- {
- DbgPrint("HOOK NtCreateSection失败! OldNtCreateSection 写入失败\n");
- ExFreePool(JmpData);
- JmpData=NULL;
- WriteAddress=NULL;
- pMdl=NULL;
- return Status;
- }
- memcpy(WriteAddress,NtCreateSection,7);
- IoFreeMdlForAddress(WriteAddress,pMdl);
- WriteAddress=NULL;
- //将NtCreateSection的头5字节变成可写
- pMdl=IoCreateWriteMdlForAddress(NtCreateSection,&WriteAddress,7);
- if(pMdl==NULL)
- {
- DbgPrint("HOOK NtCreateSection失败! NtCreateSection 写入失败\n");
- ExFreePool(JmpData);
- JmpData=NULL;
- WriteAddress=NULL;
- pMdl=NULL;
- return Status;
- }
- //检查是否已经被别人HOOK,如果已经被别人HOOK则我们退出HOOK,这里是可以改进的,但是我没有时间写,只能退出HOOK
- HookAddress=GetJmpAddress(NtCreateSection,&Hook);
- if(HookAddress!=NULL)
- {
- DbgPrint("HOOK NtCreateSection失败! 发现NtCreateSection已经被别人HOOK 所以本HOOK退出\n");
- ExFreePool(JmpData);
- JmpData=NULL;
- IoFreeMdlForAddress(WriteAddress,pMdl);
- WriteAddress=NULL;
- pMdl=NULL;
- return Status;
- }
- //HOOK NtCreateSection
- if(NT_SUCCESS(ZwYieldExecution()))//先向系统申请CPU时间
- {
- _asm cli//关闭中断
- oldIrql = KeRaiseIrqlToDpcLevel();//提升到DPC级别
- memset(WriteAddress,0x90,7);//修改NtCreateSection的前7个字节为NOP指令
- makejmp(NtCreateSection,CallBack_NtCreateSection,JmpData);//取得 Jmp CallBack_NtCreateSection的代码,代码保存在JmpData中
- memcpy(WriteAddress,JmpData,5);//修改NtCreateSection前5个字节为 Jmp CallBack_NtCreateSection
- KeLowerIrql(oldIrql);//还原到原来的IRQL级别
- _asm sti//开中断
- }else
- {
- DbgPrint("申请CPU时间失败,HOOK退出\n");
- }
- ExFreePool(JmpData);
- JmpData=NULL;
- }
- return Status;
- }
- //我们的NtCreateSection过滤函数
- NTSTATUS
- NTAPI
- CallBack_NtCreateSection (
- OUT PHANDLE SectionHandle,
- IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
- IN PLARGE_INTEGER MaximumSize OPTIONAL,
- IN ULONG SectionPageProtection,
- IN ULONG AllocationAttributes,
- IN HANDLE FileHandle OPTIONAL
- )
- {
- NTSTATUS Status = 0;
- Status = OldNtCreateSection(
- SectionHandle,
- DesiredAccess,
- ObjectAttributes ,
- MaximumSize ,
- SectionPageProtection,
- AllocationAttributes,
- FileHandle);
- if ( NT_SUCCESS(Status) )
- {
- //进行行为判断,如果是要加载驱动,我们就直接返回错误,并关闭句柄,如果不是就返回原来的结果
- if(((DWORD)PsGetCurrentProcessId()==(DWORD)4)|((DWORD)PsGetCurrentProcessId()==(DWORD)8)|((DWORD)PsGetCurrentProcessId()==(DWORD)0))
- {
- if((FlagOn(DesiredAccess,SECTION_MAP_EXECUTE))||(FlagOn(DesiredAccess,PAGE_EXECUTE_READ)))
- if((PAGE_EXECUTE==SectionPageProtection)|(AllocationAttributes==SEC_IMAGE))
- {
- ZwClose(*SectionHandle);
- *SectionHandle=NULL;
- return STATUS_ACCESS_DENIED;
- }
- }
- }
- return Status;
- }
- //用来跳转到原函数的一个裸函数
- __declspec(naked)NTSTATUS
- NTAPI
- OldNtCreateSection (
- OUT PHANDLE SectionHandle,
- IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
- IN PLARGE_INTEGER MaximumSize OPTIONAL,
- IN ULONG SectionPageProtection,
- IN ULONG AllocationAttributes,
- IN HANDLE FileHandle OPTIONAL
- )
- {
- _asm
- {
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- mov eax,NtCreateSection //将原函数地址送入eax
- add eax,7 //eax加7,用来跳过我们的HOOK
- push eax //将eax压入
- ret //跳回原函数
- }
- }
- //用来生成跳转代码的函数
- void makejmp(LPVOID Fun1,LPVOID Fun2,LPVOID jmp)
- {
- BYTE *data=(BYTE *)jmp;
- long dFun1=(long)Fun1;
- long dFun2=(long)Fun2;
- DWORD H;
- data[0]=0xe9;
- _asm
- {
- mov eax,dFun1
- mov edx,dFun2
- sub edx,eax
- sub edx,5
- mov H,edx
- }
- memcpy(&data[1],(void *)&H,4);
- }
- VOID Unload(
- IN PDRIVER_OBJECT DriverObject
- )
- {
- KIRQL oldIrql;
- while(!NT_SUCCESS(ZwYieldExecution()))//取得CPU时间,如果取得失败,就一直获取,直到成功
- {
- }
- if(pMdl!=NULL)//检查我们是否已经进行过HOOK,//如果我们进行过HOOK 就还原
- {
- oldIrql = KeRaiseIrqlToDpcLevel();
- memcpy(WriteAddress,OldNtCreateSection,7);
- KeLowerIrql(oldIrql);
- IoFreeMdlForAddress(WriteAddress,pMdl);
- }
- IoDeleteDevice(DriverObject->DeviceObject);//删除设备
- pMdl=NULL;
- WriteAddress=NULL;
- }
- PVOID NTAPI GetJmpAddress(PVOID Fun,BOOL *Call_Code)
- {
- PVOID Return=NULL;
- BYTE *data=(BYTE *)Fun;
- DWORD Old=0;
- if(data[0]==0xe9)
- {
- *Call_Code=0;
- }else if (data[0]==0xe8)
- {
- *Call_Code=1;
- }else
- {
- *Call_Code=2;
- return NULL;
- }
- memcpy((void *)&Old,&data[1],4);
- _asm
- {
- mov eax,Old
- mov edx,Fun
- add eax,edx
- add eax,5
- mov Return,eax
- }
- return Return;
- }
复制代码
这个驱动只支持单核心CPU 多核心CPU请自行修改(将所有CPU都提升到DPC级别 以后再进行HOOK)驱动加载流程是看了WIN2K的代码来讲的,WIN2K的代码很长,所以就简单的讲了一下
|
|