(回显内核模式调试 Windows 驱动程序分步实验室)

本实验室介绍 WinDbg 内核调试器。 使用 WinDbg 调试回显内核模式示例驱动程序代码。

实验室目标

本实验室包括介绍调试工具、教授常见调试命令、演示断点的使用以及演示如何使用调试扩展的练习。

在本实验室中,你将使用实时内核调试连接来探索以下操作:

  • 使用 Windows 调试器命令
  • 使用标准命令 (调用堆栈、变量、线程、IRQL)
  • 使用高级驱动程序调试命令 (!commands)
  • 使用符号
  • 在实时调试中设置断点
  • 查看调用堆栈
  • 显示即插即用设备树
  • 使用线程和进程上下文

用户和内核模式调试

使用 Windows 调试器时,可以执行两种类型的调试:

用户模式 - 应用程序和子系统以用户模式在计算机上运行。 在用户模式下运行的进程在其自己的虚拟地址空间中运行。 它们无法直接访问系统的许多部分,包括系统硬件、未分配供其使用的内存,以及可能损害系统完整性的系统其他部分。 由于在用户模式下运行的进程与系统和其他用户模式进程有效隔离,因此它们不能干扰这些资源。

内核模式 - 操作系统和特权程序在内核模式下运行。 内核模式代码有权访问系统的任何部分。 它不受限制,就像用户模式代码一样。 它可以获取对在用户模式或内核模式下运行的任何其他进程的任何部分的访问权限。 许多核心操作系统功能和许多硬件设备驱动程序在内核模式下运行。

本练习介绍在用户模式和内核模式调试期间经常使用的调试命令。 本练习还介绍了用于内核模式调试的调试扩展(有时称为 !命令)。

实验室设置

需要以下硬件才能完成实验室:

  • 笔记本电脑或台式计算机 (运行 Windows 10 的主机)
  • 运行Windows 10的第二台笔记本电脑或台式计算机 (目标)
  • 用于连接两台计算机的网络集线器或路由器和网络电缆
  • 访问 Internet 以下载符号文件

需要以下软件才能完成实验室:

  • Visual Studio
  • 适用于 Windows 10 的 Windows 软件开发工具包 (SDK)
  • 适用于 Windows 10 的 Windows 驱动程序工具包 (WDK)
  • Windows 10 的示例回显驱动程序

实验室包含以下部分:

连接到内核模式 WinDbg 会话

在本部分中,在主机和目标系统上配置网络调试。

此实验室中的计算机需要配置为使用以太网网络连接进行内核调试。

此实验室使用两台计算机。 Windows 调试器在 主机 系统上运行,内核模式驱动程序框架 (KMDF) 回显驱动程序在 目标 系统上运行。

使用网络集线器或路由器和网络电缆连接两台计算机。

该图显示了两台使用双箭头连接的计算机。

若要使用内核模式应用程序并使用 WinDbg,建议使用基于以太网的 KDNET 传输。 有关如何使用以太网传输协议的信息,请参阅 winDbg (内核模式) 入门 。 有关设置目标计算机的详细信息,请参阅 为手动驱动程序部署准备计算机自动设置 KDNET 网络内核调试

使用以太网配置内核模式调试

若要在目标系统上启用内核模式调试,请执行以下操作:

  1. 在主机系统上,打开命令提示符窗口并输入 ipconfig 以确定其 IP 地址。

    Windows IP Configuration
    Ethernet adapter Ethernet:
       Connection-specific DNS Suffix  . :
       Link-local IPv6 Address . . . . . : fe80::c8b6:db13:d1e8:b13b%3
       Autoconfiguration IPv4 Address. . : 169.182.1.1
       Subnet Mask . . . . . . . . . . . : 255.255.0.0
       Default Gateway . . . . . . . . . :
    
  2. 记录主机系统的 IP 地址:____________

  3. 在目标系统上,打开命令提示符窗口并使用 ping 命令确认两个系统之间的网络连接。

    ping 169.182.1.1
    

    使用记录的主机系统的实际 IP 地址,而不是示例输出中显示的 169.182.1.1。

    Pinging 169.182.1.1 with 32 bytes of data:
    Reply from 169.182.1.1: bytes=32 time=1ms TTL=255
    Reply from 169.182.1.1: bytes=32 time<1ms TTL=255
    Reply from 169.182.1.1: bytes=32 time<1ms TTL=255
    Reply from 169.182.1.1: bytes=32 time<1ms TTL=255
    
    Ping statistics for 169.182.1.1:
        Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
    Approximate round trip times in milli-seconds:
        Minimum = 0ms, Maximum = 1ms, Average = 0ms
    

通过完成以下步骤,在目标系统上启用内核模式调试。

重要

在使用 BCDEdit 更改启动信息之前,可能需要暂时挂起测试计算机上的 Windows 安全功能,例如 BitLocker 和安全启动。 测试完成后,重新启用这些安全功能。 禁用安全功能时,请适当地管理测试计算机。

  1. 在目标计算机上,以管理员身份打开“命令提示符”窗口。 输入以下命令以启用调试:

    bcdedit /set {default} DEBUG YES
    
  2. 输入以下命令以启用测试签名:

    bcdedit /set TESTSIGNING ON 
    
  3. 输入此命令以设置主机系统的 IP 地址。 使用前面记录的主机系统的 IP 地址,而不是显示的 IP 地址。

    bcdedit /dbgsettings net hostip:192.168.1.1 port:50000 key:2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    

    警告

    若要提高连接的安全性并降低随机客户端调试器连接请求的风险,请使用自动生成的随机密钥。 有关详细信息,请参阅 设置 KDNET 网络内核自动调试

  4. 输入此命令以确认 dbgsettings 的值已正确设置:

    bcdedit /dbgsettings
    
    key                     2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    debugtype               NET
    hostip                  169.168.1.1
    port                    50000
    dhcp                    Yes
    The operation completed successfully.
    

    注意

    如果收到来自防火墙的消息,并且想要使用调试器,请选择所有三个框。

    屏幕截图显示“Windows 安全中心警报”对话框,其中显示 Windows 防火墙已阻止此应用的某些功能。

  5. 在主计算机上,以管理员身份打开命令提示符窗口。 本实验室使用 Windows 驱动程序工具包 ( WDK) 中作为 Windows 工具包安装的一部分安装的 x64 版WinDbg.exe。 更改为默认 WinDbg 目录,默认位置如下所示。

    cd C:\Program Files(x86)\Windows Kits\10\Debuggers\x64 
    

    此实验室假定这两台计算机在目标和主机上都运行 64 位版本的 Windows。 如果不是这种情况,最佳方法是在主机上运行与目标运行相同的工具 位数 。 例如,如果目标运行 32 位 Windows,请在主机上运行 32 位版本的调试器。 有关详细信息,请参阅选择 32 位或 64 位调试工具

  6. 使用以下命令通过远程用户调试打开 WinDbg。 密钥和端口的值与之前在目标计算机上使用 BCDEdit 设置的值匹配。

    WinDbg –k net:port=50000,key=2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    
  7. 重启目标系统。

  8. 在一两分钟内,应在主机系统上显示调试输出。

    Microsoft (R) Windows Debugger Version 10.0.17074.1002 AMD64
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    Using NET for debugging
    Opened WinSock 2.0
    Waiting to reconnect...
    Connected to target 169.182.1.1 on port 50005 on local IP 169.182.1.2
    You can get the target MAC address by running .kdtargetmac command.
    Connected to Windows 10 16299 x64 target at (Wed Feb 28 17:16:23.051 2018 (UTC - 8:00)), ptr64 TRUE
    Kernel Debugger connection established.  (Initial Breakpoint requested)
    Symbol search path is: srv*
    Executable search path is: 
    Windows 10 Kernel Version 16299 MP (4 procs) Free x64
    Product: WinNt, suite: TerminalServer SingleUserTS
    Built by: 16299.15.amd64fre.rs3_release.170928-1534
    Machine Name:
    Kernel base = 0xfffff800`9540d000 PsLoadedModuleList = 0xfffff800`95774110
    Debug session time: Wed Feb 28 17:16:23.816 2018 (UTC - 8:00)
    System Uptime: 0 days 0:00:20.534
    

