这篇文章解释了如何使用驱动程序验证工具来分析崩溃转储文件。

使用Microsoft驱动程序验证工具

如果您曾经使用Windows的调试工具来分析崩溃转储,那么毫无疑问,您已经使用WinDbg打开了一个崩溃转储文件。WinDbg将对崩溃文件执行内部分析,并建议您从!analyze命令开始。该命令输出堆栈以及许多其他信息。执行此操作时,堆栈底部将显示转换为内核模式的线程,然后从那里开始,您向上遍历堆栈以查看是否存在罪魁祸首驱动程序。虽然这是一种可靠的调试技术,但有时崩溃转储或其中的一组将无法分析。内存中没有任何模式可以指出导致系统崩溃的原因,或者内存可能已损坏,因为崩溃转储文件实际上指向Ntsokrnl.exe或win32k.sys。
有一种方法可以通过使用Microsoft驱动程序验证工具将无法处理的崩溃转换为可分析的崩溃。此工具随Windows的每个版本一起提供,不是单独安装的。它不可见,因此不在“开始”菜单或“控制面板”中的“管理工具”中。通过在“开始”菜单的“运行”框中键入“verifier”来启动它,但是为了使用它,您应该知道它是如何工作的。本文将介绍如何使用此工具将无法分析的崩溃转换为可分析的崩溃。驱动程序验证工具包含许多选项,其中一些应该严格避免。
要启动驱动程序验证程序,请在“开始”菜单的“运行”框中键入“Verifier.exe”。第一个数据框显示列表选项。选择的选项是“创建自定义设置(用于代码开发人员)”。避免使用默认的“标准设置”选项。单击“下一步”后,我们选择“从完整列表中选择各个设置”。注意,我们没有选择任何默认设置。单击Next之后,我们会看到一个选项列表,范围从“特殊池”到“杂项检查”。实际上,除了“低资源模拟”之外,我们都选择了它们。低资源模拟正是这样一种设置,因此我们不希望重新启动并让任何设备驱动程序实际测试其行为,因为它的资源是模拟的,并且正在被有目的地耗尽。“特殊池”选项将在本文后面讨论。让我们首先检查“强制IRQL检查”。

假设一个驱动程序接触到一段分页内存。IRQL当前处于被动级别,这是所有用户模式代码运行的IRQL。但是,由于驱动程序已经接触到一块内存,内存管理器必须将该数据引入物理RAM并将其连接到该分页虚拟地址。现在,驱动程序执行一个操作,使IRQL上升到DISPATCH_LEVEL,并立即再次引用同一个分页缓冲区。要想让它显示为被操作系统捕获的bug,内存管理器需要在第一次引用、IRQL提升和第二次引用之间的少量指令发生的很短时间内,决定是否需要重用(或发送到分页文件)。这是极不可能的。“强制IRQL检查”所做的是,在启用该选项验证驱动程序时,它会导致IRQL移动到分派级别或更高级别。内存管理器获取连接到分页池虚拟内存的系统工作集的所有页,并断开这些页与物理内存的连接。

请注意,工作集是分配给进程的物理内存量,该量由内存管理器确定,因为操作系统已根据内存需求和分页速率监视该进程的行为。所以现在,如果这个驱动程序在DISPATCH_LEVEL再次访问这个缓冲区,它将生成一个页面错误,因为内存管理器将不得不去修复那个虚拟内存和那个物理内存之间的连接。此时内存管理器将检查当前的IRQL。它将看到它是DISPATCH_LEVEL或更高级别,然后确定这是一个非法操作,从而导致系统崩溃。这就是你想要的。您希望找到哪个驱动程序有足够的错误,以至于在执行某些非法操作时被抓到,这些操作将使系统在某些最终用户的计算机上或在某些不受控制的环境中崩溃。强制IRQL检查将揭示哪种驱动程序确实存在这些类型的错误,并因此暴露罪魁祸首驱动程序。

池跟踪选项对于驱动程序内存泄漏非常有用。I/O验证和增强的I/O验证使操作系统驱动程序验证程序代码对驱动程序传递到的和驱动程序传递回系统的数据结构执行一些严格的检查。这种数据结构称为中断请求包(IRP)。IRP的结构有一些特殊的规则。它必须指向有效的结构。它必须有一组一致的值。因此,驱动验证器将在驱动程序对其进行操作后检查该包,以确保其仍处于一致状态。

