驱动开发:探索DRIVER_OBJECT驱动对象

本章将探索驱动程序开发的基础部分,了解驱动对象DRIVER_OBJECT结构体的定义,一般来说驱动程序DriverEntry入口处都会存在这样一个驱动对象,该对象内所包含的就是当前所加载驱动自身的一些详细参数,例如驱动大小,驱动标志,驱动名,驱动节等等,每一个驱动程序都会存在这样的一个结构。

首先来看一下微软对其的定义,此处我已将重要字段进行了备注。

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;
    PFAST_IO_DISPATCH FastIoDispatch;
    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;                 // 驱动对象的卸载地址
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;

DRIVER_OBJECT结构体是Windows操作系统内核中用于表示驱动程序的基本信息的结构体。它包含了一系列的字段,用于描述驱动程序的特定属性。

以下是DRIVER_OBJECT结构体中的一些重要字段:

  • Type:该字段标识该结构体的类型,始终设置为DRIVER_OBJECT_TYPE。
  • Size:该字段表示该结构体的大小,以字节为单位。
  • DeviceObject:该字段是一个指针,指向驱动程序所创建的设备对象链表的头部。每个设备对象代表着一个设备或者驱动程序创建的一种虚拟设备。
  • DriverStart:该字段是一个指针,指向驱动程序代码的入口点,也就是驱动程序的DriverEntry函数。该函数会在驱动程序被加载时被调用。
  • DriverSize:该字段表示驱动程序代码的大小,以字节为单位。
  • DriverName:该字段是一个UNICODE_STRING结构体,用于表示驱动程序的名称。
  • Flags:该字段是一个32位的位掩码,用于表示驱动程序的一些属性。例如,可以设置DO_BUFFERED_IO标志表示驱动程序支持缓冲I/O。