“调试器命令”窗口是 WinDbg 中的主要调试信息窗口。 可以输入调试器命令并在此窗口中查看命令输出。

调试器命令窗口拆分为两个窗格。 在较小的窗格中输入命令,该窗格是窗口底部的命令输入窗格,并在窗口顶部的大窗格中查看命令输出。

在命令条目窗格中,使用向上键和向下键滚动浏览命令历史记录。 出现命令时,可以对其进行编辑或按 Enter 运行命令。

内核模式调试命令和技术

在本部分中,使用调试命令显示有关目标系统的信息。

某些调试命令使用调试器标记语言 (DML) 显示文本,你可以选择这些语言来快速收集更多信息。

  1. 在主机系统上,使用 WinDBg 中的 Ctrl+Scroll Lock 中断目标系统上运行的代码。 目标系统可能需要一些时间才能做出响应。

    调试器中的主屏幕,显示来自实时内核连接的命令窗口输出。

  2. 在“调试器命令”窗口中输入以下命令以启用 DML:

    0: kd> .prefer_dml 1
    DML versions of commands on by default
    
  3. 可以使用 命令访问引用命令帮助 .hh 。 输入以下命令以查看 的 .prefer_dml命令参考帮助:

    0: kd> .hh .prefer_dml
    

    调试器帮助文件显示命令的 .prefer_dml 帮助。

    屏幕截图显示了调试器帮助应用程序,其中显示了有关 .prefer-dml 命令的帮助。

  4. 若要在目标系统上显示详细的版本信息,请在 WinDbg 窗口中输入 vertarget (显示目标计算机版本) 命令:

    0: kd> vertarget
    Windows 10 Kernel Version 9926 MP (4 procs) Free x64
    Product: WinNt, suite: TerminalServer SingleUserTS
    Built by: 9926.0.amd64fre.fbl_awesome1501.150119-1648
    Machine Name: ""
    Kernel base = 0xfffff801`8d283000 PsLoadedModuleList = 0xfffff801`8d58aef0
    Debug session time: Fri Feb 20 10:15:17.807 2015 (UTC - 8:00)
    System Uptime: 0 days 01:31:58.931
    
  5. 若要验证是否使用正确的内核模式进程,请在 WinDbg 窗口中输入 lm (列出加载的模块) 命令以显示加载的模块:

    0: Kd> lm
    start             end                 module name
    fffff801`09200000 fffff801`0925f000   volmgrx    (no symbols)
    fffff801`09261000 fffff801`092de000   mcupdate_GenuineIntel   (no symbols)
    fffff801`092de000 fffff801`092ec000   werkernel   (export symbols)       werkernel.sys
    fffff801`092ec000 fffff801`0934d000   CLFS       (export symbols)       CLFS.SYS
    fffff801`0934d000 fffff801`0936f000   tm         (export symbols)       tm.sys
    fffff801`0936f000 fffff801`09384000   PSHED      (export symbols)       PSHED.dll
    fffff801`09384000 fffff801`0938e000   BOOTVID    (export symbols)       BOOTVID.dll
    fffff801`0938e000 fffff801`093f7000   spaceport   (no symbols)
    fffff801`09400000 fffff801`094cf000   Wdf01000   (no symbols)
    fffff801`094d9000 fffff801`09561000   CI         (export symbols)       CI.dll
    ...
    

    省略的输出用“...”指示在本实验室中。

  6. 若要请求有关特定模块的详细信息,请使用 v (详细) 选项:

    0: Kd> lm v m tcpip
    Browse full module list
    start             end                 module name
    fffff801`09eeb000 fffff801`0a157000   tcpip      (no symbols)           
        Loaded symbol image file: tcpip.sys
        Image path: \SystemRoot\System32\drivers\tcpip.sys
        Image name: tcpip.sys
        Browse all global symbols  functions  data
        Timestamp:        Sun Nov 09 18:59:03 2014 (546029F7)
        CheckSum:         00263DB1
        ImageSize:        0026C000
        Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
    
    Unable to enumerate user-mode unloaded modules, Win32 error 0n30
    

    没有设置的符号路径和加载的符号,因此调试器中提供的信息有限。

下载并生成 KMDF 回显驱动程序

在本部分中,下载并生成 KMDF 回显驱动程序。

通常,在使用 WinDbg 时,你将使用自己的驱动程序代码。 为了熟悉 WinDbg 操作,本实验室使用 KMDF 模板“Echo”示例驱动程序。 源代码可用于帮助了解 WinDbg 中显示的信息。 此示例还用于说明如何单步执行本机内核模式代码。 此方法对于调试复杂的内核模式代码问题非常有用。

下载并生成 Echo 示例音频驱动程序:

  1. 从 GitHub 下载并提取 KMDF Echo 示例。

    KMDF Echo 示例位于 常规 文件夹中。

    github windows-driver-samples 的屏幕截图,其中突出显示了常规文件夹和下载 zip 按钮。

    1. 在一个 zip 文件中下载驱动程序示例: 驱动程序示例

    2. 将 zip 文件下载到本地硬盘驱动器。

    3. 选择并按住或右键单击 zip 文件,然后选择“ 全部提取”。 指定一个新文件夹,或浏览到现有文件夹以存储提取的文件。 例如,可以将 C:\DriverSamples\ 指定为要将文件提取到其中的新文件夹。

    4. 提取文件后,转到以下子文件夹: C:\DriverSamples\general\echo\kmdf

  2. 在 Microsoft Visual Studio 中,选择“ 文件>打开>项目/解决方案...” ,然后转到包含提取文件的文件夹,例如 C:\DriverSamples\general\echo\kmdf。 双击 kmdfecho 解决方案文件将其打开。

    在 Visual Studio 中,找到解决方案资源管理器。 如果此窗口尚未打开,请从“视图”菜单中选择“解决方案资源管理器”。 在 解决方案资源管理器 中,可以看到一个包含三个项目的解决方案。

    屏幕截图显示 Visual Studio,其中包含从 kmdfecho 项目加载的 device.c 文件。

  3. 设置示例的配置和平台。 在“解决方案资源管理器”中,选择并按住或右键单击“解决方案”kmdfecho“ (3 个项目) ,然后选择”Configuration Manager”。 确保这三个项目的配置和平台设置相同。 默认情况下,配置设置为 Win10 Debug,平台设置为 所有项目的 Win64 。 如果对一个项目进行任何配置或平台更改,请对其余三个项目进行相同的更改。

  4. 需要修改驱动程序示例,以使用与现有驱动程序不重叠的值。 请参阅 从示例代码到生产驱动程序 - 示例中的更改 内容,了解如何创建与 Windows 中安装的现有实际驱动程序共存的唯一驱动程序示例。

  5. 设置运行时库。 打开回显驱动程序属性页并找到 C/C++>代码生成。 将运行时库更改为多线程调试 (/MTd) 。 有关生成选项的详细信息,请参阅 /MD、/MT、/LD (使用 Run-Time 库)

    屏幕截图显示了回显属性页,其中突出显示了运行时库设置。

  6. 在驱动程序属性中,确保 将“驱动程序签名>签名模式 ”设置为 “测试签名”。

    屏幕截图显示了 echo 属性页,其中突出显示了签名模式设置。

  7. 在 Visual Studio 中,选择“ 生成>解决方案”。

    生成窗口应显示一条消息,指示所有三个项目的生成都成功。