要创建一组步骤来将无法分析的崩溃转换为可分析的崩溃,我们必须认识到某些崩溃与某些条件相关,这些条件由这些选项描述。因此,在这些选项后单击Next,选择“从列表中选择驱动程序”。不要选择“自动选择系统上安装的所有驱动程序”。此时,菜单将加载驱动程序列表。在该列表下面是一个按钮,说明“将当前加载的驱动程序添加到列表中”。也许,你知道有一个驱动程序是有问题的,你可能想添加。拖放Provider部分以隔离那些不是来自Microsoft的驱动程序,并快速清点。在选择这些可疑的驱动程序、对这些驱动程序启用验证器并重新启动后,查看系统是否崩溃。如果系统没有崩溃,则采取另一个步骤,选择所有未签名的驱动程序和/或第三方设备驱动程序,并在这些驱动程序上运行验证程序。如果系统没有崩溃,那么作为最后手段,对每个驱动程序运行驱动程序验证程序。但是,不要一蹴而就。一次选择大约10或20个驱动程序,启用验证程序,然后重新启动。如果选择所有配置了这些选项的驱动程序,则系统可能需要20分钟才能重新启动。系统的行为可能在短时间内看起来有所不同,但最终会进入稳定状态(如果您将此作为练习,而不是尝试将无法分析的崩溃转化为可分析的崩溃)。

使用Notmyfault.exe测试驱动程序

目前受雇于微软的Mark Russonivich编写了一个名为“Notmyfault.exe”的测试驱动程序。此实用程序包含一个设备驱动程序myfault.sys,它将导致符合特定操作系统条件的特定类型的崩溃。在他编写的其他工具中,这个工具尤其是对于使用和理解系统崩溃以及如何避免它们是非常宝贵的。虽然不是必需的,但此工具最好在虚拟环境中运行。虚拟环境是作为计算环境运行的软件层。如果您下载了一个VMWare Workstation的试用版并安装了它,您将能够在正在运行的操作系统内(但与该操作系统分离)安装一个操作系统。试试看,安装一个旧版本的XP。

即使您不关心性能,但如果您的目标是所有驱动程序,那么还有另一个原因要追求成批的驱动程序,这就是特殊池选项。当我们调用NotmyFault.exe程序将控制请求发送到myfault.sys驱动程序以执行缓冲区溢出时,myfault.sys驱动程序将从内核内存分配一个缓冲区,然后写入缓冲区数组的末尾。这将损坏内存,如图所示:

注意,我们检查了缓冲区溢出选项。当我们按下“DoBug”按钮时,一些随机缓冲区将在内核内存中被覆盖。因此,被覆盖的内存已损坏。但是,仅仅损坏内存可能会导致系统崩溃,也可能不会,直到引用了损坏内存的内容。然后,系统就会崩溃。因此,在内存损坏和检测到损坏之间可能有很长的延迟。通常,另一个驱动程序或内核进行引用。MyFault.sys分配一个非分页的池缓冲区,并在末尾写入一个字符串,从而损坏池头和后面的数据结构。所以,我们按一次“Do bug”,什么也没发生。也许,按那个按钮十次,还是什么都没发生。不过,有一点是肯定的,我们现在有一个非常病态的内核内存。如果仍然没有崩溃,那么运行类似于Internet Explorer的程序,看看它是否引用了足以检测内存损坏的操作系统。如果这不起作用,那么运行一些更重的程序,更有可能导致系统崩溃,比如WindowsMessenger。
假设系统现在崩溃了。但当系统崩溃时,是不是Windows Messenger导致了系统崩溃?当然没有。但是,在一些内核模式的软件中发生了一些事情(不是Windows Messenger),这些事情被间接调用,导致系统崩溃。当内核检测到损坏的池时,蓝屏显示启用驱动程序验证程序。它告诉你发生了什么,但不是为什么。此时,我们将检查崩溃文件。崩溃文件显示了堆栈,但随后在跟踪堆栈上显示了一个Microsoft设备驱动程序,这可能是一个非常重要的驱动程序。但是,驱动程序只引用了损坏的内存。它实际上并没有损坏内存,这就是系统崩溃的原因。
现在,我们执行相同的测试,但是启用了驱动程序验证程序,启用了所有选项(特别是特殊池,但再次声明,不启用低资源模拟)。当您按下“DoBug”按钮时,驱动程序将尝试写入到其分配的末尾,但会在实际操作中被捕获。系统立即崩溃,但更重要的是,它直接指向myfault.sys。也就是说,我们当场抓到一名司机。触发的验证选项是特殊池选项。当使用特殊池验证选项集验证驱动程序时,Windows会尝试从内存的特殊区域满足对它的内存分配,因此命名为特殊池。此区域是特殊的,因为此区域中的每个其他页都是无效的内存页,因为它将驱动程序缓冲区与分配缓冲区的内存顶部对齐。所以,当司机从缓冲区的末端溜走时,它不会坐在其他缓冲区中,但最终会碰到这些无效的内存页中的一页。而且,仅仅是它接触到一个无效的内存页就触发了一个页面错误。页面错误处理程序查看引用的内容,发现从内核模式访问的内存页无效,并将立即使系统崩溃并告诉您驱动程序有问题:

