一、DebugView 过滤打印
打印日志前缀,过滤掉不是我们的日志,只会显示我们自己的日志
二、内核遍历驱动模块
首先需要先了解一下双向循环链表
typedef struct _KLDR_DATA_TABLE_ENTRY
{
typedef struct LIST_ENTRY InLoadOrderLinks
{
0x00 struct _LIST_ENTRY *Flink; //相当于_KLDR_DATA_TABLE_ENTRY这个结构体的首地址,是一个指针占用4个字节
0x04 struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
...
...
PVOID PatchInformation;
}
2.
遍历驱动模块
首先我们看一下PDRIVER_OBJECT这个结构体
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
}
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 //【DriverEntry】例程的入口点,被【I/O管理器】调用;
+ 0x030 DriverStartIo : Ptr32 void
+ 0x034 DriverUnload : Ptr32 void
+ 0x038 MajorFunction : [28] Ptr32 long
其中DriverSection这个就是指向_LDR_DATA_TABLE_ENTRY这个结构体的指针,这是个驱动内核双向链表,可以通过遍历这个链表获得所有的驱动程序;或者通过断链进行隐藏驱动程序;
dt _LDR_DATA_TABLE_ENTRY 0x84356850
ntdll!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x86741c20 - 0x84356850 ] //这个就是指向上一个/下一个驱动程序的结构体指针
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x84284544 - 0x12 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x018 DllBase : 0x8420c000 Void
+0x01c EntryPoint : 0x8432b4d8 Void
+0x020 SizeOfImage : 0x412000
+0x024 FullDllName : _UNICODE_STRING "\SystemRoot\system32\ntkrnlpa.exe"
+0x02c BaseDllName : _UNICODE_STRING "ntoskrnl.exe"
+0x034 Flags : 0x8004000
+0x038 LoadCount : 0x6f
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x0 - 0x3c88ac ]
+0x03c SectionPointer : (null)
+0x040 CheckSum : 0x3c88ac
+0x044 TimeDateStamp : 0
+0x044 LoadedImports : (null)
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x0 - 0x412000 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x4ce78a09 - 0x74006e ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x73006f - 0x72006b ]
+0x068 ContextInformation : 0x006c006e Void
+0x06c OriginalBase : 0x65002e
+0x070 LoadTime : _LARGE_INTEGER 0x650078
驱动遍历源代码
#include <ntifs.h>
//内核驱动链表的结构体
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks; //驱动链表、双向循环链表
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase; //PE头
PVOID EntryPoint; //入口点
UINT32 SizeOfImage;
UNICODE_STRING FullDllName; //驱动路径名字
UNICODE_STRING BaseDllName; //驱动名字
UINT32 Flags;
UINT16 LoadCount;
UINT16 TlsIndex;
LIST_ENTRY HashLinks;
PVOID SectionPointer;
UINT32 CheckSum; //PE文件校验和时间戳
UINT32 TimeDateStamp;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
//驱动卸载函数
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
return;
}
//驱动主函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
//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;
UINT32 count = 1;
DbgPrintEx((PCSTR)77, 0, "[db]: 0 driver name = %wZ\n", &pre->FullDllName);
while (next != pre)
{
DbgPrintEx((PCSTR)77, 0, "[db]: %d driver name = %wZ\n", count++, &next->FullDllName);
next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
三、驱动断链
驱动断链源码
通过找到_KLDR_DATA_TABLE_ENTRY这个结构体进行断链,但是无法做到完全隐藏
#include <ntifs.h>
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
UINT32 SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
UINT32 Flags;
UINT16 LoadCount;
UINT16 TlsIndex;
LIST_ENTRY HashLinks;
PVOID SectionPointer;
UINT32 CheckSum;
UINT32 TimeDateStamp;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
return;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
DbgBreakPoint();
//新创建的驱动,增加在链表的末尾
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
//这个是空的,因为这是链表头
//Flink指向下一个LIST_ENTRY
//Blink指向上一个LIST_ENTRY
//用作头链表,用来判断是否遍历到了起始位置,这个位置没有驱动程序
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_STRING driverName = { 0 };
//有两个PCHunter32am.sys模块,PCHunter32aq.sys的意思是断链两个驱动模块
RtlInitUnicodeString(&driverName, L"PCHunter32aq.sys");
UINT32 count = 1;
DbgPrintEx((PCSTR)77, 0, "[db]: 0 driver name = %wZ\n", &pre->FullDllName);
while (next != pre)
{
DbgPrintEx((PCSTR)77, 0, "[db]: %d driver name = %wZ\n", count++, &next->FullDllName);
//字符串的长度不是0,并且按照规则比较结果返回有符号数值
if (next->BaseDllName.Length != 0 && RtlCompareUnicodeString(&driverName, &next->BaseDllName, TRUE) == 0)
{
//进行断链接
//next->InLoadOrderLinks相当于是这个驱动自身的_KLDR_DATA_TABLE_ENTRY
RemoveEntryList(&next->InLoadOrderLinks);
break;
}
next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
我们来看一下RemoveEntryList()这个函数
四、上面的方法无法做到完全隐藏驱动模块断链,需要另一种方法:找到驱动对象进行断链而不是_KLDR_DATA_TABLE_ENTRY这个结构体
#include <ntifs.h>
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
UINT32 SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
UINT32 Flags;
UINT16 LoadCount;
UINT16 TlsIndex;
LIST_ENTRY HashLinks;
PVOID SectionPointer;
UINT32 CheckSum;
UINT32 TimeDateStamp;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
//未导出的函数
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID *Object
);
extern POBJECT_TYPE *IoDriverObjectType;
void DriverHide(PWCH ObjName)
{
LARGE_INTEGER in = { 0 };
//这里的负数表示的是相对时间,正数拒说表示绝对时间,我没试出效果。
//单位是100nm,此处乘以10000是让单位变为s,很多代码都是乘以10,即传入的单位是ms;
in.QuadPart = -10000 * 1000;
//该函数将当前执行线程置于等待状态,当时间过后被唤醒。
KeDelayExecutionThread(KernelMode, FALSE, &in);
UNICODE_STRING driverName1 = { 0 };
//PCHunter32am.sys模块
//驱动对象路径
RtlInitUnicodeString(&driverName1, ObjName);
//通过 L"\\Driver\\DriverA" 找到驱动对象
PDRIVER_OBJECT httpDriver = NULL;
NTSTATUS status = ObReferenceObjectByName(
&driverName1,
FILE_ALL_ACCESS,
0,
0,
*IoDriverObjectType,
KernelMode,
NULL,
&httpDriver
);
if (NT_SUCCESS(status))
{
//获取 PKLDR_DATA_TABLE_ENTRY 这个结构体,进行打印
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)httpDriver->DriverSection;
DbgPrintEx(77, 0, "[db]: %wZ\n", &ldr->FullDllName);
//进行断链接
httpDriver->DriverSection = ldr->InLoadOrderLinks.Flink; //在没有断链之前,给他填充一下;
//指向下一个PKLDR_DATA_TABLE_ENTRY 结构结构体
//ldr->InLoadOrderLinks 相当于是这个驱动自身的_KLDR_DATA_TABLE_ENTRY
RemoveEntryList(&ldr->InLoadOrderLinks);
//【DriverEntry】例程的入口点,被【I/O管理器】调用;进行清零
httpDriver->DriverInit = NULL;
//关闭驱动对象引用
ObDereferenceObject(httpDriver);
}
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
return;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
HANDLE hThread = NULL;
//创建系统线程
//C:\Users\PNDGMCSY\Desktop\PCHunter驱动模块查看\PCHunter32am.sys
NTSTATUS status = PsCreateSystemThread(
&hThread,
THREAD_ALL_ACCESS,
NULL,
NULL,
NULL,
DriverHide, //调用的函数
L"\\driver\\PCHunter32am" //传递给函数的参数
);
if (NT_SUCCESS(status))
{
NtClose(hThread);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}