windows驱动开发-内核编程技术汇总(六)
在驱动程序中使用文件
内核模式组件通过其对象名称引用文件,该对象名称是连接到文件的完整路径的 \DosDevices 。 在 Microsoft Windows 2000 及更高版本的操作系统上, \?? 等效于 \DosDevices。例如,C:\WINDOWS\example.txt 文件的对象名称 \DosDevices\C:\WINDOWS\example.txt,我们可以使用对象名称打开文件的句柄。
打开/读写文件
若要打开文件的句柄,请执行以下步骤:
- 创建 OBJECT_ATTRIBUTES 结构,并调用 InitializeObjectAttributes 宏来初始化结构。 将文件的对象名称指定为 InitializeObjectAttributes 的 ObjectName 参数;
- 通过将 OBJECT_ATTRIBUTES 结构传递给 IoCreateFile、 ZwCreateFile 或 ZwOpenFile,打开文件的句柄;
- 如果文件不存在, IoCreateFile 和 ZwCreateFile 将创建该文件,而 ZwOpenFile 将返回STATUS_OBJECT_NAME_NOT_FOUND;
请注意,驱动程序几乎始终使用 ZwCreateFile 或 ZwOpenFile ,而不是 IoCreateFile。
调用 IoCreateFile、 ZwCreateFile 或 ZwOpenFile 时,Windows 主管会创建一个新的文件对象来表示文件,并为对象提供一个打开的句柄。 此文件对象将一直保留,直到你关闭所有打开的句柄。
无论调用哪个例程,都必须传递所需的访问权限作为 DesiredAccess 参数。 这些权限必须涵盖驱动程序将执行的所有操作。 请求的相应访问权限如下:
- 从文件中读取:FILE_READ_DATA或GENERIC_READ
- 向文件写入数据:FILE_WRITE_DATA或GENERIC_WRITE
- 仅写入文件末尾:FILE_APPEND_DATA
- 读取文件的元数据,例如文件的创建时间:FILE_READ_ATTRIBUTES或GENERIC_READ
- 写入文件的元数据,例如文件的创建时间:FILE_WRITE_ATTRIBUTES或GENERIC_WRITE
下表列出了驱动程序可以对文件句柄执行的操作以及执行这些操作的相应例程。
- 从文件读取数据:ZwReadFile
- 将数据写入文件:ZwWriteFile
- 读取文件或文件句柄的元数据:ZwQueryInformationFile
- 写入文件或文件句柄的元数据:ZwSetInformationFile
若要指示开始读取或写入数据的文件中的位置,请分别将 ByteOffset 参数传递给 ZwReadFile 或 ZwWriteFile。
如果使用FILE_APPEND_DATA访问权限打开句柄,则所有数据将写入文件末尾,并忽略 ByteOffset 参数。
在某些情况下,I/O 管理器维护文件的当前文件位置指针。 可以通过为 ByteOffset 指定 NULL,在该位置开始读取或写入操作。
若要检查或更改有关文件的信息,请分别调用 ZwQueryInformationFile 或 ZwSetInformationFile。 将特定类型的信息指定为每个例程的 FileInformationClass 参数。 例如,将 FileInformationClass 设置为 FileBasicInformation 可以检查或更改 FILE_BASIC_INFORMATION 结构,该结构包含文件创建时间和上次访问时间的成员等。
使用当前文件位置
创建或打开文件时,可能导致 I/O 管理器创建与文件句柄关联的当前文件位置指针。 执行此操作后,可以读取和写入当前文件位置的数据,I/O 管理器将按读取或写入的字节数自动更新位置。
默认情况下,I/O 管理器不维护当前文件位置指针。 此默认值可提供效率,因为正确维护当前文件位置需要 I/O 管理器同步文件对象上的每个读取和写入操作。
若要创建具有关联的当前文件位置指针的句柄,请在 DesiredAccess 参数中将 SYNCHRONIZE 访问权限指定为 ZwCreateFile、 IoCreateFile 或 ZwOpenFile,并在 CreateOptions 或 OpenOptions 参数中指定FILE_SYNCHRONOUS_IO_ALERT或FILE_SYNCHRONOUS_IO_NONALERT。 请确保不要同时指定FILE_APPEND_DATA访问权限。
ZwReadFile 和 ZwWriteFile 自动更新当前文件位置指针,使其指向受操作影响的数据之外。 例如,如果从字节偏移量 101 开始读取 20 个字节, 则 ZwReadFile 会将当前文件位置更新为 121。
可以通过分别调用 ZwQueryInformationFile 或 ZwSetInformationFile 来检查或更改当前文件位置。 在任一情况下,将 FileInformationClass 参数设置为 FilePositionInformation。
注册表的使用
Windows 将注册表项表示为由对象管理器管理的执行对象。具体而言,每个键都有一个对象名称,你可以打开键的句柄。
用户模式应用程序访问与全局句柄相关的密钥,例如HKEY_LOCAL_MACHINE或HKEY_CURRENT_USER。 但是,这些句柄不适用于内核模式代码。 相反,可以按键的对象名称来引用键。 所有注册表项的根目录都是 \Registry 对象。 全局句柄对应于 \Registry 对象的后代,如下所示:
- HKEY_LOCAL_MACHINE:\Registry\Machine
- HKEY_USERS:\Registry\User
- HKEY_CLASSES_ROOT:没有等效的内核模式;
- HKEY_CURRENT_USER:没有简单的内核模式等效项;
驱动程序可以通过执行以下步骤操作注册表项对象:
- 打开注册表项对象的句柄;
- 通过调用相应的 ZwXxx密钥 例程来执行预期操作。 有关如何执行此操作的信息,请参阅 使用 Registry-Key 对象的句柄;
- 通过调用 ZwClose 关闭句柄;
打开/读写注册表
若要打开注册表项对象的句柄,请执行以下两个步骤:
- 创建 OBJECT_ATTRIBUTES 结构,并通过调用 InitializeObjectAttributes 对其进行初始化。 指定要操作的键的名称作为 InitializeObjectAttributes 的 ObjectName 参数。如果将 NULL 作为 RootDirectory 参数传递给 InitializeObjectAttributes, 则 ObjectName 必须是注册表项的完整路径,从 \Registry 开始。 否则, RootDirectory 必须是键的打开句柄,而 ObjectName 是相对于该键的路径;
- 通过调用 ZwCreateKey 或 ZwOpenKey 打开密钥对象的句柄,并将 OBJECT_ATTRIBUTES 结构传递给该对象。 如果该键尚不存在, ZwCreateKey 将创建密钥,而 ZwOpenKey 将返回STATUS_OBJECT_NAME_NOT_FOUND;
将 DesiredAccess 参数传递给包含所请求的访问权限的 ZwCreateKey 或 ZwOpenKey 。 必须指定允许驱动程序执行的操作的访问权限。 下面列出了可以执行的操作以及要请求的相应访问权限:
- 获取注册表项值: KEY_QUERY_VALUE或KEY_READ
- 设置注册表项值: KEY_SET_VALUE或KEY_WRITE
- 循环访问键的所有子项:KEY_ENUMERATE_SUB_KEYS或KEY_READ
- 创建子项: KEY_CREATE_SUB_KEY或KEY_WRITE
- 删除密钥: DELETE
还可以调用 IoOpenDeviceRegistryKey 和 IoOpenDeviceInterfaceRegistryKey ,分别打开特定于设备的注册表项和设备接口特定的句柄。
注意 对于对 ZwCreateKey、 ZwOpenKey、 IoOpenDeviceRegistryKey 和 IoOpenDeviceInterfaceRegistryKey 的调用,泛型访问权限GENERIC_READ和GENERIC_WRITE在含义上等效于键特定的访问权限,分别KEY_READ和KEY_WRITE,并且可用作这些特定于密钥的访问权限的替代项。
下面列出了驱动程序可以对打开键执行的操作以及要调用的相应例程。
- 检查密钥的属性,例如其名称或其子项的数目:ZwQueryKey
- 循环访问密钥的子项,检查每个子项的属性:ZwEnumerateKey
- 检查键值的属性,包括值的数据:ZwQueryValueKey
- 循环访问键的值,检查每个键的属性:ZwEnumerateValueKey
- 为与键关联的值设置数据:ZwSetValueKey
- 删除密钥:ZwDeleteKey
- 删除键值:ZwDeleteValueKey
驱动程序完成其操作后,必须调用 ZwClose 以关闭句柄,即使它已调用 ZwDeleteKey 来删除密钥。 (删除密钥后,其所有打开的句柄将变为无效,但驱动程序仍必须关闭 handle.)
下面的代码示例演示了如何为名为 \Registry\Machine\Software\MyCompanyMyApp 打开句柄,然后检索密钥数据并关闭句柄:
//
// Get the frame location from the registry key
// HKLM\SOFTWARE\MyCompany\MyApp.
// For example: "FrameLocation"="X:\\MyApp\\Frames"
//
HANDLE handleRegKey = NULL;
for (int n = 0; n < 1; n++)
{NTSTATUS status = NULL;UNICODE_STRING RegistryKeyName;OBJECT_ATTRIBUTES ObjectAttributes;RtlInitUnicodeString(&RegistryKeyName, L"\\Registry\\Machine\\Software\\MyCompany\\MyApp");InitializeObjectAttributes(&ObjectAttributes, &RegistryKeyName,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,NULL, // handleNULL);status = ZwOpenKey(&handleRegKey, KEY_READ, &ObjectAttributes);// If the driver cannot open the key, the driver cannot continue. // In this situation, the driver was probably set up incorrectly // and worst case, the driver cannot stream.if( NT_SUCCESS(status) == FALSE ) {break;}// The driver obtained the registry key.PKEY_VALUE_FULL_INFORMATION pKeyInfo = NULL;UNICODE_STRING ValueName;ULONG ulKeyInfoSize = 0;ULONG ulKeyInfoSizeNeeded = 0;// The driver requires the following value.RtlInitUnicodeString(&ValueName, L"FrameLocation");// Determine the required size of keyInfo.status = ZwQueryValueKey( handleRegKey,&ValueName,KeyValueFullInformation,pKeyInfo,ulKeyInfoSize,&ulKeyInfoSizeNeeded );// The driver expects one of the following errors.if( (status == STATUS_BUFFER_TOO_SMALL) || (status == STATUS_BUFFER_OVERFLOW) ){// Allocate the memory required for the key.ulKeyInfoSize = ulKeyInfoSizeNeeded;pKeyInfo = (PKEY_VALUE_FULL_INFORMATION) ExAllocatePoolWithTag( NonPagedPool, ulKeyInfoSizeNeeded, g_ulTag);if( NULL == pKeyInfo ){break;}RtlZeroMemory( pKeyInfo, ulKeyInfoSize );// Get the key data.status = ZwQueryValueKey( handleRegKey,&ValueName,KeyValueFullInformation,pKeyInfo,ulKeyInfoSize,&ulKeyInfoSizeNeeded );if( (status != STATUS_SUCCESS) || (ulKeyInfoSizeNeeded != ulKeyInfoSize) || (NULL == pKeyInfo) ){break;}// Fill in the frame location if it has not been filled in already.if ( NULL == m_szwFramePath ){m_ulFramePathLength = pKeyInfo->DataLength;ULONG_PTR pSrc = NULL;pSrc = (ULONG_PTR) ( (PBYTE) pKeyInfo + pKeyInfo->DataOffset);m_szwFramePath = (LPWSTR) ExAllocatePoolWithTag( NonPagedPool, m_ulFramePathLength, g_ulTag);if ( NULL == m_szwFramePath ){m_ulFramePathLength = 0;break;}// Copy the frame path.RtlCopyMemory(m_szwFramePath, (PVOID) pSrc, m_ulFramePathLength);}// The driver is done with the pKeyInfo.xFreePoolWithTag(pKeyInfo, g_ulTag);} // if( (status == STATUS_BUFFER_TOO_SMALL) || (status == STATUS_BUFFER_OVERFLOW) )
} // Get the Frame location from the registry key.// All done with the registry.
if (NULL != handleRegKey)
{ZwClose(handleRegKey);
}
系统缓存内存中的密钥更改,并每隔几秒将其写入磁盘。 若要强制更改磁盘的密钥,请调用 ZwFlushKey。
若要通过更简单的接口操作注册表,驱动程序还可以调用 RtlXxx注册表Xxx 例程。
注册表运行时库例程
若要操作注册表项,驱动程序可以调用 RtlXxxRegistryXxx 例程,该例程提供比 ZwXxxKey 例程更简单的接口。 执行此操作时,驱动程序不需要打开和关闭句柄;相反,驱动程序按名称引用密钥。
将 RelativeTo 和 Path 参数传递给每个 RtlXxx注册表Xxx 例程。 如果 RelativeTo 是RTL_REGISTRY_ABSOLUTE, 则 Path 指定项的完整路径,从 \Registry 根目录开始。 如果 RelativeTo 是RTL_REGISTRY_HANDLE, 则 Path 实际上是一个打开的句柄。Relative 的其他 RTL_REGISTRY_XXX 值 指定 键的公共根路径;在这些情况下, Path 指定相对于该根的路径。 例如,RTL_REGISTRY_USER要求 Path 相对于当前用户的注册表设置。 此值等效于在用户模式应用程序中指定HKEY_CURRENT_USER。)
下面列出了驱动程序可以通过调用 RtlXxx注册表Xxx 例程执行的操作:
- 创建注册表项:RtlCreateRegistryKey
- 检查是否存在注册表项:RtlCheckRegistryKey
- 检查一个或多个注册表项值:RtlQueryRegistryValues
- 编写注册表项值:RtlWriteRegistryValue
- 删除注册表项值:RtlDeleteRegistryValue
下面的代码示例演示如何将 \Registry\Machine\System\KeyName 的 ValueName 设置为 ULONG 值0xFF:
NTSTATUS status;
ULONG data = 0xFF;status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,(PWCSTR)L"\\Registry\\Machine\\System\\KeyName",(PWCSTR)L"ValueName",REG_DWORD,&data,sizeof(ULONG));
尽管使用 RtlXxxRegistryXxx 例程(而不是 Zw Xxx Key 例程)时写入的代码行更少,但执行某些操作需要后一行代码。 例如,不存在与 ZwEnumerateKey 对应的 RtlXxxRegistryXxx 例程。
如果对同一键执行多个操作, 则 ZwXxx密钥 例程会更高效—你可以对每个操作使用相同的打开句柄。 相比之下, RtlXxx注册表Xxx 例程打开和关闭每个操作的新句柄。
相关文章:
windows驱动开发-内核编程技术汇总(六)
在驱动程序中使用文件 内核模式组件通过其对象名称引用文件,该对象名称是连接到文件的完整路径的 \DosDevices 。 在 Microsoft Windows 2000 及更高版本的操作系统上, \?? 等效于 \DosDevices。例如,C:\WINDOWS\example.txt 文件的对象名…...
Windows Server 2019虚拟机安装
目录 第一步、准备工作 第二步、部署虚拟机 第三步、 Windows Server 2019系统启动配置 第一步、准备工作 下载Windows Server 2019系统镜像 官网下载地址:Windows Server 2019 | Microsoft Evaluation Center VMware Workstation 17下载地址: 链…...
【软件工程】详细设计
目录 前言详细设计算法设计工具——判定表 前言 软件工程生命周期分为八个阶段: 问题定义—>可行性研究—>需求分析 —>概要设计—>详细设计—>编码与单元测试 —>综合测试—>软件维护 这节我们讲的是软件开发流程中的一个阶段,需求…...
在K8S中,集群可以做哪些优化?
在Kubernetes(简称K8s)集群中,可以进行多种优化以提升性能、稳定性和资源利用率。以下是一些常见的优化措施: 控制面组件优化: kube-apiserver 高可用与扩展:通过配置多个API服务器实例并使用负载均衡器分发…...
【C++】从零开始认识多态
送给大家一句话: 一个犹豫不决的灵魂,奋起抗击无穷的忧患,而内心又矛盾重重,真实生活就是如此。 – 詹姆斯・乔伊斯 《尤利西斯》 _φ(* ̄ω ̄)ノ_φ(* ̄ω ̄)&…...
为什么叫“机器学习”Machine Learning 而不是“计算机学习”——深度学习Note
有一门学科“机器学习”火了起来,它是计算机科学与数学结合的产物,它的目的是使计算机“聪明”起来,实现人工智能。可是,令人困惑的是它明明就是计算机学习,为什么不叫“计算机学习”而叫“机器学习”呢?这…...
Spring Boot集成RabbitMQ-之6大模式总结
A.集成 一:添加依赖 在pom.xml文件中添加spring-boot-starter-amqp依赖,以便使用Spring Boot提供的RabbitMQ支持: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp&…...
后端开发面经系列 -- 滴滴C++一面面经
滴滴C一面面经 公众号:阿Q技术站 来源:https://www.nowcoder.com/feed/main/detail/38cf9704ef084e27903d2204352835ef 1、const在C和C区别,const定义的类成员变量如何初始化? 区别 C中的const: 在C中,c…...
Three.js的几何形状
在创建物体的时候,需要传入两个参数,一个是几何形状【Geometry】,一个是材质【Material】 几何形状主要是存储一个物体的顶点信息,在Three中可以通过指定一些特征来创建几何形状,比如使用半径来创建一个球体。 立方体…...
设计模式——单例模式(Singleton)
单例模式(Singleton Pattern)是设计模式中的一种,它确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。这种模式在多种场景下都非常有用,比如配置文件的读取、数据库连接的创建、线程池的管理等。 实现…...
springboot3项目练习详细步骤(第二部分:文章分类模块)
新增文章分类 接口文档 业务实现 参数校验 文章分类列表 接口文档 业务实现 获取文章分类详情 接口文档 业务实现 更新文章分类 接口文档 业务实现 分组校验 问题 概念 实现步骤 总结 删除文章分类 接口文档 业务实现 该模块大部分请求的路径相同&…...
VUE中父组件向子组件进行传值
在 Vue 中,父组件向子组件传值主要通过在子组件的标签上绑定属性(props)的方式来实现。以下是一个具体的示例。 父组件(ParentComponent.vue): <template><div><!-- 父组件中使用子组件,并传…...
alpine安装中文字体
背景 最近在alpine容器中需要用到中文字体处理视频,不想从本地拷贝字体文件, 所以找到了一个中文的字体包font-droid-nonlatin,在此记录下。 安装 apk add font-droid-nonlatin安装好后会出现在目录下/usr/share/fonts/droid-nonlatin/ 这…...
JavaScript学习—JavaScript高级
原型链和继承 在 JavaScript 中,每个对象都有一个原型(prototype),这个原型指向另一个对象。这个链式的原型关系被称为原型链。当访问一个对象的属性时,如果该对象没有该属性,它会沿着原型链向上查找&…...
CompletableFuture使用案例
优化代码时,除了Async注解,项目中如何使用多线程异步调用? 举个例子,去餐厅吃饭的时候。先点餐,厨师做菜,在厨师做菜的时候打游戏,然后根据厨师做的菜的口味去买矿泉水还是可乐。这样࿰…...
安卓使用so库
最近需要给小伙伴扫盲一下如何使用Android Studio 生成一个SO文件,网上找了很多都没有合适的样例,那只能自己来写一个了。 原先生成SO是一个很麻烦的事情,现在Android Studio帮忙做了很多的事情,基本只要管好自己的C代码即可。 …...
【介绍下LeetCode的使用方法】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...
重学java 30.API 1.String字符串
于是,虚度的光阴换来了模糊 —— 24.5.8 一、String基础知识以及创建 1.String介绍 1.概述 String类代表字符串 2.特点 a.Java程序中的所有字符串字面值(如“abc”)都作为此类的实例(对象)实现 凡是带双引号的,都是String的对象 String s "abc&q…...
【区块链】共识算法简介
共识算法简介 区块链三要素: 去中心化共识算法智能合约 共识算法作为区块链三大核心技术之一,其重要性不言而喻。今天就来简单介绍共识算法的基本知识。 最简单的解释,共识算法就是要让所有节点达成共识,保证少数服从多数&#x…...
Qt---day2-信号与槽
1、思维导图 2、 拖拽式 源文件 #include "mywidget.h" #include "ui_mywidget.h" MyWidget::MyWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MyWidget) { ui->setupUi(this); //按钮2 this->btn2new QPushButton("按钮2",th…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
