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 示例音频驱动程序:
从 GitHub 下载并提取 KMDF Echo 示例。
在 GitHub 中查看回显示例。
阅读 有关示例的信息。
浏览所有 Windows 驱动程序示例。
KMDF Echo 示例位于 常规 文件夹中。
在一个 zip 文件中下载驱动程序示例: 驱动程序示例
将 zip 文件下载到本地硬盘驱动器。
选择并按住或右键单击 zip 文件,然后选择“ 全部提取”。 指定一个新文件夹,或浏览到现有文件夹以存储提取的文件。 例如,可以将 C:\DriverSamples\ 指定为要将文件提取到其中的新文件夹。
提取文件后,转到以下子文件夹: C:\DriverSamples\general\echo\kmdf
在 Microsoft Visual Studio 中,选择“ 文件>打开>项目/解决方案...” ,然后转到包含提取文件的文件夹,例如 C:\DriverSamples\general\echo\kmdf。 双击 kmdfecho 解决方案文件将其打开。
在 Visual Studio 中,找到解决方案资源管理器。 如果此窗口尚未打开,请从“视图”菜单中选择“解决方案资源管理器”。 在 解决方案资源管理器 中,可以看到一个包含三个项目的解决方案。
设置示例的配置和平台。 在“解决方案资源管理器”中,选择并按住或右键单击“解决方案”kmdfecho“ (3 个项目) ,然后选择”Configuration Manager”。 确保这三个项目的配置和平台设置相同。 默认情况下,配置设置为 Win10 Debug,平台设置为 所有项目的 Win64 。 如果对一个项目进行任何配置或平台更改,请对其余三个项目进行相同的更改。
需要修改驱动程序示例,以使用与现有驱动程序不重叠的值。 请参阅 从示例代码到生产驱动程序 - 示例中的更改 内容,了解如何创建与 Windows 中安装的现有实际驱动程序共存的唯一驱动程序示例。
设置运行时库。 打开回显驱动程序属性页并找到 C/C++>代码生成。 将运行时库更改为多线程调试 (/MTd) 。 有关生成选项的详细信息,请参阅 /MD、/MT、/LD (使用 Run-Time 库) 。
在驱动程序属性中,确保 将“驱动程序签名>签名模式 ”设置为 “测试签名”。
在 Visual Studio 中,选择“ 生成>解决方案”。
生成窗口应显示一条消息,指示所有三个项目的生成都成功。
如果遇到生成错误消息,请使用生成错误号来确定修复方法。 例如, MSBuild 错误 MSB8040 介绍了如何使用 spectre 缓解库。
在“文件资源管理器”中,转到包含示例提取文件的文件夹。 例如,如果这是之前指定的文件夹,请转到 C:\DriverSamples\general\echo\kmdf。 在该文件夹中,编译的驱动程序文件的位置因在Configuration Manager中选择的配置和平台设置而异。 如果保留默认设置不变,则编译的驱动程序文件将保存到名为 \x64\Debug 的文件夹中,以便进行 64 位调试生成。
转到包含 Autosync 驱动程序生成文件的文件夹: C:\DriverSamples\general\echo\kmdf\driver\AutoSync\x64\Debug。
该文件夹应包含以下文件:
在目标系统上安装 KMDF 回显驱动程序示例
在本部分中,使用 DevCon 工具安装回显示例驱动程序。
安装驱动程序的计算机称为 目标计算机 或 测试计算机。 通常,此计算机独立于开发和生成驱动程序包的计算机。 开发和生成驱动程序的计算机称为 主计算机。
将驱动程序包移动到目标计算机并安装驱动程序的过程称为 部署 驱动程序。
在部署测试签名驱动程序之前,请通过启用测试签名来准备目标计算机。 还需要在 WDK 安装中找到 DevCon 工具,并将其复制到目标系统。
若要在目标系统上安装驱动程序,请执行以下步骤。
在目标系统上,启用测试签名的驱动程序:
打开 Windows 设置。
在 “更新和安全”中,选择“ 恢复”。
在“ 高级启动”下,选择“ 立即重启”。
当计算机重启时,选择“ 启动选项”。 在“Windows 10”中,选择“排查>高级选项”>“启动设置”,然后选择“重启”。
按 F7 键,选择 “禁用驱动程序签名强制 ”。
重启目标计算机。
在主机系统上,转到 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
将出现一个对话框,指示测试驱动程序是未签名的驱动程序。 选择“仍然安装此驱动程序”以继续。
如果安装有任何问题,检查以下文件了解详细信息。
%windir%\inf\setupapi.dev.log
成功安装示例驱动程序后,即可对其进行测试。
在目标计算机上的命令提示符窗口中,输入 devmgmt 以打开设备管理器。 在“设备管理器”的“视图”菜单上,按类型选择“设备”。在设备树中,在“示例设备”节点中找到“示例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
在本部分中,设置符号路径并使用内核调试器命令显示有关 KMDF 回显示例驱动程序的信息。
若要查看有关驱动程序的信息,请执行以下操作:
在主机系统上,如果关闭了调试器,请在管理员命令提示符窗口中使用以下命令再次打开它。
WinDbg -k net:port=50000,key=2steg4fzbj2sz.23418vzkd4ko3.1g34ou07z4pev.1sp3yo9yz874p
使用 Ctrl+Break (Scroll Lock) 中断目标系统上运行的代码。
若要在 WinDbg 环境中设置 Microsoft 符号服务器的符号路径,请使用 .symfix
命令。
0: kd> .symfix
若要添加本地符号位置以使用本地符号,请使用 .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
在调试器的命令区域中输入以下命令,以显示有关回显驱动程序的信息:
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。
由于此实验室之前设置 prefer_dml
,因此输出的某些元素是可以选择的热链接。 选择调试输出中的“ 浏览所有全局符号 ”链接,以显示有关以字母“a”开头的项符号的信息。
0: kd> x /D Echo!a*
回显示例不包含任何以字母“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 (检查符号) 。
该 !lmi
扩展显示有关模块的详细信息。 输入 !lmi echo
。 输出应类似于此示例中显示的文本:
0: kd> !lmi echo
Loaded Module Info: [echo]
Module: ECHO
Base Address: fffff8010bf94000
Image Name: ECHO.sys
!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
输入以下内容以更改默认调试位掩码,以便在调试器中显示来自目标系统的所有调试消息:
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。
在主机系统上,若要查看即插即用设备树中的所有设备节点,请输入 命令!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…
使用 Ctrl+F 在生成的输出中搜索,以查找设备驱动程序的名称 echo。
应加载回显设备驱动程序。 !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)
上一命令中显示的输出包括与正在运行的驱动程序实例关联的 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.
命令中显示的 !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
此图显示了更复杂的设备节点树。
有关更复杂的驱动程序堆栈的详细信息,请参阅 驱动程序堆栈 和设备 节点和设备堆栈。
使用断点和源代码
在本部分中,设置断点和单步执行内核模式源代码。
若要能够单步执行代码并实时检查变量的值,请启用断点并设置源代码的路径。
断点停止特定代码行的代码执行。 从该点向前单步执行代码,调试代码的特定部分。
若要使用调试命令设置断点,请使用以下命令 b
之一。
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
的 方法是 。
或者,查看源代码以查找断点的函数名称。
使用驱动程序名称的 命令设置断点 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 调试器中的条件断点。
通过输入 bl
命令列出当前断点以确认已设置断点:
0: kd> bl
1 e fffff801`0bf9b1c0 0001 (0001) ECHO!EchoEvtDeviceAdd
此处所示的输出中的“e”指示允许触发断点 1。
输入 g
(go) 命令,在目标系统上重启代码执行。
在目标系统上的 Windows 中,使用 图标或输入 mmc devmgmt.msc 打开设备管理器。 在“设备管理器”中,展开“示例”节点。
选择并按住或右键单击 KMDF 回显驱动程序条目,然后从菜单中选择“ 禁用 ”。
再次选择并按住或右键单击 KMDF 回显驱动程序条目,然后从菜单中选择“ 启用 ”。
在主机系统上,启用驱动程序时,应触发 AddDevice 调试断点。 应在目标系统上停止执行驱动程序代码。 命中断点时,应在 AddDevice 例程的开头停止执行。 调试命令输出显示 Breakpoint 1 hit
。
通过输入 p
命令或按 F10 逐行执行代码,直到到达 AddDevice 例程的下一个末尾。 突出显示大括号字符 (}
) ,如下所示。
在下一部分中,检查执行 DeviceAdd 代码后变量的状态。
可以使用以下命令修改现有断点:
在任何给定时间只能设置四个数据断点。 由你来决定确保正确对齐数据以触发断点。 单词必须以地址结尾,可被 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 例程中停止。 若要查看此处显示的输出,请重复前面所述的步骤(如有必要)。
在主机系统上,若要显示变量,请使用 视图>本地 菜单项显示局部变量。
若要查找全局变量地址的位置,请输入 ? <variable name>
。
单步执行 (Shift+F11) – 此命令会导致执行运行到调用堆栈) 中的当前位置 (当前例程并退出。 如果你已经了解了足够的例程,这非常有用。
有关详细信息,请参阅调试参考文档中 的 WinDbg (经典) 源代码调试。
第 8 部分:查看变量和调用堆栈
在“第 8 节”中,将显示有关变量和调用堆栈的信息。
本实验室假定你已使用前面所述的过程在 AddDevice 例程中停止。 若要查看此处显示的输出,请重复前面所述的步骤(如有必要)。
<- 在主机系统上
使用 视图>本地 菜单项显示局部变量。
可以通过键入 ?“ 查找全局变量地址的位置 。 <变量名称>。
通过键入 dv 命令,可以显示给定帧的所有局部变量的名称和值。
若要显示特定帧的所有局部变量的名称和值,请输入 dv
命令:
0: kd> dv
Driver = 0x00001fff`7ff9c838
DeviceInit = 0xffffd001`51978190
status = 0n0
调用堆栈是导致程序计数器当前位置的函数调用链。 调用堆栈上的顶部函数是当前函数,下一个函数是调用当前函数的函数,依此而行。
若要显示调用堆栈,请使用 k*
命令。
在 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 调试器扩展显示或设置进程信息。 设置断点以检查播放声音时使用的进程。
在主机系统上,输入 dv
命令以检查与例程关联的 EchoEvtIo
区域设置变量:
0: kd> dv ECHO!EchoEvtIo*
ECHO!EchoEvtIoQueueContextDestroy
ECHO!EchoEvtIoWrite
ECHO!EchoEvtIoRead
使用 bc *
清除前面的断点:
0: kd> bc *
使用以下命令在 EchoEvtIo
例程上设置符号断点:
0: kd> bm ECHO!EchoEvtIo*
2: aade5490 @!”ECHO!EchoEvtIoQueueContextDestroy”
3: aade55d0 @!”ECHO!EchoEvtIoWrite”
4: aade54c0 @!”ECHO!EchoEvtIoRead”
列出断点以确认断点设置正确:
0: kd> bl
1 e aabf0490 [c:\Samples\kmdf echo sample\c++\driver\autosync\queue.c @ 197] 0001 (0001) ECHO!EchoEvtIoQueueContextDestroy
输入 g
以重启代码执行:
0: kd> g
在目标系统上,在 EchoApp.exe
目标系统上运行驱动程序测试程序。
在主机系统上,当测试应用运行时,将调用驱动程序中的 I/O 例程。 此调用会导致触发断点,并且目标系统上的驱动程序代码执行停止。
Breakpoint 2 hit
ECHO!EchoEvtIoWrite:
fffff801`0bf95810 4c89442418 mov qword ptr [rsp+18h],r8
!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。
!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
记录与 echoapp.exe 关联的进程 ID,以便稍后在本实验室中使用。 还可以使用 Ctrl+C 将地址复制到复制缓冲区供以后使用。
_______ (echoapp.exe 进程地址)
根据需要,在调试器中输入 g
以向前运行代码 ,直到echoapp.exe 完成运行。 它多次命中读取和写入事件中的断点。 echoapp.exe完成后,通过按 Ctrl+ScrLk (Ctrl+中断) 切换到调试器。
!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 正在运行。
!process
使用 命令尝试查看与前面记录的echoapp.exe关联的进程 ID。 提供前面记录的 echoapp.exe 进程地址,而不是此示例中显示的示例进程地址。
0: kd> !process ffffe0007e6a7780
TYPE mismatch for process object at 82a9acc0
进程对象不再可用,因为 echoapp.exe 进程不再运行。
用于查看和设置线程的命令类似于进程的命令。 使用 !thread 命令查看线程。 使用 .thread 设置当前线程。
在主机系统上,输入 g
调试器以在目标系统上重启代码执行。
在目标系统上,运行EchoApp.exe驱动程序测试程序。
在主机系统上,命中断点并停止代码执行。
Breakpoint 4 hit
ECHO!EchoEvtIoRead:
aade54c0 55 push ebp
若要查看正在运行的线程,请输入 !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的映像名称。 这表明你正在查看与测试应用关联的线程。
!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
!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 搜索输出。
!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
在此处记录与两个进程关联的第一个线程地址。
Cmd.exe:______
EchoApp.exe:_______________
!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 关联的线程,并且它处于运行状态。
!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 相关联,并且处于等待状态。
提供等待 CMD.exe 线程的线程地址,以将上下文更改为该等待线程。
0: kd> .Thread ffffe0007cf34880
Implicit thread is now ffffe000`7cf34880
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。
《高级 Windows 调试》,作者:Mario Hewardt 和 Daniel Pravat
内部 Windows 调试:Windows® 中调试和跟踪策略的实用指南,作者:Tarik Soulami
Windows Internals 作者:Pavel Yosifovich、Alex Ionescu、Mark Russinovich 和 David Solomon
碎片整理工具显示 WinDbg 剧集 13-29
培训供应商
标准调试技术
专用调试技术
Windows 调试入门