驱动程序是让硬件(如显卡、声卡、网卡、打印机)或某些软件与操作系统(Windows)通信的关键软件,绝大部分的驱动程序都是使用C/C++编写。在C/C++编程中,内存问题是开发过程中必然面对的挑战,内存问题处理不好可能埋下严重的安全风险。编写不当、存在错误的驱动程序很容易导致系统性能下降,甚至系统崩溃、蓝屏、死机等问题。
微软自Windows 2000以来,提供了一种名为Verifier的驱动程序验证程序工具,它包含在Windows的发行版本中,专门设计来主动“考验”这些驱动程序,找出那些行为不符合规范、可能引发问题的驱动。Verifier有几项重要的功能,如内存检测、强制IRQL检查、死锁检测、低资源模拟等功能,可以方便定位驱动程序的内存泄漏、IRQL使用级别错误、锁使用错误等问题。Verifier可以帮助开发人员及时发现驱动程序中的问题,提升系统的安全性和稳定性,是C/C++程序员不可或缺的得力助手。
Verifier会检测被测试驱动程序申请与释放内存的API调用过程,记录下每一个申请的内存块的地址、大小、相关调用栈等参数,在驱动程序调用关键操作系统内核 API(函数)时进行“挂钩”或拦截,检测多种内存违规现象:
池溢出:检测驱动程序是否错误地写入或读取了超出分配给它的内存块(缓冲池)边界。
无效内存访问:检测驱动程序是否试图访问它无权访问或根本不存在的内存地址。
资源泄漏:检测驱动程序是否正确释放内存或资源(内存泄漏、句柄泄漏)。
对于资源泄露问题,当驱动被卸载时,Verifier检查该驱动在其生命周期内分配的所有资源是否都已被正确释放。如果发现该驱动还有未释放的资源(内存块、句柄等),Verifier会认为发生了资源泄露,会立即触发一个 BUGCHECK(系统崩溃),也就是蓝屏死机(BSOD)。蓝屏的错误代码通常是DRIVER_Verifier_DETECTED_VIOLATION (0xC4),并会附带一个具体的参数来指明违规类型,并把这些未释放的内存块的详细信息记录到memory.dmp中。
本文通过一个实例介绍如何使用Verifier定位驱动程序的内存泄漏问题,展示Verifier定位问题的全过程。完整过程分为以下4个步骤:
配置系统的启动和故障恢复功能,以便保存memory.dmp文件。
准备并安装好待测试的驱动程序,配置Verifier。
驱动程序运行,申请内存但不释放,引发内存泄漏;卸载驱动程序触发系统蓝屏。
使用Windows的定位工具WinDbg分析蓝屏后的memory.dmp文件。
在Windows10系统上,使用威努特工控主机卫士的驱动框架代码进行演示,驱动程序为wlminisecmod.sys,演示Verifier定位内存泄漏的实际操作过程。
在wlminisecmod.sys中增加一处内存泄漏的代码,如下图所示:
为了方便定位查找问题,在申请内存的API使用ExAllocatePoolWithTag进行申请,这部分内存申请后不再释放。ExAllocatePoolWithTag的第三个参数“1234”作为申请内存块的Tag标记。后续在分析memory.dmp时可以查看到所有未被释放的内存块的标记。
配置启动和故障恢复功能
首先,我们配置Windows系统的启动和故障恢复功能,方便后续根据日志定位问题。
在“设置”-〉“关于”下的“高级系统设置”,打开“系统属性”页。
选择“高级”中的“启动和故障恢复”的设置,配置项下见图。
配置后需要重启系统后生效。
配置Verifier监视驱动程序
预先安装好待测试的驱动程序,然后在“运行”中输入Verifier,以管理员运行Verifier。
作为初学者可以使用“创建标准设置”。
选择要进行测试的驱动。
选择fopf.sys与wlminisecmod.sys作为被测试的驱动程序。
配置完成后,重启系统后Verifier生效。
启动测试并生成蓝屏
重启后,被测试驱动程序会在Verifier的监视下工作。本例中wlminisecmod.sys在启动后即申请内存,没有释放内存的代码,所以引发了内存泄漏。
Verifier会记录驱动程序申请与释放的次数,当驱动程序卸载时,Verifier发现数据不一致,则会主动触发引起蓝屏,故此我们需要手动卸载被测试驱动。驱动程序不同,卸载命令也有可能不同。以wlminisecmod.sys为例,在有管理员权限下的cmd下使用命令:fltmc unload wlminisecmod即可完成卸载。
在本例中,这个命令执行完成,将立刻触发蓝屏。重启后生成文件c:\Windows\memory.dmp,这就是我们下一步要分析的memory.dmp文件。
使用WinDbg分析memory.dmp文件
蓝屏后重启操作系统,使用WinDbg 打开c:\Windows\memory.dmp文件,进一步了定位内存泄漏的详细信息。
首先我们定位本次故障的BUG号,由Verifier主动引起的蓝屏的BUG号为:000000C4,即表示是Verifier主动引起的蓝屏。
蓝屏相关的BUG信息可参见官方:
错误检查代码参考 - Windows drivers | Microsoft Learn
https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debugger/bug-check-code-reference2
下图为WinDbg中本次引发蓝屏的基本日志信息,可以看到BUG号为C4,表示是Verifier主动引起的蓝屏,Arg1:62表示驱动忘记释放一些内存。
然后在WinDbg使用命令!Verifier 3 wlminisecmod.sys,可以显示出具体哪些Tag的内存块未释放,其中wlminisecmod.sys就是我们本次测试的驱动名称。
WinDbg的分析结果中还显示了内存泄漏的每一块的详细信息,如地址、长度、Tag、申请内存的调用函数。下图的分析结果显示未释放块的Tag为“4321”。前面我们在wlminisecmod的测试代码中写的Tag为“1234”,由于大小字节序的问题,在WinDbg中Tag的显示与实际代码的参数是相反的,所以这个“4321”就是我们前面申请的内存标记。
Verifier除了定位内存问题,还可以定位很多常见的Windows的驱动问题,比如强制IRQL检查、死锁检测、低资源模拟等这些功能,后续文章将继续介绍Verifier工具其他工具的使用方法。Verifier 是一个极其强大但也非常危险的工具,绝对不要把它当作一个持续运行在后台的系统优化或监控工具。在开发威努特工控主机卫士的过程中,我们广泛使用Verifier快速高效地定位出有关内存泄漏、死锁等编码问题,有效地保证了软件的稳定性、可靠性。
驱动程序验证程序工具Verifier是 Windows 内置的一个“驱动程序压力测试仪”和“显微镜”。它通过主动施加压力、严格检查规则遵守情况和监控内存操作,帮助识别、诊断和隔离那些编写不良、存在缺陷或不稳定的内核模式驱动程序。其主要用户是驱动开发者和需要解决棘手系统不稳定(尤其是蓝屏)问题的技术人员。普通用户在使用它时必须非常小心,避免导致系统无法启动。



