驱动断链

03驱动断链

获取驱动信息的途径是从DriverFileSystem两个目录下获得的

正常情况下我们自己做驱动遍历只能便利Driver下的所有驱动,也就是驱动链表里的驱动

分析

这里我们F12进去找一下DRIVER_OBJECT的结构

typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; 

WinDbg里dt一下

kd> dt _DRIVER_OBJECT
ntdll!_DRIVER_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
   +0x004 DeviceObject     : Ptr32 _DEVICE_OBJECT
   +0x008 Flags            : Uint4B
   +0x00c DriverStart      : Ptr32 Void
   +0x010 DriverSize       : Uint4B
   +0x014 DriverSection    : Ptr32 Void
   +0x018 DriverExtension  : Ptr32 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING
   +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
   +0x028 FastIoDispatch   : Ptr32 _FAST_IO_DISPATCH
   +0x02c DriverInit       : Ptr32     long 
   +0x030 DriverStartIo    : Ptr32     void 
   +0x034 DriverUnload     : Ptr32     void 
   +0x038 MajorFunction    : [28] Ptr32     long 

其中DriverSection里面存放了一个类似于R3的PEB的链表

   +0x014 DriverSection    : Ptr32 Void

PEB

kd> dt _PEB
ntdll!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsLegacyProcess  : Pos 2, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
   +0x003 SpareBits        : Pos 5, 3 Bits
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
   +0x014 SubSystemData    : Ptr32 Void
   +0x018 ProcessHeap      : Ptr32 Void
   +0x01c FastPebLock      : Ptr32 _RTL_CRITICAL_SECTION
   +0x020 AtlThunkSListPtr : Ptr32 Void
   +0x024 IFEOKey          : Ptr32 Void
   +0x028 CrossProcessFlags : Uint4B
   +0x028 ProcessInJob     : Pos 0, 1 Bit
   +0x028 ProcessInitializing : Pos 1, 1 Bit
   +0x028 ProcessUsingVEH  : Pos 2, 1 Bit
   +0x028 ProcessUsingVCH  : Pos 3, 1 Bit
   +0x028 ProcessUsingFTH  : Pos 4, 1 Bit
   +0x028 ReservedBits0    : Pos 5, 27 Bits
   +0x02c KernelCallbackTable : Ptr32 Void
   +0x02c UserSharedInfoPtr : Ptr32 Void
   +0x030 SystemReserved   : [1] Uint4B
   +0x034 AtlThunkSListPtr32 : Uint4B
   +0x038 ApiSetMap        : Ptr32 Void
   +0x03c TlsExpansionCounter : Uint4B
   +0x040 TlsBitmap        : Ptr32 Void
   +0x044 TlsBitmapBits    : [2] Uint4B
   +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0x050 HotpatchInformation : Ptr32 Void
   +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0x058 AnsiCodePageData : Ptr32 Void
   +0x05c OemCodePageData  : Ptr32 Void
   +0x060 UnicodeCaseTableData : Ptr32 Void
   +0x064 NumberOfProcessors : Uint4B
   +0x068 NtGlobalFlag     : Uint4B
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER
   +0x078 HeapSegmentReserve : Uint4B
   +0x07c HeapSegmentCommit : Uint4B
   +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0x088 NumberOfHeaps    : Uint4B
   +0x08c MaximumNumberOfHeaps : Uint4B
   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void
   +0x094 GdiSharedHandleTable : Ptr32 Void
   +0x098 ProcessStarterHelper : Ptr32 Void
   +0x09c GdiDCAttributeList : Uint4B
   +0x0a0 LoaderLock       : Ptr32 _RTL_CRITICAL_SECTION
   +0x0a4 OSMajorVersion   : Uint4B
   +0x0a8 OSMinorVersion   : Uint4B
   +0x0ac OSBuildNumber    : Uint2B
   +0x0ae OSCSDVersion     : Uint2B
   +0x0b0 OSPlatformId     : Uint4B
   +0x0b4 ImageSubsystem   : Uint4B
   +0x0b8 ImageSubsystemMajorVersion : Uint4B
   +0x0bc ImageSubsystemMinorVersion : Uint4B
   +0x0c0 ActiveProcessAffinityMask : Uint4B
   +0x0c4 GdiHandleBuffer  : [34] Uint4B
   +0x14c PostProcessInitRoutine : Ptr32     void 
   +0x150 TlsExpansionBitmap : Ptr32 Void
   +0x154 TlsExpansionBitmapBits : [32] Uint4B
   +0x1d4 SessionId        : Uint4B
   +0x1d8 AppCompatFlags   : _ULARGE_INTEGER
   +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0x1e8 pShimData        : Ptr32 Void
   +0x1ec AppCompatInfo    : Ptr32 Void
   +0x1f0 CSDVersion       : _UNICODE_STRING
   +0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
   +0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
   +0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
   +0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
   +0x208 MinimumStackCommit : Uint4B
   +0x20c FlsCallback      : Ptr32 _FLS_CALLBACK_INFO
   +0x210 FlsListHead      : _LIST_ENTRY
   +0x218 FlsBitmap        : Ptr32 Void
   +0x21c FlsBitmapBits    : [4] Uint4B
   +0x22c FlsHighIndex     : Uint4B
   +0x230 WerRegistrationData : Ptr32 Void
   +0x234 WerShipAssertPtr : Ptr32 Void
   +0x238 pContextData     : Ptr32 Void
   +0x23c pImageHeaderHash : Ptr32 Void
   +0x240 TracingFlags     : Uint4B
   +0x240 HeapTracingEnabled : Pos 0, 1 Bit
   +0x240 CritSecTracingEnabled : Pos 1, 1 Bit
   +0x240 SpareTracingBits : Pos 2, 30 Bits