提示

如果遇到生成错误消息,请使用生成错误号来确定修复方法。 例如, MSBuild 错误 MSB8040 介绍了如何使用 spectre 缓解库。

  1. 在“文件资源管理器”中,转到包含示例提取文件的文件夹。 例如,如果这是之前指定的文件夹,请转到 C:\DriverSamples\general\echo\kmdf。 在该文件夹中,编译的驱动程序文件的位置因在Configuration Manager中选择的配置和平台设置而异。 如果保留默认设置不变,则编译的驱动程序文件将保存到名为 \x64\Debug 的文件夹中,以便进行 64 位调试生成。

    转到包含 Autosync 驱动程序生成文件的文件夹: C:\DriverSamples\general\echo\kmdf\driver\AutoSync\x64\Debug

    该文件夹应包含以下文件:

    文件 说明
    Echo.sys 驱动程序文件。
    Echo.inf 一个信息 (INF) 文件,其中包含安装驱动程序所需的信息。

    此外, echoapp.exe 文件已生成,它应位于以下位置: C:\DriverSamples\general\echo\kmdf\exe\x64\Debug

    文件 说明
    EchoApp.exe 与echo.sys驱动程序通信的命令提示符可执行测试文件。
  2. 找到 U 盘或设置网络共享,将生成的驱动程序文件和测试 EchoApp 从主机复制到目标系统。

在下一部分中,将代码复制到目标系统,并安装并测试驱动程序。

在目标系统上安装 KMDF 回显驱动程序示例

在本部分中,使用 DevCon 工具安装回显示例驱动程序。

安装驱动程序的计算机称为 目标计算机测试计算机。 通常,此计算机独立于开发和生成驱动程序包的计算机。 开发和生成驱动程序的计算机称为 主计算机

将驱动程序包移动到目标计算机并安装驱动程序的过程称为 部署 驱动程序。

在部署测试签名驱动程序之前,请通过启用测试签名来准备目标计算机。 还需要在 WDK 安装中找到 DevCon 工具,并将其复制到目标系统。

若要在目标系统上安装驱动程序,请执行以下步骤。

在目标系统上,启用测试签名的驱动程序:

  1. 打开 Windows 设置

  2. “更新和安全”中,选择“ 恢复”。

  3. 在“ 高级启动”下,选择“ 立即重启”。

  4. 当计算机重启时,选择“ 启动选项”。 在“Windows 10”中,选择“排查>高级选项”>“启动设置”,然后选择“重启”。

  5. 按 F7 键,选择 “禁用驱动程序签名强制 ”。

  6. 重启目标计算机。

在主机系统上,转到 WDK 安装中的 “工具” 文件夹,并找到 DevCon 工具。 例如,查找以下文件夹: C:\Program Files (x86) \Windows Kits\10\Tools\x64\devcon.exe

在生成的驱动程序包的目标上创建一个文件夹,例如 C:\EchoDriver 将devcon.exe复制到目标系统。 在主机系统上找到 .cer 证书。 它位于主计算机上的同一文件夹中,该文件夹中包含生成的驱动程序文件。 复制前面在主计算机上描述的生成驱动程序中的所有文件,并将其保存到在目标计算机上创建的同一文件夹中。

在目标计算机上,选择并按住或右键单击证书文件,然后选择“ 安装”,然后按照提示安装测试证书。

如果需要有关设置目标计算机的更详细说明,请参阅 为手动驱动程序部署准备计算机

以下说明演示如何安装和测试示例驱动程序。 下面是用于安装驱动程序的 devcon 工具的一般语法:

devcon install <INF file> <hardware ID>

安装此驱动程序所需的 INF 文件是 echo.inf。 inf 文件包含用于安装 echo.sys的硬件 ID。 对于回显示例,硬件 ID 为 root\ECHO

在目标计算机上,以管理员身份打开“命令提示符”窗口。 转到驱动程序包文件夹,并输入以下命令:

devcon install echo.inf root\ECHO

如果你收到一条关于 devcon 未被识别的错误消息,请尝试添加 devcon 工具的路径。 例如,如果将它复制到名为 C:\Tools 的文件夹,请尝试使用以下命令:

c:\tools\devcon install echo.inf root\ECHO

将出现一个对话框,指示测试驱动程序是未签名的驱动程序。 选择“仍然安装此驱动程序”以继续。

屏幕截图显示了一个Windows 安全中心警告,指出 Windows 无法验证此驱动程序软件的发布者。

提示

 如果安装有任何问题,检查以下文件了解详细信息。 %windir%\inf\setupapi.dev.log

成功安装示例驱动程序后,即可对其进行测试。

在目标计算机上的命令提示符窗口中,输入 devmgmt 以打开设备管理器。 在“设备管理器”的“视图”菜单上,按类型选择“设备”。在设备树中,在“示例设备”节点中找到“示例WDF 回显驱动程序”。

屏幕截图显示了设备管理器树,其中突出显示了示例 wdf 回显驱动程序。

输入 echoapp 以启动测试回显应用,以确认驱动程序正常运行。

C:\Samples\KMDF_Echo_Sample> echoapp
DevicePath: \\?\root#sample#0005#{cdc35b6e-0be4-4936-bf5f-5537380a7c1a}
Opened device successfully
512 Pattern Bytes Written successfully
512 Pattern Bytes Read successfully
Pattern Verified successfully
30720 Pattern Bytes Written successfully
30720 Pattern Bytes Read successfully
Pattern Verified successfully

使用 WinDbg 显示有关驱动程序的信息

在本部分中,设置符号路径并使用内核调试器命令显示有关 KMDF 回显示例驱动程序的信息。

若要查看有关驱动程序的信息,请执行以下操作:

  1. 在主机系统上,如果关闭了调试器,请在管理员命令提示符窗口中使用以下命令再次打开它。

    WinDbg -k net:port=50000,key=2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
    
  2. 使用 Ctrl+Break (Scroll Lock) 中断目标系统上运行的代码。

  3. 若要在 WinDbg 环境中设置 Microsoft 符号服务器的符号路径,请使用 .symfix 命令。

    0: kd> .symfix
    
  4. 若要添加本地符号位置以使用本地符号,请使用 .sympath+ 然后 .reload /f添加路径。

    0: kd> .sympath+ C:\DriverSamples\general\echo\kmdf
    0: kd> .reload /f
    

    .reload具有 force 选项的/f命令将删除指定模块的所有符号信息并重新加载符号。 在某些情况下,此命令还会重新加载或卸载模块本身。

必须加载正确的符号才能使用 WinDbg 提供的高级功能。 如果未正确配置符号,则尝试使用依赖于符号的功能时,会收到指示符号不可用的消息。

