2.5 Windows驱动开发:DRIVER_OBJECT对象结构
在Windows内核中,每个设备驱动程序都需要一个DRIVER_OBJECT
对象,该对象由系统创建并传递给驱动程序的DriverEntry
函数。驱动程序使用此对象来注册与设备对象和其他系统对象的交互,并在操作系统需要与驱动程序进行交互时使用此对象。DRIVER_OBJECT
对象还包含了与驱动程序所管理的设备对象相关联的设备扩展结构,以及用于处理I/O
请求的函数指针等信息。它是驱动程序与操作系统内核之间的桥梁,用于协调设备的操作和管理。
本章将探索驱动程序开发的基础部分,了解驱动对象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
即可得到全部的数据,这段代码可以写成如下样子,其中的IRP_MJ_
这一系列则是微软的调用号,不同的RIP代表着不同的涵义,但一般驱动也就会用到如下这几种调用号。
#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
解析,即可输出当前系统内所有驱动的详细信息。这段程序的写法可以如下所示;
#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
两个驱动模块的基地址;
2.5 Windows驱动开发:DRIVER_OBJECT对象结构的更多相关文章
- windows 驱动开发入门——驱动中的数据结构
最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...
- Windows驱动开发(中间层)
Windows驱动开发 一.前言 依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发. 二.初步环境 1.下载安装WDK7.1.0(WinDDK\7600.16385 ...
- [Windows驱动开发](一)序言
笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓——内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...
- Windows 驱动开发 - 7
在<Windows 驱动开发 - 5>我们所说的读写操作在本篇实现. 在WDF中实现此功能主要为:EvtIoRead和EvtIoWrite. 首先,在EvtDeviceAdd设置以上两个回 ...
- Windows 驱动开发 - 8
最后的一点开发工作:跟踪驱动. 一.驱动跟踪 1. 包括TMH头文件 #include "step5.tmh" 2. 初始化跟踪 在DriverEntry中初始化. WPP_INI ...
- C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍
因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...
- windows驱动开发推荐书籍
[作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...
- Windows 驱动开发 - 5
上篇<Windows 驱动开发 - 4>我们已经完毕了硬件准备. 可是我们还没有详细的数据操作,比如接收读写操作. 在WDF中进行此类操作前须要进行设备的IO控制,已保持数据的完整性. 我 ...
- Windows驱动——读书笔记《Windows驱动开发技术详解》
=================================版权声明================================= 版权声明:原创文章 谢绝转载 请通过右侧公告中的“联系邮 ...
- Windows驱动开发-IRP的完成例程
<Windows驱动开发技术详解 >331页, 在将IRP发送给底层驱动或其他驱动之前,可以对IRP设置一个完成例程,一旦底层驱动将IRP完成后,IRP完成例程立刻被处罚,通过设置完成例程 ...
随机推荐
- 【申请教程】ChatGPT访问互联网插件
https://openai.com/blog/chatgpt-plugins 大家好,我是章北海mlpy 申请ChatGPT插件很久了,一直没下文 最近看到两种套路,我早上试了一下,看能否快速成功吧 ...
- AIGC加速迭代,云栖大会视频云「媒体服务」专场与你共话云智深度融合
2023杭州·云栖大会 倒计时5天! 阿里云视频云 5大并行Session 11场话题演讲 深度演绎云智融合的全面进化 「媒体服务」Tech专场 重磅议题剧透来袭 01 「媒体服务」Tech • 新数 ...
- CSS3 ------- object-fit属性
做项目经常会遇到图片列表展示,图片一般是用户从后台上传的,上传的图片尺寸千差万别.如果前端不控制图片大小,整个排版就会很乱,如果给定长宽,图片又会变形,用背景图片来处理有特别麻烦.这个问题一直苦恼了我 ...
- elementUI表格单选
html代码 <el-table :data="gkbmList" max-height="264" border @selection-change=& ...
- spring cloud feign 调用一直fallback
本文为博主原创,转载请注明出处: 功能在本地调试的时候一直是正常可以调用的,当服务发布到 dev 环境的时候,调用的时候一直 fallback,且由于服务调用的时候,对 Feign 配置了 fallb ...
- PHPCMS V9安装出现DNS解析失败的解决方法-不支持采集和保存远程图片
目前因为phpcms官网停止解析后,很多人安装phpcms v9出现如下错误: 这是因为检测dns解析的域名是phpcms官网的域名,官网域名停止解析后肯定检测失败.解决方法如下: 打开/ ...
- .Net 使用 MongoDB
1.安装nuget包 MongoDB.Driver 2.简单代码 using MongoDB.Bson; using MongoDB.Driver; using System.Buffers; usi ...
- [转帖]Region is unavailable的排查总结
https://tidb.net/blog/07c99ed0#4%C2%A0%20%E4%B8%80%E4%BA%9B%E5%BB%BA%E8%AE%AE 1 region访问基本流程 tidb在访问 ...
- [转帖]QPS、TPS、RT、并发数、吞吐量理解和性能优化深入思考
https://baijiahao.baidu.com/s?id=1675704570461446033&wfr=spider&for=pc 吞吐量 在了解qps.tps.rt.并发数 ...
- Oracle 提示 用户在线,无法删除的清理方法
查找删除策略. select username,sid,serial# from v$session where username='FSSCDEC004'; alter system kill se ...