windows驱动开发-内核编程技术汇总(四)

编写Bug检查原因回调例程

驱动程序可以选择性地提供 KBUGCHECK_REASON_CALLBACK_ROUTINE 回调函数,系统在写入故障转储文件后调用该函数。

在此回调中,驱动程序可以:

  • 将特定于驱动程序的数据添加到故障转储文件;
  • 将设备重置为已知状态;

使用以下例程注册和删除回调:

  • KeRegisterBugCheckReasonCallback
  • KeDeregisterBugCheckReasonCallback

此回调类型是重载的,其行为会根据注册时提供的 KBUGCHECK_CALLBACK_REASON 常量值而更改。

Bug 检查回调例程限制

bug 检查回调例程在 IRQL = HIGH_LEVEL 处执行,这对其可执行的操作施加了严格的限制。

bug 检查回调例程无法:

  • 分配内存;
  • 访问可分页内存;
  • 使用任何同步机制;
  • 调用必须在 IRQL = DISPATCH_LEVEL 或更低位置执行的任何例程;

Bug 检查回调例程保证运行而不会中断,因此无需同步。 如果 bug 检查例程尝试使用任何同步机制获取锁,系统会死锁。请记住,在检查 bug 时,数据结构或列表可能处于不一致状态,因此在访问受锁保护的数据结构时应小心。 例如,在浏览列表时,应添加上限检查,并验证链接是否指向有效内存,以防有循环列表或链接指向无效地址。

Bug 检查回调例程可以使用 MmIsAddressValid 来检查访问地址是否会导致页面错误。 由于例程在没有中断的情况下运行,并且其他核心被冻结,这满足了该函数的同步要求。 在 Bug 检查回调中延迟它们之前,应始终使用 MmIsAddressValid 检查可能分页或无效的内核地址进行检查,因为页面错误将导致双重错误,并可能阻止写入转储。

驱动程序的 bug 检查回调例程可以安全地使用 READ_PORT_XXX、READ_REGISTER_XXX、WRITE_PORT_XXX 和 WRITE_REGISTER_XXX 例程与驱动程序的设备通信。

实现 KbCallbackAddPages 回调例程

内核模式驱动程序可以实现 KbCallbackAddPages 类型的KBUGCHECK_REASON_CALLBACK_ROUTINE回调函数,以在 bug 检查发生时向故障转储文件添加一页或多页数据。 若要向操作系统注册此例程,驱动程序会调用 KeRegisterBugCheckReasonCallback 例程。 在驱动程序卸载之前,它必须调用 KeDeregisterBugCheckReasonCallback 例程来删除注册。

从 Windows 8 开始,在内核内存转储或完整内存转储期间调用已注册的 KbCallbackAddPages 例程。 在早期版本的 Windows 中,注册的 KbCallbackAddPages 例程在内核内存转储期间调用,但不在完整内存转储期间调用。 默认情况下,内核内存转储仅包含发生 bug 检查时 Windows 内核正在使用的物理页,而完整的内存转储则包含 Windows 使用的所有物理内存。 默认情况下,完整内存转储不包括平台固件使用的物理内存。

KbCallbackAddPages 例程可以提供特定于驱动程序的数据,以添加到转储文件。 例如,对于内核内存转储,此附加数据可以包括未映射到虚拟内存中的系统地址范围但包含有助于调试驱动程序的信息的物理页。 KbCallbackAddPages 例程可能会将未映射或映射到虚拟内存中用户模式地址的任何驱动程序拥有的物理页面添加到转储文件。

当出现 bug 检查时,操作系统会调用所有已注册的 KbCallbackAddPages 例程,以轮询驱动程序以查找要添加到故障转储文件中的数据。 每次调用都会将一页或多页连续数据添加到故障转储文件。 KbCallbackAddPages 例程可以为起始页提供虚拟地址或物理地址。 如果在调用期间提供了多个页面,则这些页面在虚拟内存或物理内存中是连续的,具体取决于起始地址是虚拟地址还是物理地址。 若要提供不连续的页面, KbCallbackAddPages 例程可以在 KBUGCHECK_ADD_PAGES 结构中设置一个标志,以指示它具有其他数据,并且必须再次调用。