其中 +0x00c Ldr:Ptr32 _PEB_LDR_DATA中存放了一个_PEB_LDR_DATA

kd> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr32 Void
   +0x00c InLoadOrderModuleList : _LIST_ENTRY
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY
   +0x024 EntryInProgress  : Ptr32 Void
   +0x028 ShutdownInProgress : UChar
   +0x02c ShutdownThreadId : Ptr32 Void

其中就就有模块链表

   +0x00c InLoadOrderModuleList : _LIST_ENTRY
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY

_KLDR_DATA_TABLE_ENTRY

这里我们使用R0的_KLDR_DATA_TABLE_ENTRY结构体

这里我使用WRK搜索拿到这个结构体的

这个是32位的

typedef struct _KLDR_DATA_TABLE_ENTRY {
	LIST_ENTRY InLoadOrderLinks;
	ULONG __Undefined1;
	ULONG __Undefined2;
	ULONG __Undefined3;
	ULONG NonPagedDebugInfo;
	ULONG DllBase;
	ULONG EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	USHORT LoadCount;
	USHORT __Undefined5;
	ULONG  __Undefined6;
	ULONG  CheckSum;
	ULONG  TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;

查看当前驱动的_KLDR_DATA_TABLE_ENTRY结构

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}
//定义未公开结构体
typedef struct _KLDR_DATA_TABLE_ENTRY {
	LIST_ENTRY InLoadOrderLinks;
	ULONG __Undefined1;
	ULONG __Undefined2;
	ULONG __Undefined3;
	ULONG NonPagedDebugInfo;
	ULONG DllBase;
	ULONG EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	USHORT LoadCount;
	USHORT __Undefined5;
	ULONG  __Undefined6;
	ULONG  CheckSum;
	ULONG  TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;


NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	DbgBreakPoint();
    //用我们定义的PKLDR_DATA_TABLE_ENTRY结构体的格式来表现DriverSection里面的数据
	PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
	return STATUS_SUCCESS;
}

这里如果我们不使用这种手动定义然后显示的方式是看不到DriverSection里面的数据的,因为这是一个未导出结构体

DRIVER_OBJECT中可以看到这个DriverSection位置是没有显示的,不像其他的导出结构体显示类型

这里通过代码就可以看到我们的结构体了

执行完定义结构体的操作

kd> dt ldr
Local var @ 0x8fd039cc Type _KLDR_DATA_TABLE_ENTRY*
0x86eece18 
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x84153750 - 0x88e53610 ]//加载的链表
   +0x008 __Undefined1     : 0xffffffff
   +0x00c __Undefined2     : 0xffffffff
   +0x010 __Undefined3     : 0x630069
   +0x014 NonPagedDebugInfo : 0
   +0x018 DllBase          : 0xad2cd000
   +0x01c EntryPoint       : 0xad2d1000
   +0x020 SizeOfImage      : 0x6000
   +0x024 FullDllName      : _UNICODE_STRING "\??\C:\Users\Administrator\Desktop\MyDriver8.sys"//驱动名字
   +0x02c BaseDllName      : _UNICODE_STRING "MyDriver8.sys"//短名字
   +0x034 Flags            : 0x49104000//FLAG标志位
   +0x038 LoadCount        : 1//被加载的次数
   +0x03a __Undefined5     : 0x69
   +0x03c __Undefined6     : 0
   +0x040 CheckSum         : 0xf5c9//驱动文件的校验和
   +0x044 TimeDateStamp    : 0x5c0073//时间戳