但是,有两个条件,即使您在特定驱动程序或系统范围内启用了特殊池。发送到特殊池的分配必须略少于一页。也就是说,在x86系统上,分配的页大小必须小于4kb或4096字节。所以,当一个驱动程序进行大的内存分配时,它不会来自特殊的池。这意味着,如果它覆盖了缓冲区,它就没有myfault.sys演示中的保护和检查。特殊池是不需要重新启动的驱动程序验证选项之一,但请记住它是一个有限的资源。因此,当特殊池用完时,正在验证的驱动程序将把它们的池分配发送到普通池。换言之,它们将在没有上述保护的情况下进行验证。
另一个有用的测试是系统代码覆盖测试。当驱动程序中有一个错误损坏指针,并且指针指向操作系统内核或其他引导启动驱动程序的代码时,系统代码覆盖就会发生。大多数情况下,这种访问不会被检测到。在这些情况下,当它被检测到时,Windows将识别出一个驱动程序正在试图覆盖操作系统或另一个驱动程序的部分代码。要做到这一点,Windows必须有一个称为系统代码写入保护的工具。系统代码写入保护是内存管理器将操作系统和驱动程序的代码页标记为只读的机制。因此,如果驱动程序试图写入这些页面,则会触发页面错误,内存管理器会用停止代码来指示驱动程序试图修改代码,从而使系统崩溃。但是,由于性能原因,系统代码写入功能被关闭。也就是说,为了提高性能,内核在大多数系统上没有标记为只读。Windows为了节省CPU上作为缓存的translation look aside缓冲区的空间,将虚拟地址映射到物理地址,将操作系统代码和引导启动驱动程序映射到一个大的内存页中。在典型的x86机器上,标准或“小”页是4KB。但是,像驱动程序或内核这样的映像是在4kb的段中定义的,其中代码和数据在映像中。如果操作系统将包含代码页和数据页的整个映像加载到一个4 MB的大页中,则它别无选择,只能将该页上的内存保护设置为读/写。否则,内核和驱动程序将无法修改它们自己的数据(也映射到那个大页面)。因此,系统几乎总是设置为关闭系统代码覆盖。

简短的说明

一个错误的想法是,如果像记事本这样的可执行文件被启动,整个image就会被加载。实际上,只加载其中的一部分,这被称为“惰性分配器”。随着更多功能的使用,这个可执行文件的更多image将从磁盘中读取。必要的dll也不会全部加载。只加载这些dll的引用部分。这称为“虚拟分配”。Windows中任何可共享的内存都是共享的。这意味着代码和dll,但不是数据。一种误解是,如果加载两个记事本实例,则会有两个加载的记事本image。Windows意识到有一个映像的第二个实例已经将它的一部分加载到物理RAM中,并自动将两个虚拟映像连接到相同的底层页面。但是,在每个加载的记事本实例中键入的数据是对应实例的私有数据。因此,数据不是共享的,但是执行记事本的代码是共享的,dll也是共享的。
有一种方法可以在注册表中显式启用系统代码覆盖,但这不是必需的。当验证器打开时,即使使用最小的设置,也会启用系统代码覆盖。因此,当您打开verifier时,内核将用小页面映射自身和驱动程序,因此尝试写入代码将立即生成蓝屏。如果在Notmyfault.exe上选择“代码覆盖”单选按钮,myfault.sys将覆盖NtReadFile的前几个字节,NtReadFile是一个常用的系统函数。NtReadFile是代表从任何文件句柄读取的线程调用的底层系统调用。
因此,当我们按下“Do bug”时,代码将被覆盖(因为这是允许的,因为我们运行的是一个默认系统,它的内核和引导驱动程序映像映射到一个标记为读/写的大页面)。当代码被重写时,它将很容易被检测到,因为其他东西将调用NtReadFile函数,该函数将运行到某些被重写的指令中,从而导致崩溃。如果你看一下停止代码,就没有指向驱动程序的指针,因为导致这次崩溃的驱动程序早已不复存在。如果我们查看崩溃转储文件,我们可以很容易地找到一个关键系统组件驱动程序,如Win32k.sys,现在这是一个误诊。我们可以使用最高级的调试命令,并保留一个无法分析的崩溃文件。同样,解决方案是使用驱动程序验证程序。在驱动程序验证程序打开的情况下按“Do bug”表示系统代码覆盖已打开。当它崩溃时,蓝屏立即指向myfault.sys,并且进一步的文本说明试图写入只读内存。崩溃转储文件以前指向与崩溃无关的驱动程序,现在指向正确的驱动程

粗略地说,当Windows崩溃转储文件从调试器的分析引擎中不正确时,目标是将这些文件转换为可分析的文件。驱动验证器是帮助实现这一点的工具,以及提高系统性能的工具。