与向次要故障转储区域追加数据的 KbCallbackSecondaryDumpData 例程不同, KbCallbackAddPages 例程会将数据页添加到主要故障转储区域。 在调试期间,主要故障转储数据比辅助故障转储数据更易于访问。

操作系统填充 ReasonSpecificData 指向的 KBUGCHECK_ADD_PAGES 结构的 BugCheckCode 成员。 KbCallbackAddPages 例程必须设置此结构的标志、地址和计数成员的值。

在首次调用 KbCallbackAddPages 之前,操作系统会将 Context 初始化为 NULL。 如果多次调用 KbCallbackAddPages 例程,操作系统将保留回调例程在上一次调用中写入上下文的成员的值。

KbCallbackAddPages 例程在可以执行的操作方面非常有限。

实现 KbCallbackDumpIo 回调例程

内核模式驱动程序可以实现 KbCallbackDumpIo 类型的KBUGCHECK_REASON_CALLBACK_ROUTINE回调函数,以在每次将数据写入故障转储文件时执行工作。 系统会在 ReasonSpecificData 参数中传递指向 KBUGCHECK_DUMP_IO 结构的指针。 Buffer 成员指向当前数据,BufferLength 成员指定其长度。 Type 成员指示当前正在写入的数据类型,例如转储文件头信息、内存状态或驱动程序提供的数据。 有关可能的信息类型的说明,请参阅 KBUGCHECK_DUMP_IO_TYPE 枚举。

系统可以按顺序或无序写入故障转储文件。 如果系统按顺序写入故障转储文件,则 ReasonSpecificData 的 Offset 成员为 -1;否则,Offset 设置为故障转储文件中的当前以字节为单位的偏移量。

当系统按顺序写入文件时, 在编写类型 = KbDumpIoHeader () 的标头信息时,它会调用每个 KbCallbackDumpIo 例程一次或多次,在编写故障转储文件的main正文 (Type = KbDumpIoBody) 时调用一次或多次, (Type = KbDumpIoSecondaryDumpData) 编写辅助转储数据时调用一次或多次。 系统完成编写故障转储文件后,会调用 Buffer = NULL、 BufferLength = 0 和 Type = KbDumpIoComplete 的回调。

KbCallbackDumpIo 例程main目的是允许将系统故障转储数据写入磁盘以外的设备。 例如,监视系统状态的设备可以使用回调来报告系统检查发出 bug,并提供故障转储以供分析。

使用 KeRegisterBugCheckReasonCallback 注册 KbCallbackDumpIo 例程。 驱动程序随后可以使用 KeDeregisterBugCheckReasonCallback 例程删除回调。 如果驱动程序可以卸载,则必须删除其 DRIVER_UNLOAD 回调函数中的任何已注册回调。

实现 KbCallbackSecondaryDumpData 回调例程

内核模式驱动程序可以实现 KbCallbackSecondaryDumpData 类型的KBUGCHECK_REASON_CALLBACK_ROUTINE回调函数,以提供要追加到故障转储文件的数据。

系统设置 ReasonSpecificData 指向的KBUGCHECK_SECONDARY_DUMP_DATA结构的 InBuffer、InBufferLength、OutBuffer 和 MaximumAllowed 成员。 MaximumAllowed 成员指定例程可以提供的最大转储数据量。

OutBuffer 成员的值确定系统是请求驱动程序转储数据的大小还是数据本身的大小,如下所示:

  • 如果 KBUGCHECK_SECONDARY_DUMP_DATA 的 OutBuffer 成员为 NULL,则系统仅请求大小信息。 KbCallbackSecondaryDumpData 例程填充 OutBuffer 和 OutBufferLength 成员;
  • 如果 KBUGCHECK_SECONDARY_DUMP_DATA 的 OutBuffer 成员等于 InBuffer 成员,则系统正在请求驱动程序的辅助转储数据。 KbCallbackSecondaryDumpData 例程填充 OutBuffer 和 OutBufferLength 成员,并将数据写入 OutBuffer 指定的缓冲区;