其中

   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x84153750 - 0x88e53610 ]//加载的链表

是双向循环链表,[]中的数据代表了上一个节点和下一个节点

kd> dt _KLDR_DATA_TABLE_ENTRY 0x84153750 //上一个节点
MyDriver8!_KLDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x86b41c98 - 0x86eece18 ]
   +0x008 __Undefined1     : 8
   +0x00c __Undefined2     : 0xd0e
   +0x010 __Undefined3     : 0
   +0x014 NonPagedDebugInfo : 0
   +0x018 DllBase          : 0
   +0x01c EntryPoint       : 0
   +0x020 SizeOfImage      : 0
   +0x024 FullDllName      : _UNICODE_STRING ""
   +0x02c BaseDllName      : _UNICODE_STRING "㙀萕㞀萕"
   +0x034 Flags            : 0x84144f20
   +0x038 LoadCount        : 0
   +0x03a __Undefined5     : 0
   +0x03c __Undefined6     : 0
   +0x040 CheckSum         : 0
   +0x044 TimeDateStamp    : 0

这里我们查看一下上一个节点,发现是空的

查看一下下一个节点

kd> dt _KLDR_DATA_TABLE_ENTRY 0x88e53610 //下一个节点
MyDriver8!_KLDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x86eece18 - 0x88c13450 ]
   +0x008 __Undefined1     : 0xad2954d0
   +0x00c __Undefined2     : 1
   +0x010 __Undefined3     : 0x2c
   +0x014 NonPagedDebugInfo : 0
   +0x018 DllBase          : 0xad209000
   +0x01c EntryPoint       : 0xad28c5f0
   +0x020 SizeOfImage      : 0xbe000
   +0x024 FullDllName      : _UNICODE_STRING "\??\C:\Users\Administrator\Desktop\PCHunter32aq.sys"
   +0x02c BaseDllName      : _UNICODE_STRING "PCHunter32aq.sys"
   +0x034 Flags            : 0x41104000
   +0x038 LoadCount        : 1
   +0x03a __Undefined5     : 0
   +0x03c __Undefined6     : 0
   +0x040 CheckSum         : 0xaa562
   +0x044 TimeDateStamp    : 0

发现是PCHunter的驱动

接下来呢我们查看一下PCHunter的上一个节点[ 0x86eece18 ],如果没有问题的话就是我们当前的驱动MyDriver8

dt _KLDR_DATA_TABLE_ENTRY 0x86eece18 
MyDriver8!_KLDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x84153750 - 0x88e53610 ]
   +0x008 __Undefined1     : 0xffffffff
   +0x00c __Undefined2     : 0xffffffff
   +0x010 __Undefined3     : 0x630069
   +0x014 NonPagedDebugInfo : 0
   +0x018 DllBase          : 0xad2cd000
   +0x01c EntryPoint       : 0xad2d1000
   +0x020 SizeOfImage      : 0x6000
   +0x024 FullDllName      : _UNICODE_STRING "\??\C:\Users\Administrator\Desktop\MyDriver8.sys"
   +0x02c BaseDllName      : _UNICODE_STRING "MyDriver8.sys"
   +0x034 Flags            : 0x49104000
   +0x038 LoadCount        : 1
   +0x03a __Undefined5     : 0x69
   +0x03c __Undefined6     : 0
   +0x040 CheckSum         : 0xf5c9
   +0x044 TimeDateStamp    : 0x5c0073

没有问题是当前的驱动

综上,我们当前的双向链表_KLDR_DATA_TABLE_ENTRY连接方式如下所示

所以我们只要遍历这个链表,就可以把所有的驱动遍历出来

