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内核中,每个设备驱动程序都需要一个DRIVER_OBJECT对象,该对象由系统创建并传递给驱动程序的DriverEntry函数。驱动程序使用此对象来注册与设备对象和其他系统对象的交互,并在操作系统需要与驱动程序进行交互时使用此对象。DRIVER_OB…...
[ubuntu]ubuntu上安装jdk1.8教程
首先需要去官方网站去下载对应jdk1.8版本: https://www.oracle.com/java/technologies/downloads/ 您也可以去csdn搜索我提供jdk安装包 这里以jdk-8u201-linux-x64.tar.gz为例子,首先下载安装后解压 tar -zxvf jdk-8u201-linux-x64.tar.gz 比如我解…...

金蝶云星空其他出库单保存提示序列号不一致
文章目录 金蝶云星空其他出库单保存提示序列号不一致保存报错初步分析总结 金蝶云星空其他出库单保存提示序列号不一致 保存报错 显示单据数量0.序列号数量3 初步分析 输入实发数量没有触发序列号数量的计算 检查实发数量的值更新事件 实发数量和序列号数量的转换ÿ…...

FBI:皇家勒索软件要求350名受害者支付2.75亿美元
导语 最近,FBI和CISA联合发布的一份通告中透露,自2022年9月以来,皇家勒索软件(Royal ransomware)已经入侵了全球至少350家组织的网络。这次更新的通告还指出,这个勒索软件团伙的赎金要求已经超过了2.75亿美…...

Layout工程师们--Allegro X AI实现pcb自动布局布线
Cadence 推出 Allegro X AI,旨在加速 PCB 设计流程,可将周转时间缩短 10 倍以上 楷登电子(美国 Cadence 公司,NASDAQ:CDNS)今日宣布推出 Cadence Allegro X AI technology,这是 Cadence 新一代…...

Hive入门--学习笔记
1,Apache Hive概述 定义: Hive是由Facebook开源用于解决海量结构化日志的数据统计,它是基于大数据生态圈Hadoop的一个数据仓库工具。 作用: Hive可以用于将结构化的数据文件【映射】为一张表,并提供类SQL查询功能。 H…...
【nlp】1文本预处理总括目录(附各章节链接)
文本预处理 1. 文本预处理机器作用2. 文本预处理包含的主要环节2.1 文本处理的基本方法2.1.1 分词2.1.2 词性标注2.2.3 命名实体标注2.2 文本张量表示方法2.2.1 one-hot编码2.2.2 Word2vec2.2.3 Word Embedding2.3 文本语料的数据分析2.3.1 标签数量分布2.3.2 句子长度分布2.3.…...

《增长黑客》思维导图
增长黑客这个词源于硅谷,简单说,这是一群以数据驱动营销、以迭代验证策略,通过技术手段实现爆发式增长的新型人才。 近年来,互联网公司意识到这一角色可以发挥四两拨千斤的作用,因此对该职位的需求也如井喷式增长。本…...

oracle-buffer cache
段,区,块。 每当新建一个表,数据库会相应创建一个段。然后给这个段分配一个区。 一个区包含多个块。 区是oracle给段分配空间的最小单位。 块是oracle i\o的最小单位。 原则上,一个块包含多行数据。 dbf文件会被划分成一个一个…...
数据可视化—D3(Data Driven Documents)
链接 教程链接安装教程官方github仓库 基础知识 D3是一个Javascript库,用于在浏览器中创建可视化和可交互的各种图表。通过以下代码的对比,说明D3的使用场景以及使用效果(理论上,以下两段代码效果是一样的)…...

±15kV ESD 保护、3V-5.5V 供电、真 RS-232 收发器MS2232/MS2232T
产品简述 MS2232/MS2232T 芯片是集成电荷泵,具有 15kV ESD 保护的 RS-232 收发器,包括两路接收器、两路发送器。 芯片满足 TIA/EIA-232 标准,为异步通信控制器和串口连 接器提供通信接口。 芯片采用 3V-5.5V 供电,电荷泵仅用…...

企业版远程软件推荐
在当今的数字时代,为您的企业配备远程访问功能至关重要。通过远程访问,您的团队可以在办公室外工作,并且无论身在何处都可以保持相同的生产力水平。在本文中,我们汇总了市场上的四大选择。 我们在远程访问解决方案中寻找什么 远…...
独孤思维:没学会走就要跑,你只能一辈子是穷b
很多人眼高手低,没学会走就要跟别人比赛跑步; 很多人想要发财,没赚到钱就要喊着跟谁比有钱。 眼高手低,自命不凡,愚蠢至极。 上周团队要扩编,招一个运营。 来了一个00后女孩应聘。 上来就说自己目标三…...

鸿蒙LiteOs读源码教程+向LiteOS中添加一个系统调用
本文分为2个部分:第1部分简要介绍如何读鸿蒙Liteos源码,第2部分是实验向LiteOS中添加一个系统调用的完整过程。 前置资料: imx6ull开发板使用方式详解 源码下载 编译运行简单程序 Ubuntu虚拟机使用鸿蒙LiteOs操作系统常见错误汇总 一、鸿…...

美国站群服务器IP如何设置分配?
在配置美国站群服务器时,IP的分配是一个重要的步骤。下面将介绍一些关于美国站群服务器IP分配的相关知识。 独享IP和虚拟IP 在租用美国站群服务器之前,我们需要了解提供的IP是独享的还是虚拟的。独享IP指每个网站都有独立的IP地址,而虚…...

R语言——taxize(第二部分)
taxize(第二部分) 3. taxize 文档中译3.10. classification(根据类群ID检索分类阶元层级)示例1:传递单个ID值示例2:传递多个ID值示例3:传递单个名称示例4:传递多个名称示例5…...

Postman+Newman+Jenkins实现接口测试持续集成
近期在复习Postman的基础知识,在小破站上跟着百里老师系统复习了一遍,也做了一些笔记,希望可以给大家一点点启发。 1.新建一个项目 2.设置自定义工作空间 3.执行windows的批处理命令 4.执行系统的Groovy脚本 5.生成的HTML的报告集成到Jenkin…...

C#WPF中的实现读取和写入文件的几种方式
说明:C#中实现读取和写入的类根据需要来选择。 1、File类 File类是用于操作文件的工具类,提供了对文件进行创建、复制、删除、移动和打开单一文件的静态方法。但需要注意的是,WPF中使用File的类,需要先引用System.IO下的命名空间。…...

如何利用自动发现将现网的进程纳入到监控系统中?
进程监控是一项关键任务,旨在监测系统中运行的进程的性能和状态。通过有效的进程监控,可以实时了解进程的运行情况,及时发现问题并采取措施,确保系统的稳定性和性能。 本期EasyOps产品使用最佳实践,我们将为您揭晓&am…...
英语学习(过去篇)
一、询问别人一周的情况 1.日常活动词汇 1)I watched TV 我看了电视 2)I ate breakfast 我吃了早餐 3)I left the house 我离开了家 4)I did the dishes 我洗了碗 5)I washed my clothes …...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...