KBUGCHECK_SECONDARY_DUMP_DATA 的 InBuffer 成员指向一个小缓冲区供例程使用。 InBufferLength 成员指定缓冲区的大小。 如果要写入的数据量小于 InBufferLength,则回调例程可以使用此缓冲区向系统提供故障转储数据。 然后,回调例程将 OutBuffer 设置为 InBuffer , 将 OutBufferLength 设置为写入缓冲区的实际数据量。

必须写入大于 InBufferLength 的数据量的驱动程序可以使用其自己的缓冲区来提供数据。 必须在执行回调例程之前分配此缓冲区,并且必须驻留在常驻内存 (,例如非分页池) 。 然后,回调例程将 OutBuffer 设置为指向驱动程序的缓冲区,将 OutBufferLength 设置为缓冲区中要写入故障转储文件的数据量。

要写入故障转储文件的每个数据块都使用 KBUGCHECK_SECONDARY_DUMP_DATA 结构的 Guid 成员的值进行标记。 使用的 GUID 对于驱动程序必须是唯一的。 若要显示与此 GUID 对应的辅助转储数据,可以在调试器扩展中使用 .enumtag 命令或 IDebugDataSpaces3::ReadTagged 方法。 

驱动程序可以将具有相同 GUID 的多个块写入故障转储文件,但这种做法非常糟糕,因为调试器只能访问第一个块。 注册多个 KbCallbackSecondaryDumpData 例程的驱动程序应为每个回调分配唯一的 GUID。

使用 KeRegisterBugCheckReasonCallback 注册 KbCallbackSecondaryDumpData 例程。 驱动程序随后可以使用 KeDeregisterBugCheckReasonCallback 例程删除回调例程。 如果驱动程序可以卸载,则必须删除其 DRIVER_UNLOAD 回调函数中的任何已注册回调例程。

实现 KbCallbackTriageDumpData 回调例程

从 Windows 10 版本 1809 和 Windows Server 2019 开始,内核模式驱动程序可以实现 KbCallbackTriageDumpData 类型的KBUGCHECK_REASON_CALLBACK_ROUTINE回调函数,以标记虚拟内存范围以包含在新使用的内核微型转储中。 这可确保小型转储将包含指定的范围,以便可以使用在内核转储中工作的相同调试器命令来访问它们。 目前,这是针对win 10的小型转储实现的,这意味着捕获了内核或更大的转储,然后从较大的转储创建了一个小型转储。 默认情况下,大多数系统都针对自动/内核转储进行配置,并且系统会在崩溃后下次启动时自动创建一个小型转储。

系统在 ReasonSpecificData 参数中传递指向 KBUGCHECK_TRIAGE_DUMP_DATA 结构的指针,该结构包含有关 Bug 检查的信息,以及驱动程序用来返回其初始化和填充的数据数组的 OUT 参数。

在下面的示例中,驱动程序配置会审转储数组,然后注册回调的最小实现。 驱动程序将使用 数组将两个全局变量添加到小型转储:

