首页 > 其他分享 >驱动开发:探索DRIVER_OBJECT驱动对象

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

时间:2023-04-03 20:45:27浏览次数:31  
标签:驱动程序 DRIVER OBJECT Driver ENTRY 驱动 TABLE

本章将探索驱动程序开发的基础部分,了解驱动对象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: [email protected]

#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对象中的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: [email protected]

#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;
}

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

通过使用上一篇文章《驱动开发:内核字符串拷贝与比较》中所介绍的的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,Driver,ENTRY,驱动,TABLE
From: https://www.cnblogs.com/LyShark/p/17140039.html

相关文章

  • Spring注解驱动原理及源码,深入理解Spring注解驱动
    文章目录一、Java注解入门大全二、Spring注解驱动编程发展历程1、注解驱动启蒙时代:SpringFramework1.x@Transactional@ManagedResource2、注解驱动过渡时代:SpringFramework2.x@Repository@Component3、注解驱动黄金时代:SpringFramework3.x4、注解驱动完善时代:SpringFramewo......
  • S5PV210开发 -- 串口驱动开发
    上篇文章讲的UART,更多的是硬件相关的知识。接下来进入正题了,串口驱动开发。一、阅读原理图我们用的是UART2串口,则接收管脚XuRXD2复用GPA1_0,发送管脚XuTXD2复用GPA1_1二、S5PV210UART (1)通用异步接收器和发送器的概述 (p-853)S5PV210中的通用异步接收器和发送器(UART)提供四......
  • Hystrix(一):为什么@EnableCircuitBreaker和@HystrixCommand能驱动Hystrix
    一、@EnableCircuitBreakerEnableCircuitBreaker源码如下:从源码看出实例化了@EnableCircuitBreaker注解实例化了EnableCircuitBreakerImportSelector这个类。再来看EnableCircuitBreakerImportSelector源码:EnableCircuitBreakerImportSelector继承了SpringFactoryImportSelector,Spr......
  • 【论文速递】ECCV2022 - PETR: Position Embedding Transformation for Multi-View 3D
    【论文原文】:PETR:用于多视图3D对象检测的位置嵌入变换论文:https://arxiv.org/abs/2203.05625代码:https://github.com/megvii-research/PETR博主关键词:小样本学习,语义分割,图注意力网络,互监督,目标检测,三维视觉摘要在本文中,我们开发了用于多视图3D对象检测的位置嵌入变换(PET......
  • i2c驱动
    I2c驱动分析通过rk3399I2C来通讯eeprom。首先确认下使用哪个I2C控制器,eeprom是接在I2C1上面。驱动程序分析先来一张图:I2C_Client用来表示一个I2Cdev。这个dev可以来自设备树也可以用C文件来写。使用C文件写来参考:linux-4.4\Documentation\i2c\instantiating-devices......
  • mysql驱动下载
    访问官网:https://dev.mysql.com/点击downloads点击MysqlCommunity(GPL)Downloads  点击Connector/J选择8.0.11版本,操作系统选择PlatformIndependent点击Download下载,下载后如下所示:  解压 ......
  • JDBC驱动类型
    1.JDBC-ODBCBridgeDriverSUN发布JDBC规范时,市场上可用的JDBC驱动程序并不多,但是已经逐渐成熟的ODBC方案使得通过ODBC驱动程序几乎可以连接所有类型的数据源。所以SUN发布了JDBC-ODBC的桥接驱动,利用现成的ODBC架构将JDBC调用转换为ODBC调用,避免了JDBC无驱动可用的窘境,如图2-6所示......
  • ubuntu安装ch34x驱动,并安装串口调试助手
    1、查看系统自带的ch34x驱动kangxubo@kangxubo-HKNS:/lib/modules/5.19.0-38-generic/kernel/drivers/usb/serial$lsaircable.koftdi_sio.kokobil_sct.kopl2303.kousb_debug.koark3116.kogarmin_gps.komct_u232.koqcaux.ko......
  • chrdev_open --- 通过文件路径,执行驱动设置的open()流程
    open一个字符设备的流程大概是:文件路径=>inode=>chrdev_open()=>(kobj_lookup=>)inode.i_cdev=>cdev.fops.my_chr_open()。所以只要通过VFS找到了inode,就可以找到chrdev_open(),这里我们就来关注一个chrdev_open()是怎么从内核的数据结构中找到我们的cdev并执行其中的my_c......
  • debian 安装Nvidia驱动
    1、下载显卡驱动https://www.nvidia.cn/Download/index.aspx?lang=cn2、关闭nouveau驱动2.1在/etc/modprobe.d/blacklist.conf中添加nouveau到黑名单。vim/etc/modprobe.d/blacklist.conf如下#nouveaublacklistnouveauoptionsnouveaumodeset=03、......