见如下代码

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}
typedef struct _KLDR_DATA_TABLE_ENTRY {
	LIST_ENTRY InLoadOrderLinks;
	ULONG __Undefined1;
	ULONG __Undefined2;
	ULONG __Undefined3;
	ULONG NonPagedDebugInfo;
	ULONG DllBase;
	ULONG EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	USHORT LoadCount;
	USHORT __Undefined5;
	ULONG  __Undefined6;
	ULONG  CheckSum;
	ULONG  TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;


NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	//DbgBreakPoint();
	PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
	PKLDR_DATA_TABLE_ENTRY pre = (PKLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderLinks.Flink;
	//这里之所以找了下一个节点还又找了一遍,是因为当前节点的下一个节点是???错乱的
	PKLDR_DATA_TABLE_ENTRY next = (PKLDR_DATA_TABLE_ENTRY)pre->InLoadOrderLinks.Flink;
	while (next != pre)//当下一个节点等于最开始的那个节点的时候结束遍历
	{
        //因为是Unicode字符串,所以这里取地址,打印出地址对应的值
		DbgPrint("%wZ\r\n",&next->FullDllName);//打印出当前节点的完整名称
		next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;//获取当前节点的下一个节点作为当前节点
	}

	return STATUS_SUCCESS;
}

断链

代码

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}
typedef struct _KLDR_DATA_TABLE_ENTRY {
	LIST_ENTRY InLoadOrderLinks;
	ULONG __Undefined1;
	ULONG __Undefined2;
	ULONG __Undefined3;
	ULONG NonPagedDebugInfo;
	ULONG DllBase;
	ULONG EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	USHORT LoadCount;
	USHORT __Undefined5;
	ULONG  __Undefined6;
	ULONG  CheckSum;
	ULONG  TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;


NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	//DbgBreakPoint();
	PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
	PKLDR_DATA_TABLE_ENTRY pre = (PKLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderLinks.Flink;
	//这里之所以找了下一个节点还又找了一遍,是因为当前节点的下一个节点是???错乱的
	PKLDR_DATA_TABLE_ENTRY next = (PKLDR_DATA_TABLE_ENTRY)pre->InLoadOrderLinks.Flink;
	int cont = 0;
	//初始化UNICODE字符串
	UNICODE_STRING uDriverName = { 0 };
	RtlInitUnicodeString(&uDriverName, L"MyDriver8.sys");

	while (next != pre)//当下一个节点等于最开始的那个节点的时候结束遍历
	{
		//判断这个节点是否存在(长度不等于0) && 比较当前节点的驱动短名字(BaseDllName)是否和uDriverName定义的驱动名一致,TRUE表示开启匹配大小写,如果相等也就是返回0执行下面的代码
		if (next->BaseDllName.Length != 0 && RtlCompareUnicodeString(&next->BaseDllName, &uDriverName, TRUE) == 0)
		{
			DbgPrint("%wZ\r\n",&next->FullDllName);//打印出当前节点的完整名称
			RemoveEntryList(&next->InLoadOrderLinks);//移除当前节点的上一个和下一个节点
			break;
		}
		//DbgPrint("%d\r\n", cont++);
		next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;//获取当前节点的下一个节点作为当前节点
	}
	//DbgBreakPoint();
	return STATUS_SUCCESS;
}

在PCHunter中可以看到我们指定的驱动已经变成了隐藏驱动(红色)

进一步隐藏驱动特征

这里通过Source Insight搜索ObReferenceObjectByName函数

ObReferenceObjectByName原型

NTKERNELAPI NTSTATUS ObReferenceObjectByName(
	__in PUNICODE_STRING ObjectName,//驱动对象
	__in ULONG Attributes,//属性,FILE_ALL_ACCESS
	__in_opt PACCESS_STATE AccessState,//0
	__in_opt ACCESS_MASK DesiredAccess,//0
	__in POBJECT_TYPE ObjectType,//驱动对象类型,要用二级指针
	__in KPROCESSOR_MODE AccessMode,//KernelMode
	__inout_opt PVOID ParseContext,//上下文,NULL
	__out PVOID* Object//驱动对象,提前声明一个
);

使用前需要声明一个二级指针和驱动对象

extern POBJECT_TYPE *IoDriverObjectType;//声明驱动对象类型
PDRIVER_OBJECT MyDriverObject = NULL;//声明驱动对象(ObReferenceObjectByName查找成功后他就代表了我们指定的那个驱动)

功能是能根据指定的驱动名找到驱动对象,为下面的抹除驱动特征做准备(因为只有获得了对应的驱动对象,才可以去掉这个驱动的特征)

完整隐藏代码

不过这个代码有个问题,就是不能保护自身,一旦保护自身就会蓝屏

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}