如何使用Microsoft的驱动程序验证程序解释无法分析的崩溃转储文件的更多相关文章

  1. 需要正确安装 Microsoft.Windows.ShellExperienceHost 和 "Microsoft.Windows.Cortana" 应用程序。

    windows 10 开始菜单修复工具 Win10开始菜单修复工具出现的原因,自从升级到Windows  10,一直BUG不断,而其中有一个BUG非常的让你印象深刻,就是开始菜单无响应,你用着用着电脑 ...

  2. vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装。的解决方法

    vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装.的解决方法_华英雄_新浪博客 http://blog.sina.com.cn/s/blog_5 ...

  3. 《Linux设备驱动开发具体解释(第3版)》进展同步更新

    本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...

  4. 可用于在 Microsoft.NET Framework 4.0 中的 ASP.NET 浏览器定义文件的修补程序

    http://support.microsoft.com/kb/2600088 此修补程序为 Internet Explorer 和 Mozilla Firefox 在浏览器定义文件引入了更新的定义. ...

  5. 解决方案~Microsoft Security Client OOBE 程序错误

    Microsoft Security Client OOBE 程序错误 适用于: Windows Windows 7 系统错误日志如下:会话"Microsoft Security Clien ...

  6. 《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接

    <Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub   spm=a1z10.3-b.w4011-10017777404.30.kvceXB&i ...

  7. 初始v4l2(六)-------根据虚拟驱动vivi的使用彻底分析摄像头驱动

    前面的几篇文章已经分析了v4l2的框架,对框架的分析是比较粗浅的,能基本清楚函数之间的调用过程.但是很多内容并没有分析,比如说里面有很多ioctl,并没有分析哪些ioctl是必须的,也没有分析如何从应 ...

  8. 第一次安装vs2010无法运行程序,系统找不到exe文件,LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

    最近在看数据结构的一些书籍,怎奈代码是c写的,所以安装一个编译器vs2010来测试代码,但是建完文件后编译ok,f5却出现错误:无法启动程序,系统找不到指定文件.上网找了一些解决办法,但是仍然无法解决 ...

  9. Laravel驱动管理类Manager的分析和使用

    Laravel驱动管理类Manager的分析和使用 第一部分 概念说明 第二部分 Illuminate\Support\Manager源码 第三部分 Manager类的使用 第一部分:概念解释 结合实 ...

随机推荐

  1. [CF24A]Ring road(2019-11-15考试)

    题目大意 给你一个\(n\)个点的环,每条边有方向,改变第\(i\)条边的方向代价为\(w_i\),问将其改为强连通图的最小代价.\(n\leqslant100\) 题解 求出把边全部改为顺时针和全部 ...

  2. BZOJ3209: 花神的数论题(数位DP)

    题目: 3209: 花神的数论题 解析: 二进制的数位DP 因为\([1,n]\)中每一个数对应的二进制数是唯一的,我们枚举\(1\)的个数\(k\),计算有多少个数的二进制中有\(k\)个\(1\) ...

  3. 深圳宝安图书馆官网错误 HTTP Status 500 - Servlet.init() for servlet spring threw exception

    停留了一段时间没有动 打开https://www.balib.cn/balib/category/152 *********************************************** ...

  4. IntelliJ IDEA live template 方法配置

    ** * <p></p> * 功能描述 * $params$ * @return $return$ * @author abc * @date $time$ $date$ * ...

  5. kylin2.4.1订单案例详细构建流程

    一.Hive订单数据仓库构建: hive表创建可以在命令行中直接完成,也可以在Hue中完成,本文在Hue中的完成,如下图: 下文的样例文本文件下载地址:https://files-cdn.cnblog ...

  6. English--consonant_摩擦音

    consonant_摩擦音_[f]和[v].[s]和[z].[∫]和[ʒ] 摩擦音:理论上可以无限延长气流. [f]:噘嘴,上牙咬住下唇,送气,通过气流摩擦发音,声带不震动.knife.food.le ...

  7. Map List Set的区别

    Map List Map的主要区别如下: set List 是实现了Collection接口的子接口:Map本身就是一个接口: list数据存放时有序的,允许有重复元素:set数据存放时无序的,不允许 ...

  8. 【开发笔记】-Ubuntu环境命令初始化

    更新apt-get命令 apt-get update 安装yum命令 首先检测是否安装 build-essential 包 sudo apt-get install build-essential 安 ...

  9. JavaScript 数组(二)数组练习

    1.求一组数中的最大值和最小值,以及所在位置(最大值和最小值在这组数中的位置) var numbers = [120, 13, 101, 88, 10, 25]; var max = numbers[ ...

  10. Android Handler类 发送消息-post()和postDelay(), Looper讲解

    https://blog.csdn.net/weixin_41101173/article/details/79701832 首先,post和postDelay都是Handler的方法,用以在子线程中 ...