#include <ntosp.h>// Header definitions//// The maximum count of ranges the driver will add to the array.// This example is only adding max 3 ranges with some extra.//#define MAX_RANGES 10//// This should be large enough to hold the maximum number of KADDRESS_RANGE// which the driver expects to add to the array.//#define ARRAY_SIZE ((FIELD_OFFSET(KTRIAGE_DUMP_DATA_ARRAY, Blocks)) + (sizeof(KADDRESS_RANGE) * MAX_RANGES))// Globals static PKBUGCHECK_REASON_CALLBACK_RECORD gBugcheckTriageCallbackRecord; 
static PKTRIAGE_DUMP_DATA_ARRAY gTriageDumpDataArray;//// This is a global variable which the driver wants to be available in// the kernel minidump. A real driver may add more address ranges.//ULONG64 gDriverData1 = 0xAAAAAAAA;
PULONG64 gpDriverData2;// FunctionsVOID 
ExampleBugCheckCallbackRoutine( KBUGCHECK_CALLBACK_REASON Reason, PKBUGCHECK_REASON_CALLBACK_RECORD Record, PVOID Data, ULONG Length ) 
{ PKBUGCHECK_TRIAGE_DUMP_DATA DumpData; UNREFERENCED_PARAMETER(Reason);UNREFERENCED_PARAMETER(Record);UNREFERENCED_PARAMETER(Length);DumpData = (PKBUGCHECK_TRIAGE_DUMP_DATA) Data;if ((DumpData->Flags & KB_TRIAGE_DUMP_DATA_FLAG_BUGCHECK_ACTIVE) == 0) {return;}if (gTriageDumpDataArray == NULL){return;}//// Add the dynamically allocated global pointer and buffer once validated.//if ((gpDriverData2 != NULL) && (MmIsAddressValid(gpDriverData2))) {//// Add the address of the global itself a well as the pointed data// so you can use the global to access the data in the debugger// by running a command like "dt example!gpDriverData2"//KeAddTriageDumpDataBlock(gTriageDumpDataArray, &gpDriverData2, sizeof(PULONG64));KeAddTriageDumpDataBlock(gTriageDumpDataArray, gpDriverData2, sizeof(ULONG64));}//// Pass the array back for processing.//DumpData->DataArray = gTriageDumpDataArray; return; 
}// Setup FunctionNTSTATUS
SetupTriageDataCallback(VOID) 
{ PVOID pBuffer;NTSTATUS Status;BOOLEAN bSuccess;//// Call this function from DriverEntry.// // Allocate a buffer to hold a callback record and triage dump data array// in the non-paged pool. //pBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,sizeof(KBUGCHECK_REASON_CALLBACK_RECORD) + ARRAY_SIZE,'Xmpl');if (pBuffer == NULL) {return STATUS_NO_MEMORY;}RtlZeroMemory(pBuffer, sizeof(KBUGCHECK_REASON_CALLBACK_RECORD));gBugcheckTriageCallbackRecord = (PKBUGCHECK_REASON_CALLBACK_RECORD) pBuffer;KeInitializeCallbackRecord(gBugcheckTriageCallbackRecord); gTriageDumpDataArray =(PKTRIAGE_DUMP_DATA_ARRAY) ((PUCHAR) pBuffer + sizeof(KBUGCHECK_REASON_CALLBACK_RECORD));// // Initialize the dump data block array. // Status = KeInitializeTriageDumpDataArray(gTriageDumpDataArray, ARRAY_SIZE);if (!NT_SUCCESS(Status)) {ExFreePoolWithTag(pBuffer, 'Xmpl');gTriageDumpDataArray = NULL;gBugcheckTriageCallbackRecord = NULL;return Status;}//// Set up a callback record//    bSuccess = KeRegisterBugCheckReasonCallback(gBugcheckTriageCallbackRecord, ExampleBugCheckCallbackRoutine, KbCallbackTriageDumpData, (PUCHAR)"Example"); if ( !bSuccess ) {ExFreePoolWithTag(gTriageDumpDataArray, 'Xmpl');gTriageDumpDataArray = NULL;return STATUS_UNSUCCESSFUL;}//// It is possible to add a range to the array before bugcheck if it is// guaranteed to remain valid for the lifetime of the driver.// The value could change before bug check, but the address and size// must remain valid.//KeAddTriageDumpDataBlock(gTriageDumpDataArray, &gDriverData1, sizeof(gDriverData1));//// For an example, allocate another buffer here for later addition tp the array.//gpDriverData2 = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(ULONG64), 'Xmpl');if (gpDriverData2 != NULL) {*gpDriverData2 = 0xBBBBBBBB;}return STATUS_SUCCESS;
} // Deregister functionVOID CleanupTriageDataCallbacks() 
{ //// Call this routine from DriverUnload//if (gBugcheckTriageCallbackRecord != NULL) {KeDeregisterBugCheckReasonCallback( gBugcheckTriageCallbackRecord );ExFreePoolWithTag( gBugcheckTriageCallbackRecord, 'Xmpl' );gTriageDumpDataArray = NULL;}}

只有非分页内核模式地址应与此回调方法一起使用。

验证是否设置了KB_TRIAGE_DUMP_DATA_FLAG_BUGCHECK_ACTIVE标志后,只能从 KbCallbackTriageDumpData 例程使用 MmIsAddressValid 函数。 当前始终需要设置此标志,但如果未设置其他同步,则调用例程是不安全的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/7336.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cmd输入mysql -u root -p无法启动

问题分析&#xff1a;cmd输入mysql -u root -p无法启动 解决方法&#xff1a;配置系统环境变量 1.找到mysql安装文件下的bin文件&#xff1a;&#xff08;复制改文件地址,如下图所示&#xff09; 2.电脑桌面下方直接搜索环境变量并进入&#xff0c;如下图 3.点击环境变量&a…

nginx--防盗链

盗链 通过在自己网站里面引用别人的资源链接,盗用人家的劳动和资源 referer referer是记录打开一个页面之前记录是从哪个页面跳转过来的标记信息 正常的referer信息 none&#xff1a;请求报文首部没有referer首部&#xff0c;比如用户直接在浏览器输入域名访问web网站&…

java09基础(构造方法 继承)

目录 一. 构造方法 1. 构造方法 2. 构造代码块 二. 继承 1. 基本概念 2. protected 关键字 3. 构造方法的访问特点 4. 成员变量的访问特点 5. 成员方法的访问特点 6. 向上向下转型 6.1 向上转型 6.2 向下转型 一. 构造方法 1. 构造方法 初始化一个新的对象 构建、创…

2024年03月 Scratch 图形化(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch图形化等级考试(1~4级)全部真题・点这里 一、单选题(共10题,共30分) 第1题 圆点角色的程序如下图1所示(角色默认方向90),运行程序,输入“HLHLHLHL”后得到的结果如下图2所示,如果想得到下图3中的结果,应该输入的字符串是?( ) A:HLLLHLLL B:LLLLLLL…

【docker 】 push 镜像提示:denied: requested access to the resource is denied

往 Docker Registry &#xff08;私服&#xff09;push 镜像提示&#xff1a;denied: requested access to the resource is denied 镜像push 语法&#xff1a;docker push <registry-host>:<registry-port>/<repository>:<tag> docker push 192.16…

递归陷阱七例

目录 栈溢出 无限递归 大常数参数 递归深度过大 重复计算 函数调用开销 递归与迭代的选择 总结 递归是一种强大的编程技术&#xff0c;它允许函数调用自身。递归在很多情况下可以简化代码&#xff0c;使问题更容易理解和解决。然而&#xff0c;递归也容易导致一些常见的…

C语言—控制语句

控制语句就是用来实现对流程的选择、循环、转向和返回等控制行为。 分支语句 if语句 基本结构 if(表达式) { 语句块1&#xff1b; } else { 语句块2&#xff1b; } 执行顺序&#xff1a; 如果表达式判断成立&#xff08;即表达式为真&#xff09;&#xff0c;则执行语句块…

Python量化炒股的统计数据图

Python量化炒股的统计数据图 单只股票的收益统计图 查看单只股票的收盘价信息 单击聚宽JoinQuant量化炒股平台中的“策略研究/研究环境”命令&#xff0c;进入Jupyter Notebook的研究平台。然后单击“新建”按钮&#xff0c;创建Python3文件&#xff0c;输入如下代码如下&am…

Qt中的数据库

一. 创建数据库类:QSqlIDatabase类 1.定义数据库是使用什么样的数据库&#xff0c;默认支持:SQLITE&#xff0c;与MYSQL使用静志函数:addDatabase("XXX") 2.创建并打开数据库文件: setDatabase("指定一个数据库文件的路径") 3.open()打开数据库文件 二…

面试集中营—Spring篇

Spring 框架的好处 1、轻量&#xff1a;spring是轻量的&#xff0c;基本的版本大约2MB&#xff1b; 2、IOC&#xff1a;控制反转&#xff0c;Spring的IOC机制使得对象之间的依赖不再需要我们自己来控制了&#xff0c;而是由容易来控制&#xff0c;一个字&#xff1a;爽&#xf…

【Excel VBA】深入探索VBScript中的Choose函数

深入探索VBScript中的Choose函数 在编程实践中&#xff0c;我们会遇到大量的If……ElseIf……搞得代码异常的庞大。 今天有个VBA的学生&#xff0c;突然问田辛老师有没有好的办法。 于是&#xff0c; 田辛老师发现还真有办法。 也就是Choose函数。Choose函数可以来优化代码逻辑…

【网络】什么是NAT技术

在当今互联网的环境中&#xff0c;NAT&#xff08;Network Address Translation&#xff09;技术扮演着至关重要的角色&#xff0c;它是网络通信中的一项核心技术&#xff0c;为我们的网络连接提供了便利、安全性和灵活性。本文将介绍NAT技术&#xff0c;探讨其原理、不同类型以…

算法训练营第二十天 | LeetCode 110平衡二叉树、LeetCode 257 二叉树的所有路径、LeetCode 404 左叶子之和

LeetCode 110 平衡二叉树 递归写法很简单&#xff0c;直接自底向上每个节点判断是否为空&#xff0c;为空说明该层高度为0。不为空用一个int型变量l记录左子树高度&#xff08;递归调用该函数自身&#xff09;&#xff0c;一个int型变量r记录右子树高度&#xff08;同样递归调…

Docker——consul的容器服务更新与发现

一、什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点的分布式架构&#xff0c;起初的解决手段…

【学习AI-相关路程-工具使用-自我学习-cudavisco-开发工具尝试-基础样例 (2)】

【学习AI-相关路程-工具使用-自我学习-cuda&visco-开发工具尝试-基础样例 &#xff08;2&#xff09;】 1、前言2、环境说明3、总结说明4、工具安装0、验证cuda1、软件下载2、插件安装 5、软件设置与编程练习1、创建目录2、编译软件进入目录&创建两个文件3、编写配置文…

Rust Postgres实例

Rust Postgres介绍 Rust Postgres是一个纯Rust实现的PostgreSQL客户端库&#xff0c;无需依赖任何外部二进制文件2。这意味着它可以轻松集成到你的Rust项目中&#xff0c;提供对PostgreSQL的支持。 特点 高性能&#xff1a;Rust Postgres提供了高性能的数据库交互功能&#…

js api part4

其他事件 页面加载事件 外部资源&#xff08;如图片、外联CSS和JavaScript等&#xff09;加载完毕时触发的事件 原因&#xff1a;有些时候需要等页面资源全部处理完了做一些事情&#xff0c;老代码喜欢把 script 写在 head 中&#xff0c;这时候直接找 dom 元素找不到。 事件…

获取转转数据,研究完转转请求,tx在算法方面很友好。

本篇文章仅供学习讨论。 文章中涉及到的代码、实例&#xff0c;仅是个人日常学习研究的部分成果。 如有不当&#xff0c;请联系删除。 在研究完阿里的算法以后&#xff08;其实很难说研究完&#xff0c;还有很多内容没有研究透&#xff0c;只能说暂时告一段落&#xff09;&…

服务智能化公共生活场景人员检测计数,基于YOLOv5全系列参数模型【n/s/m/l/x】开发构建公共生活场景下人员检测计数识别系统

在当今社会&#xff0c;随着科技的飞速发展&#xff0c;各种智能化系统已广泛应用于各个领域&#xff0c;特别是在人员密集、流动性大的场合&#xff0c;如商场、火车站、景区等&#xff0c;智能人员检测计数系统发挥着至关重要的作用。特别是在特殊时期&#xff0c;如节假日、…

cmake进阶:变量的作用域说明三(从函数作用域方面)

一. 简介 前一篇文章从函数作用域方面学习了 变量的作用域。文章如下&#xff1a; cmake进阶&#xff1a;变量的作用域说明一&#xff08;从函数作用域方面&#xff09;-CSDN博客cmake进阶&#xff1a;变量的作用域说明二&#xff08;从函数作用域方面&#xff09;-CSDN博客…