简介 在一些场景下,我们需要知道某些驱动是否已加载,这就需要遍历操作系统中的驱动, 大致可以使用三种方法来进行遍历。
工作原理 1.遍历”\Driver”中的对象 我们使用微软提供的 WinObj.exe
工具,可以查看 "\Driver"
对象目录下的信息, 这里存储着已加载的 驱动服务名
,假如 驱动名称
和 服务名称
不一致,就无法查到真实驱动名。
实现的方法是使用 ntdll.dll
中的 NtOpenDirectoryObject
和 NtQueryDirectoryObject
函数, 假如在驱动中使用该方法,需要调用对应的 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; pBuffer2++; } free (pBuffer); fnNtClose(hDirectory); FreeLibrary(hNtdll); return TRUE; }
2.遍历系统加载模块 使用 ntdll
中的 NtQuerySystemInformation
函数,指定 SYSTEM_INFORMATION_CLASS
为 11
, 可以查询到当前系统已加载的 dll
和 sys
文件,在驱动中要使用 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;
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 ; 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; } for (ULONG i = 0 ; i < ModInfo->NumberOfModules; i++) { 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) { DbgPrint("%p: %wZ\n" , TableEntry->DllBase, TableEntry->FullDllName); } Entry = Entry->Flink; } return STATUS_SUCCESS; }