0:000> dv
Unable to enumerate locals, HRESULT 0x80004005
Private symbols (symbols.pri) are required for locals.
Type “.hh dbgerr005” for details.

有许多方法可用于处理符号。 在许多情况下,可以将计算机配置为从 Microsoft 在需要时提供的符号服务器访问符号。 本实验室使用此方法。 如果环境中的符号位于其他位置,请修改步骤以使用该位置。 有关详细信息,请参阅 Windows 调试器的符号路径

若要执行源调试,必须生成一个已检查 (调试) 版本的二进制文件。 编译器 (.pdb 文件) 创建符号文件。 这些符号文件向调试器显示二进制指令与源行的对应方式。 调试器还必须可以访问实际的源文件本身。

符号文件不包含源代码的文本。 对于调试,最好是链接器不优化代码。 如果代码已优化,则源调试和访问局部变量会更加困难,有时几乎是不可能的。 如果在查看局部变量或源行时遇到问题,请设置以下生成选项:

set COMPILE_DEBUG=1
set ENABLE_OPTIMIZER=0
  1. 在调试器的命令区域中输入以下命令,以显示有关回显驱动程序的信息:

    0: kd> lm m echo* v
    Browse full module list
    start             end                 module name
    fffff801`4ae80000 fffff801`4ae89000   ECHO       (private pdb symbols)  C:\Samples\KMDF_ECHO_SAMPLE\echo.pdb
        Loaded symbol image file: ECHO.sys
        Image path: \SystemRoot\system32\DRIVERS\ECHO.sys
        Image name: ECHO.sys
    ...  
    

    有关详细信息,请参阅 lm

  2. 由于此实验室之前设置 prefer_dml ,因此输出的某些元素是可以选择的热链接。 选择调试输出中的“ 浏览所有全局符号 ”链接,以显示有关以字母“a”开头的项符号的信息。

    0: kd> x /D Echo!a*
    
  3. 回显示例不包含任何以字母“a”开头的符号,因此键入 x ECHO!Echo* 以显示与以“Echo”开头的回显驱动程序关联的所有符号的相关信息。

    0: kd> x ECHO!Echo*
    fffff801`0bf95690 ECHO!EchoEvtIoQueueContextDestroy (void *)
    fffff801`0bf95000 ECHO!EchoEvtDeviceSelfManagedIoStart (struct WDFDEVICE__ *)
    fffff801`0bf95ac0 ECHO!EchoEvtTimerFunc (struct WDFTIMER__ *)
    fffff801`0bf9b120 ECHO!EchoEvtDeviceSelfManagedIoSuspend (struct WDFDEVICE__ *)
    ...
    

    有关详细信息,请参阅 x (检查符号)

  4. !lmi 扩展显示有关模块的详细信息。 输入 !lmi echo。 输出应类似于此示例中显示的文本:

    0: kd> !lmi echo
    Loaded Module Info: [echo] 
             Module: ECHO
       Base Address: fffff8010bf94000
         Image Name: ECHO.sys
    … 
    
  5. !dh使用 扩展显示标头信息,如以下示例所示:

    0: kd> !dh echo
    
    File Type: EXECUTABLE IMAGE
    FILE HEADER VALUES
         14C machine (i386)
           6 number of sections
    54AD8A42 time date stamp Wed Jan 07 11:34:26 2015
    ...
    
  6. 输入以下内容以更改默认调试位掩码,以便在调试器中显示来自目标系统的所有调试消息:

    0: kd> ed nt!Kd_DEFAULT_MASK 0xFFFFFFFF
    

    使用0xFFFFFFFF掩码时,某些驱动程序会显示其他信息。 如果要减少显示的信息量,请将掩码设置为0x00000000。

    0: kd> ed nt!Kd_DEFAULT_MASK 0x00000000
    

    dd使用 命令确认掩码设置为显示所有调试器消息。

    0: kd> dd nt!kd_DEFAULT_MASK 
    fffff802`bb4057c0  ffffffff 00000000 00000000 00000000
    fffff802`bb4057d0  00000000 00000000 00000000 00000000
    fffff802`bb4057e0  00000001 00000000 00000000 00000000
    fffff802`bb4057f0  00000000 00000000 00000000 00000000
    fffff802`bb405800  00000000 00000000 00000000 00000000
    fffff802`bb405810  00000000 00000000 00000000 00000000
    fffff802`bb405820  00000000 00000000 00000000 00000000
    fffff802`bb405830  00000000 00000000 00000000 00000000
    

显示即插即用设备树信息

在本部分中,显示有关回显示例设备驱动程序及其在即插即用设备树中的位置的信息。

即插即用设备树中有关设备驱动程序的信息可用于故障排除。 例如,如果设备驱动程序不驻留在设备树中,则设备驱动程序的安装可能存在问题。

有关设备节点调试扩展的详细信息,请参阅 !devnode

  1. 在主机系统上,若要查看即插即用设备树中的所有设备节点,请输入 命令!devnode 0 1

    0: kd> !devnode 0 1
    Dumping IopRootDeviceNode (= 0xffffe0005a3a8d30)
    DevNode 0xffffe0005a3a8d30 for PDO 0xffffe0005a3a9e50
      InstancePath is "HTREE\ROOT\0"
      State = DeviceNodeStarted (0x308)
      Previous State = DeviceNodeEnumerateCompletion (0x30d)
      DevNode 0xffffe0005a3a3d30 for PDO 0xffffe0005a3a4e50
        InstancePath is "ROOT\volmgr\0000"
        ServiceName is "volmgr"
        State = DeviceNodeStarted (0x308)
        Previous State = DeviceNodeEnumerateCompletion (0x30d)
        DevNode 0xffffe0005a324560 for PDO 0xffffe0005bd95ca0…
    …
    
  2. 使用 Ctrl+F 在生成的输出中搜索,以查找设备驱动程序的名称 echo

    屏幕截图显示了“查找”对话框,其中显示了正在搜索的词回显。

  3. 应加载回显设备驱动程序。 !devnode 0 1 echo使用 命令显示与回显设备驱动程序关联的即插即用信息,如以下示例所示:

    0: Kd> !devnode 0 1 echo
    Dumping IopRootDeviceNode (= 0xffffe0007b725d30)
    DevNode 0xffffe0007b71a630 for PDO 0xffffe0007b71a960
      InstancePath is "ROOT\SAMPLE\0000"
      ServiceName is "ECHO"
      State = DeviceNodeStarted (0x308)
      Previous State = DeviceNodeEnumerateCompletion (0x30d)
    …
    
  4. 上一命令中显示的输出包括与正在运行的驱动程序实例关联的 PDO,在此示例中 ,0xffffe0007b71a960!devobj <PDO address>输入 命令以显示与回显设备驱动程序关联的即插即用信息。 使用计算机上显示的 PDO 地址 !devnode ,而不是此处显示的地址。

    0: kd> !devobj 0xffffe0007b71a960
    Device object (ffffe0007b71a960) is for:
     0000000e \Driver\PnpManager DriverObject ffffe0007b727e60
    Current Irp 00000000 RefCount 0 Type 00000004 Flags 00001040
    Dacl ffffc102c9b36031 DevExt 00000000 DevObjExt ffffe0007b71aab0 DevNode ffffe0007b71a630 
    ExtensionFlags (0x00000800)  DOE_DEFAULT_SD_PRESENT
    Characteristics (0x00000180)  FILE_AUTOGENERATED_DEVICE_NAME, FILE_DEVICE_SECURE_OPEN
    AttachedDevice (Upper) ffffe000801fee20 \Driver\ECHO
    Device queue is not busy.
    
  5. 命令中显示的 !devnode 0 1 输出包括与正在运行的驱动程序实例关联的 PDO 地址,在此示例中,它 0xffffe0007b71a960!devstack <PDO address>输入 命令以显示与设备驱动程序关联的即插即用信息。 使用计算机上显示的 PDO 地址 !devnode ,而不是此示例中显示的地址。

    0: kd> !devstack 0xffffe0007b71a960
      !DevObj           !DrvObj            !DevExt           ObjectName
      ffffe000801fee20  \Driver\ECHO       ffffe0007f72eff0  
    > ffffe0007b71a960  \Driver\PnpManager 00000000  0000000e
    !DevNode ffffe0007b71a630 :
      DeviceInst is "ROOT\SAMPLE\0000"
      ServiceName is "ECHO"
    

