通过 InLoadOrderLinks 遍历系统中所有的驱动

我们的驱动在被系统加载的同时,内存中会出现一个描述我们驱动信息的对象:DRIVER OBJECT,而这个对象的地址,其实就保存在我们驱动的入口函数 Driver Entry 的第1个参数中。

在 DriverObject 对象中,有一个 Driver Section 成员,它所指向的是一个名叫 _LDR_DATA_TABLE_ENTRY 的结构体,该结构体每个驱动模块都有一份,在这个结构体中就保存着一个驱动模块的所有信息。

要想遍历系统中所有的驱动模块,这个结构体就是我们要重点关注的地方。先来看下该结构体的结构(看下第1个和第7个就行):

 1 typedef struct _LDR_DATA_TABLE_ENTRY {
 2     LIST_ENTRY InLoadOrderLinks;//这个成员把系统所有加载(可能是停止没被卸载)已经读取到内存中 我们关系第一个  我们要遍历链表 双链表 不管中间哪个节点都可以遍历整个链表 本驱动的驱动对象就是一个节点
 3     LIST_ENTRY InMemoryOrderLinks;//系统已经启动 没有被初始化 没有调用DriverEntry这个历程的时候 通过这个链表进程串接起来
 4     LIST_ENTRY InInitializationOrderLinks;//已经调用DriverEntry这个函数的所有驱动程序
 5     PVOID DllBase;
 6     PVOID EntryPoint;//驱动的进入点 DriverEntry
 7     ULONG SizeOfImage;
 8     UNICODE_STRING FullDllName;//驱动的满路径
 9     UNICODE_STRING BaseDllName;//不带路径的驱动名字
10     ULONG Flags;
11     USHORT LoadCount;
12     USHORT TlsIndex;
13     union {
14         LIST_ENTRY HashLinks;
15         struct {
16             PVOID SectionPointer;
17             ULONG CheckSum;
18         };
19     };
20     union {
21         struct {
22             ULONG TimeDateStamp;
23         };
24         struct {
25             PVOID LoadedImports;
26         };
27     };
28 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

我们唯一需要关注的就是这个结构体的第一个成员:InLoadOrderLinks,它也是个结构体,有两个成员:Flink 和 Blink。看到这两个东西估计你已经能窥探出一些端倪了,没错,这是个链表。

系统中有一个双向链表,其中每一个节点都保存着一个驱动模块的所有信息,而 InLoadOrderLinks 就是该链表中的节点类型,Flink 指向下一个驱动对象的  _LDR_DATA_TABLE_ENTRY,Blink 指向上一个驱动对象的 _LDR_DATA_TABLE_ENTRY。

因此,我们只要遍历这个 InLoadOrderLinks ,就能获取系统中所有驱动的模块信息。例如,使用以下代码就能完成遍历所有驱动的操作:

 1 #include <ntddk.h>
 2 
 3 typedef struct _LDR_DATA_TABLE_ENTRY {
 4     LIST_ENTRY InLoadOrderLinks;//这个成员把系统所有加载(可能是停止没被卸载)已经读取到内存中 我们关系第一个  我们要遍历链表 双链表 不管中间哪个节点都可以遍历整个链表 本驱动的驱动对象就是一个节点
 5     LIST_ENTRY InMemoryOrderLinks;//系统已经启动 没有被初始化 没有调用DriverEntry这个历程的时候 通过这个链表进程串接起来
 6     LIST_ENTRY InInitializationOrderLinks;//已经调用DriverEntry这个函数的所有驱动程序
 7     PVOID DllBase;
 8     PVOID EntryPoint;//驱动的进入点 DriverEntry
 9     ULONG SizeOfImage;
10     UNICODE_STRING FullDllName;//驱动的满路径
11     UNICODE_STRING BaseDllName;//不带路径的驱动名字
12     ULONG Flags;
13     USHORT LoadCount;
14     USHORT TlsIndex;
15     union {
16         LIST_ENTRY HashLinks;
17         struct {
18             PVOID SectionPointer;
19             ULONG CheckSum;
20         };
21     };
22     union {
23         struct {
24             ULONG TimeDateStamp;
25         };
26         struct {
27             PVOID LoadedImports;
28         };
29     };
30 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
31 
32 VOID DriverUnload(PDRIVER_OBJECT pDriverObject) {
33     DbgPrint("Unloaded.");
34 }
35 
36 NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) {
37 
38     DbgPrint("Start Running...");
39     pDriverObject->DriverUnload = DriverUnload;
40     DbgPrint("对象基址:%x",pDriverObject);
41     DbgPrint("驱动名:%ws",pDriverObject->DriverName.Buffer);
42     DbgPrint("该驱动包含如下模块:");
43     PLDR_DATA_TABLE_ENTRY p = (PLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
44     DbgPrint("DriverSection:%x",p);
45     PLDR_DATA_TABLE_ENTRY item = (PLDR_DATA_TABLE_ENTRY)p->InLoadOrderLinks.Flink;
46     DbgPrint("First LDR TABLE:%x",item);
47     PLDR_DATA_TABLE_ENTRY first = item;
48     while(item != NULL)
49     {
50         DbgPrint("%ws",item->FullDllName.Buffer);
51         item = (PLDR_DATA_TABLE_ENTRY)item->InLoadOrderLinks.Flink;
52         if(item == first)
53             break;
54     }
55     return STATUS_SUCCESS;
56 }

这段代码唯一需要注意的是 “判断是否遍历结束”,因为是循环链表(即末尾节点的Flink指向起始节点),如果不加以判断将进入死循环。

解决方法很简单,就如上面的代码中写的那样,将遍历的第一个节点的地址保存下来,然后开始遍历,只要遍历到的节点又和这个节点的地址相同,就说明已经遍历完成。

原文地址:https://www.cnblogs.com/dubh3/p/13285103.html