其他检查

驱动程序验证程序的其他检查选项监视驱动程序是否存在导致驱动程序或系统崩溃的常见错误,例如释放仍包含活动内核对象的内存。

具体而言,“杂项检查”选项查找以下不正确的驱动程序行为:

  • 释放内存中的活动工作项。 驱动程序调用 ExFreePool 释放池块,其中包含使用 IoQueueWorkItem 排队的工作项。

  • 释放内存中的活动资源。 驱动程序调用 ExFreePool 以释放包含活动 ERESOURCE 结构的池块。 驱动程序应调用 ExDeleteResource 以在调用 ExFreePool 之前删除 ERESOURCE 对象。

  • 可用内存中的活动外观列表。 驱动程序调用 ExFreePool 以释放仍包含活动外观列表的池块, (NPAGED_LOOKASIDE_LISTPAGED_LOOKASIDE_LIST 结构。 驱动程序应在调用 ExFreePool 之前调用 ExDeleteNPagedLookasideListExDeletePagedLookasideList 以删除 lookaside 列表。

  • Windows Management Instrumentation (WMI) 和 Windows (ETW 的事件跟踪) 注册问题。 驱动程序验证程序检测到的此类问题包括:

    • 尝试卸载的驱动程序,无需注销其 WMI 回调。

    • 尝试删除未从 WMI 注册的设备对象的驱动程序。

    • 尝试卸载的驱动程序,无需注销其 ETW 内核模式提供程序。

    • 尝试注销已注销的提供程序的驱动程序。

  • 内核处理错误。 (Windows Vista 及更高版本) 启用“杂项检查”选项还将启用系统进程的处理跟踪,以帮助调查内核处理泄漏和 Bug 检查0x93:INVALID_KERNEL_HANDLE。 启用句柄跟踪后,内核将为最近打开和关闭操作收集堆栈跟踪。 堆栈跟踪可以使用 !htrace 调试器扩展在内核调试器中显示。 有关 !htrace 的详细信息,请参阅适用于 Windows 的调试工具文档。

  • 具有内核模式访问的用户模式句柄 从 Windows 7 开始,当你选择“杂项检查”选项时,驱动程序验证程序还会检查对 ObReferenceObjectByHandle 的调用。 不能通过内核模式访问传递用户模式句柄。 如果发生此类操作,驱动程序验证程序会发出 bug 检查0xC4,参数 1 值为 0xF6。

  • UserMode 等待在内核堆栈上分配的同步对象

    从 Windows 7 开始,驱动程序验证程序可以检测驱动程序错误地使用操作系统提供的多线程同步机制的其他方法。

    分配同步对象(如 KEVENT 结构)是一种常见做法,例如内核堆栈上的局部变量。 当进程加载到内存中时,从工作集或分页到磁盘时,线程的内核堆栈永远不会被剪裁。 分配此类不可分页内存中的同步对象是正确的。

    但是,当驱动程序调用 API(如 KeWaitForSingleObjectKeWaitForMultipleObjects)等待堆栈上分配的对象时,它们必须为 API 的 WaitMode 参数指定 KernelMode 值。 当进程的所有线程都在 UserMode 模式下等待时,该进程将有资格交换到磁盘。 因此,如果驱动程序将 UserMode 指定为 WaitMode 参数,则只要同一进程中的所有其他线程都在等待 UserMode,操作系统就可以交换当前进程。 将整个进程交换到磁盘包括分页其内核堆栈。 等待操作系统已交换的同步对象不正确。 在某些时候,线程必须随之而来,并发出同步对象的信号。 向同步对象发出信号涉及在 IRQL = DISPATCH_LEVEL 或更高版本处操作对象的 Windows 内核。 在DISPATCH_LEVEL或更高位置触摸分页或交换内存会导致系统崩溃。

    从 Windows 7 开始,当你选择“杂项检查”选项时,驱动程序验证程序会检查验证驱动程序在 UserMode 中用于等待的同步对象是否未在当前线程的内核堆栈上分配。 当驱动程序验证程序检测到此类错误等待时,它会发出 Bug 检查0xC4:DRIVER_VERIFIER_DETECTED_VIOLATION,参数 1 值为 0x123。

  • 内核句柄引用不正确

    每个 Windows 进程都有一个句柄表。 可以将句柄表视为句柄条目数组。 每个有效句柄值都引用此数组中的有效条目。

    内核句柄作为对系统进程的句柄表有效句柄。 用户句柄作为任何进程(系统进程除外)有效的句柄。

    在 Windows 7 中,驱动程序验证程序检测到尝试引用内核句柄值不正确。 这些驱动程序缺陷报告为 Bug 检查0x93: 如果启用了驱动程序验证杂项检查选项,INVALID_KERNEL_HANDLE。 通常,这种错误的句柄引用意味着驱动程序已经关闭了该句柄,但尝试继续使用它。 这种缺陷可能会导致系统出现不可预知的问题,因为引用的句柄值可能已被另一个不相关的驱动程序重复使用。

    如果内核驱动程序最近关闭了内核句柄,并且以后引用了关闭的句柄,驱动程序验证程序会强制执行 bug 检查,如前所述。 在这种情况下, !htrace 调试器扩展的输出为关闭此句柄的代码路径提供堆栈跟踪。 使用 System 进程的地址作为 !htrace 的参数。 若要查找系统进程的地址,请使用 !process 4 0 命令。

    从 Windows 7 开始,驱动程序验证程序会向 ObReferenceObjectByHandle 添加检查。 现在禁止使用 KernelMode 访问传递用户空间句柄。 如果检测到此类组合,驱动程序验证程序会发出 Bug 检查0xC4:DRIVER_VERIFIER_DETECTED_VIOLATION,参数 1 值为 0xF6。

激活此选项

可以使用驱动程序验证程序管理器或Verifier.exe命令行为一个或多个驱动程序激活杂项检查选项。 有关详细信息,请参阅 选择驱动程序验证程序选项

  • 在命令行

    在命令行中,“杂项检查”选项由 位 11 (0x800) 表示。 若要激活杂项检查,请使用标志值0x800或向标志值添加0x800。 例如:

    verifier /flags 0x800 /driver MyDriver.sys
    

    该选项将在下一次启动后处于活动状态。

    在 Windows Vista 和更高版本的 Windows 上,还可以通过向命令添加 /volatile 参数来激活和停用杂项检查,而无需重启计算机。 例如:

    verifier /volatile /flags 0x800 /adddriver MyDriver.sys
    

    此设置立即生效,但在关闭或重新启动计算机时丢失。 有关详细信息,请参阅 使用易失性设置

    标准设置中还包括“杂项检查”选项。 例如:

    verifier  /standard /driver MyDriver.sys
    
  • 使用驱动程序验证程序管理器

    1. 启动驱动程序验证程序管理器。 在命令提示符窗口中键入 验证程序

    2. 选择 (代码开发人员) 的自定义设置,然后单击“下一步”。

    3. 从完整列表中选择单个设置

    4. 选择 “杂项检查”。

    标准设置中还包括“杂项检查”功能。 若要使用此功能,请在驱动程序验证程序管理器中单击“ 创建标准设置”。

查看结果

若要查看“杂项检查”选项的结果,请使用内核调试程序中的 !verifier 扩展。 (有关 !verifier 的信息,请参阅 适用于 Windows 文档的调试工具 。)

在以下示例中,“杂项检查”选项检测到驱动程序尝试释放的内存中的活动 ERESOURCE 结构,从而导致 Bug 检查0xC4:DRIVER_VERIFIER_DETECTED_VIOLATION。 Bug 检查0xC4显示包括 ERESOURCE 的地址和受影响的内存。

1: kd> !verifier 1

Verify Level 800 ... enabled options are:
 Miscellaneous checks enabled

Summary of All Verifier Statistics

RaiseIrqls                             0x0
AcquireSpinLocks                       0x0
Synch Executions                       0x0
Trims                                  0x0

Pool Allocations Attempted             0x1
Pool Allocations Succeeded             0x1
Pool Allocations Succeeded SpecialPool 0x0
Pool Allocations With NO TAG           0x0
Pool Allocations Failed                0x0
Resource Allocations Failed Deliberately   0x0

Current paged pool allocations         0x0 for 00000000 bytes
Peak paged pool allocations            0x0 for 00000000 bytes
Current nonpaged pool allocations      0x0 for 00000000 bytes
Peak nonpaged pool allocations         0x0 for 00000000 bytes

Driver Verification List

Entry     State           NonPagedPool   PagedPool   Module

8459ca50 Loaded           00000000       00000000    buggy.sys



*** Fatal System Error: 0x000000c4
 (0x000000D2,0x9655D4A8,0x9655D468,0x000000B0)


        0xD2 : Freeing pool allocation that contains active ERESOURCE.
               2 -  ERESOURCE address.
               3 -  Pool allocation start address.
               4 -  Pool allocation size.

若要调查池分配,请使用 !pool 调试器扩展和池分配的起始地址 9655D468。 (2 标志仅显示包含指定地址的池的标头信息。禁止显示其他池的标头信息。)

1: kd> !pool 9655d468  2
Pool page 9655d468 region is Paged pool
*9655d468 size:   b0 previous size:    8  (Allocated) *Bug_

若要查找有关 ERESOURCE 的信息,请使用 !locks (!kdext*.locks) 调试器扩展和结构的地址。

1: kd> !locks 0x9655D4A8     <<<<<- ERESOURCE @0x9655D4A8 lives inside the pool block being freed

Resource @ 0x9655d4a8    Available
1 total locks

还可以使用 kb 调试器命令显示导致故障的调用的堆栈跟踪。 以下示例演示堆栈,包括调用驱动程序验证程序截获的 ExFreePoolWithTag

1: kd> kb
ChildEBP RetAddr  Args to Child
92f6374c 82c2c95a 00000003 92f68cdc 00000000 nt!RtlpBreakWithStatusInstruction
92f6379c 82c2d345 00000003 9655d468 000000c4 nt!KiBugCheckDebugBreak+0x1c
92f63b48 82c2c804 000000c4 000000d2 9655d4a8 nt!KeBugCheck2+0x5a9
92f63b6c 82e73bae 000000c4 000000d2 9655d4a8 nt!KeBugCheckEx+0x1e
92f63b88 82e78c32 9655d4a8 9655d468 000000b0 nt!VerifierBugCheckIfAppropriate+0x3c
92f63ba4 82ca7dcb 9655d468 000000b0 00000000 nt!VfCheckForResource+0x52
92f63bc8 82e7fb2d 000000b0 00000190 9655d470 nt!ExpCheckForResource+0x21
92f63be4 82e6dc6c 9655d470 92f63c18 89b6c58c nt!ExFreePoolSanityChecks+0x1fb
92f63bf0 89b6c58c 9655d470 00000000 89b74194 nt!VerifierExFreePoolWithTag+0x28
92f63c00 89b6c0f6 846550c8 846550c8 846e2200 buggy!MmTestProbeLockForEverStress+0x2e
92f63c18 82e6c5f1 846e2200 846550c8 85362e30 buggy!TdDeviceControl+0xc4
92f63c38 82c1fd81 82d4d148 846550c8 846e2200 nt!IovCallDriver+0x251
92f63c4c 82d4d148 85362e30 846550c8 84655138 nt!IofCallDriver+0x1b
92f63c6c 82d4df9e 846e2200 85362e30 00000000 nt!IopSynchronousServiceTail+0x1e6
92f63d00 82d527be 00000001 846550c8 00000000 nt!IopXxxControlFile+0x684
92f63d34 82cb9efc 0000004c 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
92f63d34 6a22b204 0000004c 00000000 00000000 nt!KiFastCallEntry+0x12c