//根据指定名字寻找驱动对象
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
	__in PUNICODE_STRING ObjectName,//驱动对象
	__in ULONG Attributes,//属性,FILE_ALL_ACCESS
	__in_opt PACCESS_STATE AccessState,//0
	__in_opt ACCESS_MASK DesiredAccess,//0
	__in POBJECT_TYPE ObjectType,//驱动对象类型,要用二级指针
	__in KPROCESSOR_MODE AccessMode,//KernelMode
	__inout_opt PVOID ParseContext,//上下文,NULL
	__out PVOID* Object//驱动对象,提前声明一个
);
//声明驱动对象类型
extern POBJECT_TYPE *IoDriverObjectType;
//声明未公开结构体
typedef struct _KLDR_DATA_TABLE_ENTRY {
	LIST_ENTRY InLoadOrderLinks;
	ULONG __Undefined1;
	ULONG __Undefined2;
	ULONG __Undefined3;
	ULONG NonPagedDebugInfo;
	ULONG DllBase;
	ULONG EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	USHORT LoadCount;
	USHORT __Undefined5;
	ULONG  __Undefined6;
	ULONG  CheckSum;
	ULONG  TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	//DbgBreakPoint();
	PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
	PKLDR_DATA_TABLE_ENTRY pre = (PKLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderLinks.Flink;
	//这里之所以找了下一个节点还又找了一遍,是因为当前节点的下一个节点是???错乱的
	PKLDR_DATA_TABLE_ENTRY next = (PKLDR_DATA_TABLE_ENTRY)pre->InLoadOrderLinks.Flink;
	//初始化UNICODE字符串
	UNICODE_STRING uDriverName = { 0 };
	RtlInitUnicodeString(&uDriverName, L"vmmemctl.sys");
	//使用ObReferenceObjectByName找驱动对象的时候这么命名(带路径不要带.sys)
	UNICODE_STRING uDriverName2 = { 0 };
	RtlInitUnicodeString(&uDriverName2, L"\\driver\\vmmemctl");//不区分大小写
	//DbgBreakPoint();
	while (next != pre)//当下一个节点等于最开始的那个节点的时候结束遍历
	{
		//因为是Unicode字符串,所以这里取地址,打印出地址对应的值
		//判断这个节点是否存在(长度不等于0) && 比较当前节点的驱动短名字(BaseDllName)是否和uDriverName定义的驱动名一致,TRUE表示开启匹配大小写,如果相等也就是返回0执行下面的代码
		if (next->BaseDllName.Length != 0 && RtlCompareUnicodeString(&next->BaseDllName, &uDriverName, TRUE) == 0)
		{
			PDRIVER_OBJECT MyDriverObject = NULL;//声明驱动对象(ObReferenceObjectByName查找成功后他就代表了我们指定的那个驱动)
			NTSTATUS status = ObReferenceObjectByName(&uDriverName2, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL,&MyDriverObject);
			if (NT_SUCCESS(status))
			{
				RemoveEntryList(&next->InLoadOrderLinks);//移除当前节点的上一个和下一个节点
				//将获取到的驱动对象的信息去掉
				MyDriverObject->DriverInit = NULL;//初始化
				MyDriverObject->DriverSection = NULL;//结构信息
			}
			break;
		}
		next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;//获取当前节点的下一个节点作为当前节点
	}
	return STATUS_SUCCESS;
}

隐藏自身驱动

前面的代码之所以没法隐藏自身是因为当前我们加载的主驱动会访问自身,但是因为我们给他断链了,让他找不到自身的入口点(MyDriverObject->DriverInit = NULL),所以导致蓝屏了。

所以我们应该在驱动完全加载完成后等一会再对自身进行断链同时抹除特征,不过断链后会导致无法卸载驱动

关键代码

VOID DriverHide(PWCH DriverName)
{
	LARGE_INTEGER in = { 0 };
	in.QuadPart = -10000 * 1000;//代表10s(10000),负数代表相对时间
	KeDelayExecutionThread(KernelMode, FALSE, &in);//FALSE代表没到指定时间不唤醒线程,休眠20ms
	//找驱动对象的时候应该这么命名(带路径不要带.sys)
	UNICODE_STRING uDriverName2 = { 0 };
	RtlInitUnicodeString(&uDriverName2, DriverName);//不区分大小写
	PDRIVER_OBJECT MyDriverObject = NULL;//声明驱动对象(ObReferenceObjectByName查找成功后他就代表了我们指定的那个驱动)
	NTSTATUS status = ObReferenceObjectByName(&uDriverName2, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &MyDriverObject);
	if (NT_SUCCESS(status))
	{
		PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)MyDriverObject->DriverSection;
		DbgPrint("%wZ", &ldr->FullDllName);
		RemoveEntryList(&ldr->InLoadOrderLinks);//移除当前节点的上一个和下一个节点
		//将获取到的驱动对象的信息去掉
		MyDriverObject->DriverInit = NULL;//初始化
		MyDriverObject->DriverSection = NULL;//结构信息
		ObReferenceObject(MyDriverObject);//释放引用计数
	}
	return;
}