如果我们想要遍历出当前自身驱动的一些基本信息,我们只需要在驱动的头部解析_DRIVER_OBJECT即可得到全部的数据,这段代码可以写成如下样子,其中的IRP_MJ_这一系列则是微软的调用号,不同的RIP代表着不同的涵义,但一般驱动也就会用到如下这几种调用号。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include <ntifs.h>

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark \n");

	Driver->DriverUnload = UnDriver;

	DbgPrint("驱动名字 = %wZ \n", Driver->DriverName);
	DbgPrint("驱动起始地址 = %p | 大小 = %x | 结束地址 %p \n",Driver->DriverStart,Driver->DriverSize,(ULONG64)Driver->DriverStart + Driver->DriverSize);

	DbgPrint("卸载地址 = %p\n", Driver->DriverUnload);
	DbgPrint("IRP_MJ_READ地址 = %p\n", Driver->MajorFunction[IRP_MJ_READ]);
	DbgPrint("IRP_MJ_WRITE地址 = %p\n", Driver->MajorFunction[IRP_MJ_WRITE]);
	DbgPrint("IRP_MJ_CREATE地址 = %p\n", Driver->MajorFunction[IRP_MJ_CREATE]);
	DbgPrint("IRP_MJ_CLOSE地址 = %p\n", Driver->MajorFunction[IRP_MJ_CLOSE]);
	DbgPrint("IRP_MJ_DEVICE_CONTROL地址 = %p\n", Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL]);

	// 输出完整的调用号
	for (auto i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		DbgPrint("IRP_MJ调用号 = %d | 函数地址 = %p \r\n", i, Driver->MajorFunction[i]);
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

编译这段程序,签名并运行,我们即可看到如下输出信息,此时当前自身驱动的详细参数都可以被输出;

驱动开发:探索DRIVER_OBJECT驱动对象

当然运用_DRIVER_OBJECT对象中的DriverSection字段我们完全可以遍历输出当前系统下所有的驱动程序的具体信息,DriverSection结构指向了一个_LDR_DATA_TABLE_ENTRY结构,结构的微软定义如下;

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;
	union {
		LIST_ENTRY HashLinks;
		struct {
			PVOID SectionPointer;
			ULONG CheckSum;
		};
	};
	union {
		struct {
			ULONG TimeDateStamp;
		};
		struct {
			PVOID LoadedImports;
		};
	};
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

为了能够遍历出所有的系统驱动,我们需要得到pLdr结构,该结构可通过Driver->DriverSection的方式获取到,获取到之后通过pLdr->InLoadOrderLinks.Flink得到当前驱动的入口地址,而每一次调用pListEntry->Flink都将会指向下一个驱动对象,通过不断地循环CONTAINING_RECORD解析,即可输出当前系统内所有驱动的详细信息。这段程序的写法可以如下所示;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include <ntifs.h>

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;
	union {
		LIST_ENTRY HashLinks;
		struct {
			PVOID SectionPointer;
			ULONG CheckSum;
		};
	};
	union {
		struct {
			ULONG TimeDateStamp;
		};
		struct {
			PVOID LoadedImports;
		};
	};
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark \n");

	Driver->DriverUnload = UnDriver;

	PLDR_DATA_TABLE_ENTRY pLdr = NULL;
	PLIST_ENTRY pListEntry = NULL;
	PLIST_ENTRY pCurrentListEntry = NULL;

	PLDR_DATA_TABLE_ENTRY pCurrentModule = NULL;
	pLdr = (PLDR_DATA_TABLE_ENTRY)Driver->DriverSection;
	pListEntry = pLdr->InLoadOrderLinks.Flink;
	pCurrentListEntry = pListEntry->Flink;

	// 判断是否结束
	while (pCurrentListEntry != pListEntry)
	{
		// 获取LDR_DATA_TABLE_ENTRY结构
		pCurrentModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

		if (pCurrentModule->BaseDllName.Buffer != 0)
		{
			DbgPrint("模块名 = %wZ | 模块基址 = %p | 模块入口 = %p | 模块时间戳 = %d \n",
				pCurrentModule->BaseDllName,
				pCurrentModule->DllBase,
				pCurrentModule->EntryPoint,
				pCurrentModule->TimeDateStamp);
		}
		pCurrentListEntry = pCurrentListEntry->Flink;
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

编译这段程序,签名并运行,我们即可看到如下输出信息,此时当前自身驱动的详细参数都可以被输出;

驱动开发:探索DRIVER_OBJECT驱动对象

通过使用上一篇文章《驱动开发:内核字符串拷贝与比较》中所介绍的的RtlCompareUnicodeString函数,还可用于对比与过滤特定结果,以此来实现通过驱动名返回驱动基址的功能。

LONGLONG GetModuleBaseByName(PDRIVER_OBJECT pDriverObj, UNICODE_STRING ModuleName)
{
	PLDR_DATA_TABLE_ENTRY pLdr = NULL;
	PLIST_ENTRY pListEntry = NULL;
	PLIST_ENTRY pCurrentListEntry = NULL;

	PLDR_DATA_TABLE_ENTRY pCurrentModule = NULL;
	pLdr = (PLDR_DATA_TABLE_ENTRY)pDriverObj->DriverSection;
	pListEntry = pLdr->InLoadOrderLinks.Flink;
	pCurrentListEntry = pListEntry->Flink;

	while (pCurrentListEntry != pListEntry)
	{
		// 获取LDR_DATA_TABLE_ENTRY结构
		pCurrentModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

		if (pCurrentModule->BaseDllName.Buffer != 0)
		{
			// 对比模块名
			if (RtlCompareUnicodeString(&pCurrentModule->BaseDllName, &ModuleName, TRUE) == 0)
			{
				return (LONGLONG)pCurrentModule->DllBase;
			}
		}
		pCurrentListEntry = pCurrentListEntry->Flink;
	}
	return 0;
}

上这段代码的使用也非常简单,通过传入一个UNICODE_STRING类型的模块名,即可获取到模块基址并返回,至于如何初始化UNICODE_STRING则在《驱动开发:内核字符串转换方法》中有详细的介绍,此处你只需要这样来写。

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark \n");

	UNICODE_STRING unicode;

	// 获取WinDDK驱动基地址
	RtlUnicodeStringInit(&unicode, L"WinDDK.sys");
	LONGLONG winddk_address = GetModuleBaseByName(Driver, unicode);
	DbgPrint("WinDDK模块基址 = %p \n", winddk_address);

	// 获取ACPI驱动基地址
	RtlUnicodeStringInit(&unicode, L"ACPI.sys");
	LONGLONG acpi_address = GetModuleBaseByName(Driver, unicode);
	DbgPrint("ACPI模块基址 = %p \n", acpi_address);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行这段驱动程序,即可分别输出WinDDK.sys以及ACPI.sys两个驱动模块的基地址;

驱动开发:探索DRIVER_OBJECT驱动对象

原文链接:https://www.cnblogs.com/LyShark/p/17140039.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:驱动开发:探索DRIVER_OBJECT驱动对象 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • 第一部分:介绍 Spdlog 日志库

    什么是 Spdlog 日志库 Spdlog 是一个 C++ 的日志库,它具有高效、易用、跨平台等特点。它可以写入到控制台、文件等输出目标,支持多种日志级别、多线程安全等功能,非常适合在 C++ 项目中使用。 Spdlog 日志库的历史和背景 Spdlog 日志库最初由 Gabi Melman 开发,它最初是为了解决 C++ 中的日志记录问题而创建的。在很长一…

    C++ 2023年4月18日
    00
  • Python 实现Tracert追踪TTL值

    Tracert 命令跟踪路由原理是IP路由每经过一个路由节点TTL值会减一,假设TTL值=0时数据包还没有到达目标主机,那么该路由则会回复给目标主机一个数据包不可达,由此我们就可以获取到目标主机的IP地址,如下我们通过scapy构造一个路由追踪工具并实现一次追踪。 需要安装扩展包: pip install scapy 首先Scapy是如何发包与收包的,看以下…

    2023年4月2日
    00
  • STL容器之queue

    是什么 循环队列, FIFO先进先出 怎么用 初始化 //C11 deque<int> deq{1,2,3,4,5}; //拷贝构造,可以拷贝deque queue<int> que(deq); //100个5 queue<int> que2(100,5); //运算符重载 que2 = que; 操作 //队尾添加元素 …

    C++ 2023年4月17日
    00
  • 07、【算例】openfoam带孔盘体应力分析

    官网:https://doc.cfd.direct/openfoam/user-guide-v9/platehole$FOAM_TUTORIALS/stressAnalysis/solidDisplacementFoam下的案例 1、网格划分 /*——————————–*- C++ -*—————…

    C++ 2023年4月18日
    00
  • Qt源码阅读(三) 对象树管理

    对象树管理 个人经验总结,如有错误或遗漏,欢迎各位大佬指正 ? @ 目录 对象树管理 设置父对象的作用 设置父对象(setParent) 完整源码 片段分析 对象的删除 夹带私货时间 设置父对象的作用 众所周知,Qt中,有为对象设置父对象的方法——setParent。 而设置父对象的作用主要有,在父对象析构的时候,会自动去析构其子对象。如果是一个窗口对象,如…

    C++ 2023年4月18日
    00
  • C++ 测试框架 GoogleTest 初学者入门篇 乙

    *以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/aFeiOGO-N9O7Ab_8KJ2wxw 开发者虽然主要负责工程里的开发任务,但是每个开发完毕的功能都是需要开发者自测通过的,所以经常会听到开发者提起单元测试的话题。那么今天我就带大伙一起来看看大名鼎鼎的谷歌 C++ 测试…

    C++ 2023年4月18日
    00
  • 信奥赛题1105:数组逆序重存放

    新奥赛一本通,题11051105:数组逆序重存放 时间限制: 1000 ms         内存限制: 65536 KB提交数: 70600                通过数: 47540【题目描述】将一个数组中的值按逆序重新存放。例如,原来的顺序为8,6,5,4,1。要求改为1,4,5,6,8。【输入】两行:第一行数组中元素的个数n(1<n&l…

    C++ 2023年5月5日
    00
  • 如何将 Spire.Doc for C++ 集成到 C++ 程序中

    Spire.Doc for C++ 是一个专业的 Word 库,供开发人员在任何类型的 C++ 应用程序中阅读、创建、编辑、比较和转换 Word 文档。 本文演示了如何以两种不同的方式将 Spire.Doc for C++ 集成到您的 C++ 应用程序中。 通过 NuGet 安装 Spire.Doc for C++ 通过手动导入库安装 Spire.Doc f…

    C++ 2023年4月27日
    00
合作推广
合作推广
分享本页
返回顶部