输出显示你有一个相当简单的设备驱动程序堆栈。 回显驱动程序是 PnPManager 节点的子级。 PnPManager 是根节点。

\Driver\ECHO
\Driver\PnpManager

此图显示了更复杂的设备节点树。

此图显示了一个具有大约 20 个节点的设备节点树。

有关更复杂的驱动程序堆栈的详细信息,请参阅 驱动程序堆栈 和设备 节点和设备堆栈

使用断点和源代码

在本部分中,设置断点和单步执行内核模式源代码。

若要能够单步执行代码并实时检查变量的值,请启用断点并设置源代码的路径。

断点停止特定代码行的代码执行。 从该点向前单步执行代码,调试代码的特定部分。

若要使用调试命令设置断点,请使用以下命令 b 之一。

命令 说明
bp 设置一个在卸载它所处模块之前处于活动状态的断点。
bu 设置一个在卸载模块时未解析的断点,并在重新加载模块时重新启用该断点。
bm 设置符号的断点。 此命令适当地使用 bubp ,并允许通配符 (*) 用于在匹配的每个符号上设置断点,就像类中的所有方法一样。

有关详细信息,请参阅 WinDbg 中的源代码调试

  1. 在主机系统上,使用 WinDbg UI 确认在当前 WinDbg 会话中启用了 调试>源模式

  2. 输入以下命令,将本地代码位置添加到源路径:

    .srcpath+ C:\DriverSamples\KMDF_Echo_Sample\driver\AutoSync
    
  3. 输入以下命令,将本地符号位置添加到符号路径:

    .sympath+ C:\DriverSamples\KMDF_Echo_Sample\driver\AutoSync
    
  4. x使用 命令检查与回显驱动程序关联的符号,以确定要用于断点的函数名称。 可以使用通配符或 Ctrl+F 查找 DeviceAdd 函数名称。

    0: kd> x ECHO!EchoEvt*
    8b4c7490          ECHO!EchoEvtIoQueueContextDestroy (void *)
    8b4c7000          ECHO!EchoEvtDeviceSelfManagedIoStart (struct WDFDEVICE__ *)
    8b4c7820          ECHO!EchoEvtTimerFunc (struct WDFTIMER__ *)
    8b4cb0e0          ECHO!EchoEvtDeviceSelfManagedIoSuspend (struct WDFDEVICE__ *)
    8b4c75d0          ECHO!EchoEvtIoWrite (struct WDFQUEUE__ *, struct WDFREQUEST__ *, unsigned int)
    8b4cb170          ECHO!EchoEvtDeviceAdd (struct WDFDRIVER__ *, struct 
    …
    

    输出显示 DeviceAdd 回显驱动程序 ECHO!EchoEvtDeviceAdd的 方法是 。

    或者,查看源代码以查找断点的函数名称。

  5. 使用驱动程序名称的 命令设置断点 bm ,后跟函数名称,例如 AddDevice,要在其中设置断点,用感叹号分隔。 此实验室使用 AddDevice watch正在加载的驱动程序。

    0: kd> bm ECHO!EchoEvtDeviceAdd
      1: fffff801`0bf9b1c0 @!"ECHO!EchoEvtDeviceAdd"
    

    可以将不同的语法与设置变量(如 <module>!<symbol><class>::<method>'<file.cpp>:<line number>')结合使用,或跳过多次 <condition> <#>。 有关详细信息,请参阅 WinDbg 和其他 Windows 调试器中的条件断点

  6. 通过输入 bl 命令列出当前断点以确认已设置断点:

    0: kd> bl
    1 e fffff801`0bf9b1c0     0001 (0001) ECHO!EchoEvtDeviceAdd
    

    此处所示的输出中的“e”指示允许触发断点 1。

  7. 输入 g (go) 命令,在目标系统上重启代码执行。

  8. 在目标系统上的 Windows 中,使用 图标或输入 mmc devmgmt.msc 打开设备管理器。 在“设备管理器”中,展开“示例”节点。

  9. 选择并按住或右键单击 KMDF 回显驱动程序条目,然后从菜单中选择“ 禁用 ”。

  10. 再次选择并按住或右键单击 KMDF 回显驱动程序条目,然后从菜单中选择“ 启用 ”。

  11. 在主机系统上,启用驱动程序时,应触发 AddDevice 调试断点。 应在目标系统上停止执行驱动程序代码。 命中断点时,应在 AddDevice 例程的开头停止执行。 调试命令输出显示 Breakpoint 1 hit

    显示 windbg 的屏幕截图,其中显示了示例代码局部变量和命令窗口。

  12. 通过输入 p 命令或按 F10 逐行执行代码,直到到达 AddDevice 例程的下一个末尾。 突出显示大括号字符 (}) ,如下所示。

    屏幕截图显示了代码窗口,其中突出显示了 adddevice 例程开头的大括号字符。

在下一部分中,检查执行 DeviceAdd 代码后变量的状态。

可以使用以下命令修改现有断点:

命令 说明
bl 列出断点。
bc 从列表中清除断点。 使用 bc * 清除所有断点。
bd 禁用断点。 使用 bd * 禁用所有断点。
be 启用断点。 使用 be * 启用所有断点。

或者,还可以在 WinDbg UI 中修改断点。

还可以设置在访问内存位置时触发的断点。 使用以下 ba 语法使用访问时 (中断) 命令:

ba <access> <size> <address> {options}
选项 说明
e execute:CPU 从地址提取指令时
r 读/写:当 CPU 读取或写入地址时
w 写入:CPU 写入地址时

在任何给定时间只能设置四个数据断点。 由你来决定确保正确对齐数据以触发断点。 单词必须以地址结尾,可被 2 整除,dword 必须可被 4 整除,四个单词必须以 0 或 8 结尾。

例如,若要在特定内存地址上设置读/写断点,可以使用类似于此示例的命令。

ba r 4 0x0003f7bf0

