0%

遍历系统已加载的驱动文件(1)

简介

在一些场景下,我们需要知道某些驱动是否已加载,这就需要遍历操作系统中的驱动,
大致可以使用三种方法来进行遍历。

工作原理

1.遍历”\Driver”中的对象

我们使用微软提供的 WinObj.exe 工具,可以查看 "\Driver" 对象目录下的信息,
这里存储着已加载的 驱动服务名,假如 驱动名称服务名称 不一致,就无法查到真实驱动名。

实现的方法是使用 ntdll.dll 中的 NtOpenDirectoryObjectNtQueryDirectoryObject 函数,
假如在驱动中使用该方法,需要调用对应的 ZwXXX 函数,代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <windows.h>

#define DIRECTORY_QUERY (0x0001)
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_MORE_ENTRIES ((NTSTATUS)0x00000105L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)

typedef ULONG NTSTATUS;

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PSECURITY_DESCRIPTOR SecurityDescriptor;
PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef struct _DIRECTORY_BASIC_INFORMATION {
UNICODE_STRING ObjectName;
UNICODE_STRING ObjectTypeName;
} DIRECTORY_BASIC_INFORMATION, *PDIRECTORY_BASIC_INFORMATION;

#define InitializeObjectAttributes(p,n,a,r,s){\
(p)->Length = sizeof(OBJECT_ATTRIBUTES); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL;}

typedef VOID(NTAPI *RTLINITUNICODESTRING)(
OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString);

typedef NTSTATUS(NTAPI *NTOPENDIRECTORYOBJECT)(
OUT PHANDLE DirectoryHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes);

typedef NTSTATUS(NTAPI *NTQUERYDIRECTORYOBJECT)(
IN HANDLE DirectoryHandle,
OUT PVOID Buffer,
IN ULONG BufferLength,
IN BOOLEAN ReturnSingleEntry,
IN BOOLEAN RestartScan,
IN OUT PULONG Context,
OUT PULONG ReturnLength OPTIONAL);

typedef NTSTATUS(NTAPI *NTCLOSE)(IN HANDLE Handle);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
BOOL EnumDriverDirectoryObject()
{
HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
if (!hNtdll) return FALSE;

RTLINITUNICODESTRING fnRtlInitUnicodeString =
(RTLINITUNICODESTRING)GetProcAddress(hNtdll, "RtlInitUnicodeString");
NTOPENDIRECTORYOBJECT fnNtOpenDirectoryObject =
(NTOPENDIRECTORYOBJECT)GetProcAddress(hNtdll, "NtOpenDirectoryObject");
NTQUERYDIRECTORYOBJECT fnNtQueryDirectoryObject =
(NTQUERYDIRECTORYOBJECT)GetProcAddress(hNtdll, "NtQueryDirectoryObject");
NTCLOSE fnNtClose = (NTCLOSE)GetProcAddress(hNtdll, "NtClose");

if (!fnRtlInitUnicodeString || !fnNtOpenDirectoryObject ||
!fnNtQueryDirectoryObject || !fnNtClose)
{
FreeLibrary(hNtdll);
return FALSE;
}

UNICODE_STRING strDirName = { 0 };
OBJECT_ATTRIBUTES objAttrib = { 0 };
fnRtlInitUnicodeString(&strDirName, L"\\Driver");
InitializeObjectAttributes(
&objAttrib, &strDirName, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hDirectory = NULL;
NTSTATUS ntStatus = fnNtOpenDirectoryObject(&hDirectory, DIRECTORY_QUERY, &objAttrib);
if (ntStatus != STATUS_SUCCESS)
{
FreeLibrary(hNtdll);
return FALSE;
}

ULONG ulRet = 0;
ULONG ulContext = 0;
ULONG ulLength = 0x800;
PDIRECTORY_BASIC_INFORMATION pBuffer = NULL;
do
{
if (!pBuffer) free(pBuffer);
ulLength = ulLength * 2;
pBuffer = (PDIRECTORY_BASIC_INFORMATION)malloc(ulLength);
if (!pBuffer)
{
fnNtClose(hDirectory);
FreeLibrary(hNtdll);
return FALSE;
}
ntStatus = fnNtQueryDirectoryObject(
hDirectory, pBuffer, ulLength, FALSE, TRUE, &ulContext, &ulRet);
} while (ntStatus == STATUS_MORE_ENTRIES || ntStatus == STATUS_BUFFER_TOO_SMALL);

if (ntStatus != STATUS_SUCCESS)
{
free(pBuffer);
fnNtClose(hDirectory);
FreeLibrary(hNtdll);
return FALSE;
}

PDIRECTORY_BASIC_INFORMATION pBuffer2 = pBuffer;
while (pBuffer2->ObjectName.Buffer && pBuffer2->ObjectName.Length &&
pBuffer2->ObjectTypeName.Buffer && pBuffer2->ObjectTypeName.Length)
{
pBuffer2->ObjectName; // 服务名
pBuffer2->ObjectTypeName; // 类型driver
pBuffer2++;
}

free(pBuffer);
fnNtClose(hDirectory);
FreeLibrary(hNtdll);
return TRUE;
}
2.遍历系统加载模块

使用 ntdll 中的 NtQuerySystemInformation 函数,指定 SYSTEM_INFORMATION_CLASS11
可以查询到当前系统已加载的 dllsys 文件,在驱动中要使用 ZwQuerySystemInformation 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")

#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)

typedef struct _RTL_PROCESS_MODULE_INFORMATION {
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
CHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;

typedef struct _RTL_PROCESS_MODULES {
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; // wrk1.2中的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
NTSTATUS EnumSystemDriverModule()
{
ULONG RetLen = 0;
// SystemModuleInformation 11
NTSTATUS Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)11, NULL, 0, &RetLen);
if (Status != STATUS_INFO_LENGTH_MISMATCH) return Status;

PVOID Buffer = malloc(RetLen * 2);
if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;

Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)11, Buffer, RetLen * 2, &RetLen);
if (!NT_SUCCESS(Status))
{
free(Buffer);
return Status;
}