完整代码

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}

//根据指定名字寻找驱动对象
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
	__in PUNICODE_STRING ObjectName,//驱动对象
	__in ULONG Attributes,//属性,FILE_ALL_ACCESS
	__in_opt PACCESS_STATE AccessState,//0
	__in_opt ACCESS_MASK DesiredAccess,//0
	__in POBJECT_TYPE ObjectType,//驱动对象类型,要用二级指针
	__in KPROCESSOR_MODE AccessMode,//KernelMode
	__inout_opt PVOID ParseContext,//上下文,NULL
	__out PVOID* Object//驱动对象,提前声明一个
);
//声明驱动对象类型
extern POBJECT_TYPE *IoDriverObjectType;
typedef struct _KLDR_DATA_TABLE_ENTRY {
	LIST_ENTRY InLoadOrderLinks;
	ULONG __Undefined1;
	ULONG __Undefined2;
	ULONG __Undefined3;
	ULONG NonPagedDebugInfo;
	ULONG DllBase;
	ULONG EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	USHORT LoadCount;
	USHORT __Undefined5;
	ULONG  __Undefined6;
	ULONG  CheckSum;
	ULONG  TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;

VOID DriverHide(PWCH DriverName)
{
	LARGE_INTEGER in = { 0 };
	in.QuadPart = -10000 * 1000;//代表10s(10000),负数代表相对时间
	KeDelayExecutionThread(KernelMode, FALSE, &in);//FALSE代表没到指定时间不唤醒线程,休眠10s
	//找驱动对象的时候应该这么命名(带路径不要带.sys)
	UNICODE_STRING uDriverName2 = { 0 };
	RtlInitUnicodeString(&uDriverName2, DriverName);//不区分大小写
	PDRIVER_OBJECT MyDriverObject = NULL;//声明驱动对象(ObReferenceObjectByName查找成功后他就代表了我们指定的那个驱动)
	NTSTATUS status = ObReferenceObjectByName(&uDriverName2, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &MyDriverObject);//根据指定名字(uDriverName2)寻找驱动对象,将对象保存在MyDriverObject中
	if (NT_SUCCESS(status))
	{
		PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)MyDriverObject->DriverSection;//获取当前驱动的DriverSection,将里面的内容使用PKLDR_DATA_TABLE_ENTRY结构体初始化
		DbgPrint("%wZ", &ldr->FullDllName);
		RemoveEntryList(&ldr->InLoadOrderLinks);//移除当前节点的上一个和下一个节点
		//将获取到的驱动对象的信息去掉
		MyDriverObject->DriverInit = NULL;//初始化
		MyDriverObject->DriverSection = NULL;//结构信息
		ObReferenceObject(MyDriverObject);//释放引用计数
	}
	return;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	HANDLE hThread;//声明一个句柄,保存线程句柄
	NTSTATUS status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, DriverHide, L"\\driver\\MyDriver8");//开启一个新线程,给DriverHide函数传参,L"\\driver\\MyDriver8",这里注意命名方式(带路径不要带.sys)
	if (NT_SUCCESS(status))
	{
		DbgPrint("断链隐藏成功");
		NtClose(hThread);//释放句柄
	}
	return STATUS_SUCCESS;
}

[实验]PCHunter断链

将上面的代码中的驱动名改成L"\\driver\\PCHunter32aq"

加载驱动后发现直接卡死并进入Windbg

发现卡死的语句是

