概述:驱动对象 DRIVER_OBJECT 结构体说明

相关参考

  • 驱动开发:探索DRIVER_OBJECT驱动对象 | LyShark®

DRIVER_OBJECT 结构体

要了解驱动对象,那了解 DRIVER_OBJECT 结构体是必要的。一般来说驱动程序 DriverEntry 入口处都会存在这样一个驱动对象,如下所示:

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)

也就是 PDriver。该对象内所包含的就是当前所加载驱动自身的一些详细参数,例如驱动大小、驱动标志、驱动名、驱动节等等。每一个驱动程序都会存在这样的一个结构,先看一下微软对其的定义:

typedef struct _DRIVER_OBJECT {
  CSHORT  Type;	// 驱动类型
  CSHORT  Size; // 驱动大小
  PDEVICE_OBJECT  DeviceObject; // 驱动对象
  ULONG  Flags; // 驱动的标志
  PVOID  DriverStart; // 驱动的起始位置
  ULONG  DriverSize; // 驱动的大小
  PVOID  DriverSection; // 指向驱动程序文件的内存区对象
  PDRIVER_EXTENSION  DriverExtension; // 驱动的扩展空间
  UNICODE_STRING  DriverName; // 驱动名字
  PUNICODE_STRING  HardwareDatabase;
  PVOID  FastIoDispatch;
  PDRIVER_INITIALIZE DriverInit;
  PDRIVER_STARTIO    DriverStartIo;
  PDRIVER_UNLOAD     DriverUnload; // 驱动对象的卸载地址
  PDRIVER_DISPATCH   MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;

如果要获取当前驱动的一些基本信息,就可以通过 DRIVER_OBJECT 的参数来获取就行,相关代码如下所示,其中 IRP_MJ_* 这一系列则是微软的调用号,不同的 RIP 代表着不同的涵义,但一般驱动也就会用到如下这几种调用号。

// #include <ntddk.h>
 
#include <ntifs.h>
 
NTSTATUS UnloadDriver(PDRIVER_OBJECT pDriver)
{
	DbgPrint("Bye, Driver\n");
 
	return STATUS_SUCCESS;
}
 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	DbgPrint("Hello World, Driver\n");
	DbgPrint("PDRIVER_OBJECT-> [0x%08x]\n", pDriver);
	DbgPrint("PUNICODE_STRING->[%ws]\n", pReg->Buffer);
 
	pDriver->DriverUnload = UnloadDriver;
 
	DbgPrint("[*] 驱动名:%wZ", pDriver->DriverName);
 
	DbgPrint("[*] 驱动起始地址:%p, 大小:%x, 结束地址: %p", pDriver->DriverStart, pDriver->DriverSize, (ULONG64)pDriver->DriverStart+pDriver->DriverSize);
 
	DbgPrint("[*] 卸载地址: %p", pDriver->DriverUnload);
	DbgPrint("[*] IRP_MJ_READ: %p", pDriver->MajorFunction[IRP_MJ_READ]);
	DbgPrint("[*] IRP_MJ_WRITE: %p", pDriver->MajorFunction[IRP_MJ_WRITE]);
	DbgPrint("[*] IRP_MJ_CREATE: %p", pDriver->MajorFunction[IRP_MJ_CREATE]);
	DbgPrint("[*] IRP_MJ_CLOSE: %p", pDriver->MajorFunction[IRP_MJ_CLOSE]);
	DbgPrint("[*] IRP_MJ_DEVICE_CONTROL: %p", pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL]);
 
	// 输出完整的调用号
	for (auto i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		DbgPrint("IRP_MJ 调用号:%d, 函数地址:%p", i, pDriver->MajorFunction[i]);
	}
    
	return STATUS_SUCCESS;
}

输出:

Hello World, Driver
PDRIVER_OBJECT-> [0x0dd35e30]
PUNICODE_STRING->[\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\KmdfDriverObject]
[*] 驱动名:\Driver\KmdfDriverObject
[*] 驱动起始地址:FFFFF80292650000, 大小:7000, 结束地址: FFFFF80292657000
[*] 卸载地址: FFFFF802926511B0
[*] IRP_MJ_READ: FFFFF8025ED1A030
[*] IRP_MJ_WRITE: FFFFF8025ED1A030
[*] IRP_MJ_CREATE: FFFFF8025ED1A030
[*] IRP_MJ_CLOSE: FFFFF8025ED1A030
[*] IRP_MJ_DEVICE_CONTROL: FFFFF8025ED1A030
IRP_MJ 调用号:0, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:1, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:2, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:3, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:4, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:5, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:6, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:7, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:8, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:9, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:10, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:11, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:12, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:13, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:14, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:15, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:16, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:17, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:18, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:19, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:20, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:21, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:22, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:23, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:24, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:25, 函数地址:FFFFF8025ED1A030
IRP_MJ 调用号:26, 函数地址:FFFFF8025ED1A030
Bye, Driver

遍历当前系统加载的所有驱动程序

DRIVER_OBJECT 对象中的 DriverSection 字段我们完全可以遍历输出当前系统下的所有的驱动程序的具体信息。DriverSection 结构指向了一个 _LDR_DATA_TABLE_ENTRY 结构体,相关定义如下所示:

//
// Loader Data Table Entry
//
typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union
    {
        LIST_ENTRY HashLinks;
        PVOID SectionPointer;
    };
    ULONG CheckSum;
    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    };
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

为了能够遍历出所有的系统驱动,我们需要得到 LDR_DATA_TABLE_ENTRY 指针,通过 PDRIVER_OBJECT->DriverSection 获取。获取到之后通过 pldr->InLoadLinks.Flink 来获取驱动的入口地址,而每一次调用 Flink 都将获取驱动链表中的下一个驱动独享,这里可以通过 CONTATNING_RECORE 解析,即可输出当前系统内所有驱动的详细信息,相关代码如下所示:

 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	DbgPrint("Hello World, Driver\n");
	DbgPrint("PDRIVER_OBJECT-> [0x%08x]\n", pDriver);
	DbgPrint("PUNICODE_STRING->[%ws]\n", pReg->Buffer);
 
	pDriver->DriverUnload = UnloadDriver;
 
	// PrintCurDirverInfo(pDriver);
 
    PLDR_DATA_TABLE_ENTRY pldr = NULL;
    PLIST_ENTRY pListEntry = NULL;
    PLIST_ENTRY pCurListEntry = NULL;
 
    PLDR_DATA_TABLE_ENTRY pCurDriver = NULL;
    pldr = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
    pListEntry = pldr->InLoadOrderLinks.Flink;
    pCurListEntry = pListEntry->Flink;
 
    // 遍历
    while (pCurListEntry != pListEntry)
    {
        // 获取 LDR_DATA_TABLE_ENTRY 结构
        pCurDriver = CONTAINING_RECORD(pCurListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
 
        if (pCurDriver->BaseDllName.Buffer != 0)
        {
            DbgPrint("[*] 模块名: %wZ, 模块基址: %p, 模块入口: %p, 模块时间戳: %d",
                pCurDriver->BaseDllName,
                pCurDriver->DllBase,
                pCurDriver->EntryPoint,
                pCurDriver->TimeDateStamp);
        }
 
        pCurListEntry = pCurListEntry->Flink;
    }
 
 
	return STATUS_SUCCESS;
}