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…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