mov     esi,dword ptr [eax]

这里我们查看一下eax寄存器的值

发现eax中是0

这里我们再Windbg中输入.reboot指令,让虚拟机重启(这时候vmware无法让虚拟机重启)

卡死的原因是PCHunter去获取了当前驱动的DriverSection 但是我们断链抹除痕迹的时候将这个置零了MyDriverObject->DriverSection = NULL;

这里我们会可以尝试去获取下一个节点的DriverSection去避免卡死

MyDriverObject->DriverSection = ldr->InLoadOrderLinks.Flink;

这里要注意要在断链之前进行这一步的操作

完整代码

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}

//根据指定名字寻找驱动对象
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
	__in PUNICODE_STRING ObjectName,//驱动对象
	__in ULONG Attributes,//属性,FILE_ALL_ACCESS
	__in_opt PACCESS_STATE AccessState,//0
	__in_opt ACCESS_MASK DesiredAccess,//0
	__in POBJECT_TYPE ObjectType,//驱动对象类型,要用二级指针
	__in KPROCESSOR_MODE AccessMode,//KernelMode
	__inout_opt PVOID ParseContext,//上下文,NULL
	__out PVOID* Object//驱动对象,提前声明一个
);
//声明驱动对象类型
extern POBJECT_TYPE *IoDriverObjectType;
typedef struct _KLDR_DATA_TABLE_ENTRY {
	LIST_ENTRY InLoadOrderLinks;
	ULONG __Undefined1;
	ULONG __Undefined2;
	ULONG __Undefined3;
	ULONG NonPagedDebugInfo;
	ULONG DllBase;
	ULONG EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	USHORT LoadCount;
	USHORT __Undefined5;
	ULONG  __Undefined6;
	ULONG  CheckSum;
	ULONG  TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;

VOID DriverHide(PWCH DriverName)
{
	LARGE_INTEGER in = { 0 };
	in.QuadPart = -10000 * 1000;//代表10s(10000),负数代表相对时间
	KeDelayExecutionThread(KernelMode, FALSE, &in);//FALSE代表没到指定时间不唤醒线程,休眠20ms
	//找驱动对象的时候应该这么命名(带路径不要带.sys)
	UNICODE_STRING uDriverName2 = { 0 };
	RtlInitUnicodeString(&uDriverName2, DriverName);//不区分大小写
	PDRIVER_OBJECT MyDriverObject = NULL;//声明驱动对象(ObReferenceObjectByName查找成功后他就代表了我们指定的那个驱动)
	NTSTATUS status = ObReferenceObjectByName(&uDriverName2, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &MyDriverObject);
	if (NT_SUCCESS(status))
	{
		PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)MyDriverObject->DriverSection;
		DbgPrint("%wZ", &ldr->FullDllName);
		MyDriverObject->DriverSection = ldr->InLoadOrderLinks.Flink;//获取下一个节点的DriverSection
		RemoveEntryList(&ldr->InLoadOrderLinks);//移除当前节点的上一个和下一个节点
		//将获取到的驱动对象的信息去掉
		MyDriverObject->DriverInit = NULL;//初始化
		ObReferenceObject(MyDriverObject);//释放引用计数
	}
	return;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	HANDLE hThread;
	NTSTATUS status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, DriverHide, L"\\driver\\PCHunter32aq");
	if (NT_SUCCESS(status))
	{
		DbgPrint("断链隐藏成功");
		NtClose(hThread);//释放句柄
	}
	
	return STATUS_SUCCESS;
}

可以发现PCHunter的驱动已经没有了且PCHunter没有崩溃

[实验]vga驱动断链

把下图中的那个驱动断链vga.sys

这里我们在status后面加个断点

查看一下status

发现不是0报了一个错误c0000034

#define STATUS_OBJECT_NAME_NOT_FOUND     ((NTSTATUS)0xC0000034L)

发现错误是STATUS_OBJECT_NAME_NOT_FOUND没有找到驱动对象

使用Winobj可以发现有一个名字不同但是以服务名作为名字的驱动

所以我们试一下这个驱动名字是VgaSave而不是vga

运行驱动后可以发现这个vga已经没有了,证明名字VgaSave才是正确的

NTSTATUS status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, DriverHide, L"\\driver\\VgaSave");

posted @ 2024-05-18 17:16  MuRKuo  阅读(32)  评论(0编辑  收藏  举报