可以使用以下命令通过括号中显示的关联键盘快捷方式逐步执行代码。

  • 按 ctrl+中断) (中断。 只要系统正在运行并与 WinDbg 通信,此命令就会中断系统。 内核调试器中的序列为 Ctrl+C。
  • 运行到光标 (F7 或 Ctrl+F10) 。 将光标置于要中断执行的源或反汇编窗口中,然后按 F7。 代码执行将运行到该点。 如果代码执行流未到达游标指示的点,则 WinDbg 不会中断。 如果未执行 IF 语句,则可能会出现这种情况。
  • 运行 (F5) 。 运行 ,直到遇到断点或发生类似 bug 的事件检查。
  • 单步 (F10) 。 此命令会导致代码执行一次执行一条语句或一条指令。 如果遇到调用,代码执行会传递调用,而不进入被调用的例程。 如果编程语言为 C 或 C++ 且 WinDbg 处于源模式,则可以使用调试>源模式打开或关闭源模式
  • (F11) 中的步骤。 此命令类似于单步执行,只不过调用的执行确实进入被调用的例程。
  • 跳出 (Shift+F11) 。 此命令导致执行运行到调用堆栈中的当前例程或当前位置并退出。 如果已了解足够的例程,此命令将很有用。

有关详细信息,请参阅 WinDbg 中的源代码调试

查看变量和调用堆栈

在本部分中,显示有关变量和调用堆栈的信息。

本实验室假定你已使用前面所述的过程在 AddDevice 例程中停止。 若要查看此处显示的输出,请重复前面所述的步骤(如有必要)。

在主机系统上,若要显示变量,请使用 视图>本地 菜单项显示局部变量。

显示 WinDbg 局部变量窗口的屏幕截图。

若要查找全局变量地址的位置,请输入 ? <variable name>

  • 单步执行 (Shift+F11) – 此命令会导致执行运行到调用堆栈) 中的当前位置 (当前例程并退出。 如果你已经了解了足够的例程,这非常有用。

有关详细信息,请参阅调试参考文档中 的 WinDbg (经典) 源代码调试。

第 8 部分:查看变量和调用堆栈

在“第 8 节”中,将显示有关变量和调用堆栈的信息。

本实验室假定你已使用前面所述的过程在 AddDevice 例程中停止。 若要查看此处显示的输出,请重复前面所述的步骤(如有必要)。

<- 在主机系统上

显示变量

使用 视图>本地 菜单项显示局部变量。

windbg 局部变量窗口。

全局变量

可以通过键入 ?“ 查找全局变量地址的位置 。 <变量名称>

局部变量

通过键入 dv 命令,可以显示给定帧的所有局部变量的名称和值。 若要显示特定帧的所有局部变量的名称和值,请输入 dv 命令:

0: kd> dv
         Driver = 0x00001fff`7ff9c838
     DeviceInit = 0xffffd001`51978190
         status = 0n0

调用堆栈是导致程序计数器当前位置的函数调用链。 调用堆栈上的顶部函数是当前函数,下一个函数是调用当前函数的函数,依此而行。

若要显示调用堆栈,请使用 k* 命令。

命令 说明
kb 显示堆栈和前三个参数。
kp 显示堆栈和参数的完整列表。
kn 允许查看堆栈旁边包含帧信息的堆栈。
  1. 在主机系统上,如果要使调用堆栈保持可用,请选择“ 查看>调用堆栈 ”进行查看。 选择窗口顶部的列以切换其他信息的显示。

    屏幕截图显示 WinDbg 显示调用堆栈窗口。

  2. kn 调试处于中断状态的示例适配器代码时,使用 命令显示调用堆栈。

    3: kd> kn
    # Child-SP          RetAddr           Call Site
    00 ffffd001`51978110 fffff801`0942f55b ECHO!EchoEvtDeviceAdd+0x66 [c:\Samples\kmdf echo sample\c++\driver\autosync\driver.c @ 138]
    01 (Inline Function) --------`-------- Wdf01000!FxDriverDeviceAdd::Invoke+0x30 [d:\wbrtm\minkernel\wdf\framework\shared\inc\private\common\fxdrivercallbacks.hpp @ 61]
    02 ffffd001`51978150 fffff801`eed8097d Wdf01000!FxDriver::AddDevice+0xab [d:\wbrtm\minkernel\wdf\framework\shared\core\km\fxdriverkm.cpp @ 72]
    03 ffffd001`51978570 fffff801`ef129423 nt!PpvUtilCallAddDevice+0x35 [d:\9142\minkernel\ntos\io\pnpmgr\verifier.c @ 104]
    04 ffffd001`519785b0 fffff801`ef0c4112 nt!PnpCallAddDevice+0x63 [d:\9142\minkernel\ntos\io\pnpmgr\enum.c @ 7397]
    05 ffffd001`51978630 fffff801`ef0c344f nt!PipCallDriverAddDevice+0x6e2 [d:\9142\minkernel\ntos\io\pnpmgr\enum.c @ 3390]
    ...
    

调用堆栈显示,内核 (nt) 调用到即插即用代码 (PnP) ,该代码 (WDF) 调用了回显驱动程序DeviceAdd函数。

显示进程和线程

在本部分中,显示有关在内核模式下运行的进程和线程的信息。

进程

可以使用 !process 调试器扩展显示或设置进程信息。 设置断点以检查播放声音时使用的进程。

  1. 在主机系统上,输入 dv 命令以检查与例程关联的 EchoEvtIo 区域设置变量:

    0: kd> dv ECHO!EchoEvtIo*
    ECHO!EchoEvtIoQueueContextDestroy
    ECHO!EchoEvtIoWrite
    ECHO!EchoEvtIoRead         
    
  2. 使用 bc *清除前面的断点:

    0: kd> bc *  
    
  3. 使用以下命令在 EchoEvtIo 例程上设置符号断点:

    0: kd> bm ECHO!EchoEvtIo*
      2: aade5490          @!”ECHO!EchoEvtIoQueueContextDestroy”
      3: aade55d0          @!”ECHO!EchoEvtIoWrite”
      4: aade54c0          @!”ECHO!EchoEvtIoRead”
    
  4. 列出断点以确认断点设置正确:

    0: kd> bl
    1 e aabf0490 [c:\Samples\kmdf echo sample\c++\driver\autosync\queue.c @ 197]    0001 (0001) ECHO!EchoEvtIoQueueContextDestroy
    ...
    
  5. 输入 g 以重启代码执行:

    0: kd> g
    
  6. 在目标系统上,在 EchoApp.exe 目标系统上运行驱动程序测试程序。

  7. 在主机系统上,当测试应用运行时,将调用驱动程序中的 I/O 例程。 此调用会导致触发断点,并且目标系统上的驱动程序代码执行停止。

    Breakpoint 2 hit
    ECHO!EchoEvtIoWrite:
    fffff801`0bf95810 4c89442418      mov     qword ptr [rsp+18h],r8
    
  8. !process使用 命令显示运行 echoapp.exe所涉及的当前进程:

    0: kd> !process
    PROCESS ffffe0007e6a7780
        SessionId: 1  Cid: 03c4    Peb: 7ff7cfec4000  ParentCid: 0f34
        DirBase: 1efd1b000  ObjectTable: ffffc001d77978c0  HandleCount:  34.
        Image: echoapp.exe
        VadRoot ffffe000802c79f0 Vads 30 Clone 0 Private 135. Modified 5. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001cf270050
        ElapsedTime                       00:00:00.052
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         33824
        QuotaPoolUsage[NonPagedPool]      4464
        Working Set Sizes (now,min,max)  (682, 50, 345) (2728KB, 200KB, 1380KB)
        PeakWorkingSetSize                652
        VirtualSize                       16 Mb
        PeakVirtualSize                   16 Mb
        PageFaultCount                    688
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      138
    
            THREAD ffffe00080e32080  Cid 03c4.0ec0  Teb: 00007ff7cfece000 Win32Thread: 0000000000000000 RUNNING on processor 1
    

    输出显示进程与 echoapp.exe 线程相关联,该线程在命中驱动程序写入事件上的断点时正在运行。 有关详细信息,请参阅 !process

  9. !process 0 0使用 显示所有进程的摘要信息。 在输出中,使用 Ctrl+F 查找与 echoapp.exe 图像关联的进程的相同进程地址。 在示例中,进程地址为 ffffe0007e6a7780

    ...
    
    PROCESS ffffe0007e6a7780
        SessionId: 1  Cid: 0f68    Peb: 7ff7cfe7a000  ParentCid: 0f34
        DirBase: 1f7fb9000  ObjectTable: ffffc001cec82780  HandleCount:  34.
        Image: echoapp.exe
    
    ...
    
  10. 记录与 echoapp.exe 关联的进程 ID,以便稍后在本实验室中使用。 还可以使用 Ctrl+C 将地址复制到复制缓冲区供以后使用。

    _______ (echoapp.exe 进程地址)

  11. 根据需要,在调试器中输入 g 以向前运行代码 ,直到echoapp.exe 完成运行。 它多次命中读取和写入事件中的断点。 echoapp.exe完成后,通过按 Ctrl+ScrLk (Ctrl+中断) 切换到调试器。

  12. !process使用 命令确认正在运行其他进程。 在此处显示的输出中,“图像”值为 “System ”的进程与“ 回显 图像”值不同。

    1: kd> !process
    PROCESS ffffe0007b65d900
        SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
        DirBase: 001ab000  ObjectTable: ffffc001c9a03000  HandleCount: 786.
        Image: System
        VadRoot ffffe0007ce45930 Vads 14 Clone 0 Private 22. Modified 131605. Locked 64.
        DeviceMap ffffc001c9a0c220
        Token                             ffffc001c9a05530
        ElapsedTime                       21:31:02.516
    ...
    

    输出显示停止 OS 时,系统进程 ffffe0007b65d900 正在运行。

  13. !process使用 命令尝试查看与前面记录的echoapp.exe关联的进程 ID。 提供前面记录的 echoapp.exe 进程地址,而不是此示例中显示的示例进程地址。

    0: kd> !process ffffe0007e6a7780
    TYPE mismatch for process object at 82a9acc0
    

    进程对象不再可用,因为 echoapp.exe 进程不再运行。

线程数

用于查看和设置线程的命令类似于进程的命令。 使用 !thread 命令查看线程。 使用 .thread 设置当前线程。

  1. 在主机系统上,输入 g 调试器以在目标系统上重启代码执行。

  2. 在目标系统上,运行EchoApp.exe驱动程序测试程序。

  3. 在主机系统上,命中断点并停止代码执行。

    Breakpoint 4 hit
    ECHO!EchoEvtIoRead:
    aade54c0 55              push    ebp
    
  4. 若要查看正在运行的线程,请输入 !thread。 应显示类似于以下示例的信息:

    0: kd>  !thread
    THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
    IRP List:
        ffffe0007bc5be10: (0006,01f0) Flags: 00060a30  Mdl: 00000000
    Not impersonating
    DeviceMap                 ffffc001d83c6e80
    Owning Process            ffffe0008096c900       Image:         echoapp.exe
    ...
    

    记下 echoapp.exe的映像名称。 这表明你正在查看与测试应用关联的线程。

  5. !process使用 命令确定此线程是否是与echoapp.exe关联的进程中运行的唯线程。 进程中正在运行的线程的线程号与命令显示的线程 !thread 相同。

    0: kd> !process
    PROCESS ffffe0008096c900
        SessionId: 1  Cid: 0b28    Peb: 7ff7d00df000  ParentCid: 0f34
        DirBase: 1fb746000  ObjectTable: ffffc001db6b52c0  HandleCount:  34.
        Image: echoapp.exe
        VadRoot ffffe000800cf920 Vads 30 Clone 0 Private 135. Modified 8. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001cf5dc050
        ElapsedTime                       00:00:00.048
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         33824
        QuotaPoolUsage[NonPagedPool]      4464
        Working Set Sizes (now,min,max)  (681, 50, 345) (2724KB, 200KB, 1380KB)
        PeakWorkingSetSize                651
        VirtualSize                       16 Mb
        PeakVirtualSize                   16 Mb
        PageFaultCount                    686
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      138
    
            THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
    
  6. !process 0 0使用 命令查找两个相关进程的进程地址,并在此处记录这些进程地址。

    Cmd.exe:________

    EchoApp.exe:_________________

    0: kd> !process 0 0 
    
    …
    
    PROCESS ffffe0007bbde900
        SessionId: 1  Cid: 0f34    Peb: 7ff72dfa7000  ParentCid: 0c64
        DirBase: 19c5fa000  ObjectTable: ffffc001d8c2f300  HandleCount:  31.
        Image: cmd.exe
    …
    PROCESS ffffe0008096c900
        SessionId: 1  Cid: 0b28    Peb: 7ff7d00df000  ParentCid: 0f34
        DirBase: 1fb746000  ObjectTable: ffffc001db6b52c0  HandleCount:  34.
        Image: echoapp.exe
    …
    

    或者, !process 0 17 可以使用 来显示有关每个进程的详细信息。 此命令的输出可能很长。 可以使用 Ctrl+F 搜索输出。

  7. !process使用 命令列出运行计算机的两个进程的进程信息。 提供输出中的进程地址 !process 0 0 ,而不是此示例中显示的地址。

    此示例输出适用于前面记录 的cmd.exe 进程 ID。 此进程 ID 的映像名称 cmd.exe

    0: kd>  !process ffffe0007bbde900
    PROCESS ffffe0007bbde900
        SessionId: 1  Cid: 0f34    Peb: 7ff72dfa7000  ParentCid: 0c64
        DirBase: 19c5fa000  ObjectTable: ffffc001d8c2f300  HandleCount:  31.
        Image: cmd.exe
        VadRoot ffffe0007bb8e7b0 Vads 25 Clone 0 Private 117. Modified 20. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001d8c48050
        ElapsedTime                       21:33:05.840
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         24656
        QuotaPoolUsage[NonPagedPool]      3184
        Working Set Sizes (now,min,max)  (261, 50, 345) (1044KB, 200KB, 1380KB)
        PeakWorkingSetSize                616
        VirtualSize                       2097164 Mb
        PeakVirtualSize                   2097165 Mb
        PageFaultCount                    823
        MemoryPriority                    FOREGROUND
        BasePriority                      8
        CommitCharge                      381
    
            THREAD ffffe0007cf34880  Cid 0f34.0f1c  Teb: 00007ff72dfae000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
                ffffe0008096c900  ProcessObject
            Not impersonating
    ...
    

    此示例输出适用于前面记录 的echoapp.exe 进程 ID。

    0: kd>  !process ffffe0008096c900
    PROCESS ffffe0008096c900
        SessionId: 1  Cid: 0b28    Peb: 7ff7d00df000  ParentCid: 0f34
        DirBase: 1fb746000  ObjectTable: ffffc001db6b52c0  HandleCount:  34.
        Image: echoapp.exe
        VadRoot ffffe000800cf920 Vads 30 Clone 0 Private 135. Modified 8. Locked 0.
        DeviceMap ffffc001d83c6e80
        Token                             ffffc001cf5dc050
        ElapsedTime                       00:00:00.048
        UserTime                          00:00:00.000
        KernelTime                        00:00:00.000
        QuotaPoolUsage[PagedPool]         33824
        QuotaPoolUsage[NonPagedPool]      4464
        Working Set Sizes (now,min,max)  (681, 50, 345) (2724KB, 200KB, 1380KB)
        PeakWorkingSetSize                651
        VirtualSize                       16 Mb
        PeakVirtualSize                   16 Mb
        PageFaultCount                    686
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      138
    
            THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
            IRP List:
                ffffe0007bc5be10: (0006,01f0) Flags: 00060a30  Mdl: 00000000
            Not impersonating
    ...
    
  8. 在此处记录与两个进程关联的第一个线程地址。

    Cmd.exe:______

    EchoApp.exe:_______________

  9. !Thread使用 命令显示有关当前线程的信息。

    0: kd>  !Thread
    THREAD ffffe000809a0880  Cid 0b28.1158  Teb: 00007ff7d00dd000 Win32Thread: 0000000000000000 RUNNING on processor 0
    IRP List:
        ffffe0007bc5be10: (0006,01f0) Flags: 00060a30  Mdl: 00000000
    Not impersonating
    DeviceMap                 ffffc001d83c6e80
    Owning Process            ffffe0008096c900       Image:         echoapp.exe
    Attached Process          N/A            Image:         N/A
    ...
    

    与预期一样,当前线程是与 echoapp.exe 关联的线程,并且它处于运行状态。

  10. !Thread使用 命令显示与cmd.exe进程关联的线程的相关信息。 提供之前记录的线程地址。

    0: kd> !Thread ffffe0007cf34880
    THREAD ffffe0007cf34880  Cid 0f34.0f1c  Teb: 00007ff72dfae000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
        ffffe0008096c900  ProcessObject
    Not impersonating
    DeviceMap                 ffffc001d83c6e80
    Owning Process            ffffe0007bbde900       Image:         cmd.exe
    Attached Process          N/A            Image:         N/A
    Wait Start TickCount      4134621        Ticks: 0
    Context Switch Count      4056           IdealProcessor: 0             
    UserTime                  00:00:00.000
    KernelTime                00:00:01.421
    Win32 Start Address 0x00007ff72e9d6e20
    Stack Init ffffd0015551dc90 Current ffffd0015551d760
    Base ffffd0015551e000 Limit ffffd00155518000 Call 0
    Priority 14 BasePriority 8 UnusualBoost 3 ForegroundBoost 2 IoPriority 2 PagePriority 5
    Child-SP          RetAddr           : Args to Child                                                           : Call Site
    ffffd001`5551d7a0 fffff801`eed184fe : fffff801`eef81180 ffffe000`7cf34880 00000000`fffffffe 00000000`fffffffe : nt!KiSwapContext+0x76 [d:\9142\minkernel\ntos\ke\amd64\ctxswap.asm @ 109]
    ffffd001`5551d8e0 fffff801`eed17f79 : ffff03a5`ca56a3c8 000000de`b6a6e990 000000de`b6a6e990 00007ff7`d00df000 : nt!KiSwapThread+0x14e [d:\9142\minkernel\ntos\ke\thredsup.c @ 6347]
    ffffd001`5551d980 fffff801`eecea340 : ffffd001`5551da18 00000000`00000000 00000000`00000000 00000000`00000388 : nt!KiCommitThreadWait+0x129 [d:\9142\minkernel\ntos\ke\waitsup.c @ 619]
    ...
    

    此线程与 cmd.exe 相关联,并且处于等待状态。

  11. 提供等待 CMD.exe 线程的线程地址,以将上下文更改为该等待线程。

    0: kd> .Thread ffffe0007cf34880
    Implicit thread is now ffffe000`7cf34880
    
  12. k使用 命令查看与等待线程关联的调用堆栈。

    0: kd> k
      *** Stack trace for last set context - .thread/.cxr resets it
    # Child-SP          RetAddr           Call Site
    00 ffffd001`5551d7a0 fffff801`eed184fe nt!KiSwapContext+0x76 [d:\9142\minkernel\ntos\ke\amd64\ctxswap.asm @ 109]
    01 ffffd001`5551d8e0 fffff801`eed17f79 nt!KiSwapThread+0x14e [d:\9142\minkernel\ntos\ke\thredsup.c @ 6347]
    02 ffffd001`5551d980 fffff801`eecea340 nt!KiCommitThreadWait+0x129 [d:\9142\minkernel\ntos\ke\waitsup.c @ 619]
    03 ffffd001`5551da00 fffff801`ef02e642 nt!KeWaitForSingleObject+0x2c0 [d:\9142\minkernel\ntos\ke\wait.c @ 683]
    ...
    

    调用堆栈元素(如 ) KiCommitThreadWait 指示此线程未按预期方式运行。

有关线程和进程的详细信息,请参阅以下参考:

IRQL、注册和结束 WinDbg 会话

在本部分中,显示 IRQL) (中断请求级别以及寄存器的内容。

查看保存的 IRQL

IRQL 用于管理中断服务的优先级。 每个处理器都有一个 IRQL 设置,线程可以提高或降低该设置。 在处理器 IRQL 设置或以下发生的中断将被屏蔽,并且不会干扰当前操作。 高于处理器 IRQL 设置的中断优先于当前操作。

在主机系统上, !irql 扩展在发生调试器中断之前,在目标计算机的当前处理器上显示 IRQL。 当目标计算机中断调试器时,IRQL 会更改,但在调试器中断之前有效的 IRQL 将保存并由 显示 !irql

0: kd> !irql
Debugger saved IRQL for processor 0x0 -- 2 (DISPATCH_LEVEL)

查看寄存器

在主机系统上,使用 r (Registers) 命令显示当前处理器上当前线程的寄存器内容。

0: kd> r
rax=000000000000c301 rbx=ffffe00173eed880 rcx=0000000000000001
rdx=000000d800000000 rsi=ffffe00173eed8e0 rdi=ffffe00173eed8f0
rip=fffff803bb757020 rsp=ffffd001f01f8988 rbp=ffffe00173f0b620
 r8=000000000000003e  r9=ffffe00167a4a000 r10=000000000000001e
r11=ffffd001f01f88f8 r12=0000000000000000 r13=ffffd001f01efdc0
r14=0000000000000001 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
nt!DbgBreakPointWithStatus:
fffff803`bb757020 cc              int     3

或者,可以通过选择“查看>寄存器”来显示寄存器的内容。 有关详细信息,请参阅 r (Registers)

在单步执行程序集语言代码时和其他方案中,查看寄存器的内容可能会有所帮助。 有关汇编语言反汇编的详细信息,请参阅 批注 x86 反汇编批注 x64 反汇编

有关寄存器内容的信息,请参阅 x86 体系结构x64 体系结构

结束 WinDbg 会话

如果要使调试器保持连接,但想要在目标上工作,请使用 bc *清除所有断点,以便目标计算机不会尝试连接到主计算机调试器。 g然后使用 命令让目标计算机再次运行。

若要结束调试会话,请在主机系统上中断调试器并输入 qd (退出和分离) 命令,或从菜单中选择“ 停止调试 ”。

0: kd> qd

有关详细信息,请参阅 在 WinDbg 中结束调试会话

Windows 调试资源

有关详细信息,请参阅 Windows 调试。 其中一些书籍在示例中使用早期版本的 Windows(如 Windows Vista),但所讨论的概念适用于大多数版本的 Windows。

另请参阅