PRTL_PROCESS_MODULES ModInfo = (PRTL_PROCESS_MODULES)Buffer;
if (!ModInfo->NumberOfModules)
{
free(Buffer);
return STATUS_UNSUCCESSFUL; // 不为0
}

for (ULONG i = 0; i < ModInfo->NumberOfModules; i++)
{
// 包含dll和sys文件,需要根据后缀过滤
// 会出现\\SystemRoot\\前缀,或者\\??\\C:前缀,需要转换
// 在内核中使用ZwOpenFile和IoQueryFileDosDeviceName查询
ModInfo->Modules[i].FullPathName; // 模块路径
ModInfo->Modules[i].OffsetToFileName; // 名字在路径中的偏移
}

free(Buffer);
return STATUS_SUCCESS;
}
3.遍历驱动的模块链表

使用 ZwQuerySystemInformation 函数查询系统模块,本质上也是在对 DRIVER_OBJECT 中的
DriverSection 成员指向的 LDR_DATA_TABLE_ENTRY 链表进行遍历,所以我们可以手动遍历它,
注意:只要涉及到链表的操作,就必须要考虑多线程竞争的问题,所以不推荐这种操作。在不同的
系统中 LDR_DATA_TABLE_ENTRY 可能会有变化,可以用 WinDbg 进行确认

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY HashLinks;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; // 没改变的部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
NTSTATUS EnumSystemModule(PDRIVER_OBJECT DriverObject)
{
PLIST_ENTRY Entry = NULL;
PLDR_DATA_TABLE_ENTRY ListEntry = NULL;
PLDR_DATA_TABLE_ENTRY TableEntry = NULL;
if (!DriverObject) return STATUS_INVALID_PARAMETER;
// 在这里有驱动自身的信息
ListEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
// 遍历子链表,注意多线程竞争的问题
Entry = ListEntry->InLoadOrderLinks.Flink;
while (Entry != &ListEntry->InLoadOrderLinks)
{
TableEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (TableEntry->FullDllName.Buffer && TableEntry->FullDllName.Length)
{
// 模块的路径,非全路径,测试时发现第1个信息为空,遍历完子链表不包含自身信息
DbgPrint("%p: %wZ\n", TableEntry->DllBase, TableEntry->FullDllName);
}
Entry = Entry->Flink;
}
return STATUS_SUCCESS;
}