Windows 调试器(WinDbg)是 Windows 调试工具中包含的内核模式和用户模式调试器。 本文提供练习,帮助你开始使用 WinDbg 作为内核模式调试器。
若要获取 WinDbg,请参阅下载并安装 Windows 调试器(WinDbg)。
设置内核模式调试
A kernel-mode debugging environment typically has two computers, the host computer and the target computer. 调试程序在主计算机上运行,所调试的代码在目标计算机上运行。 主计算机和目标计算机由调试电缆连接。
Windows 调试器支持以下类型的电缆:
- Ethernet
- USB 3.0
- Serial (also called null modem)
为了获得速度和可靠性,应将以太网电缆与本地网络中心配合使用。 下图演示了使用以太网电缆进行调试的主机和目标计算机:
旧版 Windows 的选项是使用直接电缆,例如串行电缆:
按照所需配置的安装过程启动该过程:
若要设置主机和目标计算机,请参阅 手动设置内核模式调试。
若要将调试器连接到 Hyper-V 虚拟机(VM),请参阅 为虚拟机主机设置网络调试 - 通过网络(KDNET)进行内核调试。
建立内核模式调试会话
设置好主计算机和目标计算机并用调试电缆连接它们后,可以建立内核模式调试会话。
继续阅读用于设置过程的文章中的说明。 例如,如果将主机和目标计算机设置为通过以太网电缆进行调试,以便进行内核模式调试,请按照 “设置 KDNET 网络内核调试”中的说明自动作。
开始使用 WinDbg 进行调试
若要开始使用 WinDbg 进行调试会话,请执行以下步骤:
在主计算机上打开 WinDbg,与目标计算机建立内核模式调试会话。
Open the debugger documentation CHM (.chm) file by selecting Help>Contents.
调试器文档在适用于 Windows 的调试工具中也联机提供。 有关详细信息,请参阅 安装 Windows 调试器。
建立内核模式调试会话时,WinDbg 可能会自动中断目标计算机。 If WinDbg doesn't break in, select Debug>Break.
在 WinDbg 窗口底部的命令行中,运行以下命令:
使用 .sympath (Symbol Path) 命令设置符号路径 。
.sympath srv*
输出类似于以下示例:
Symbol search path is: srv* Expanded Symbol search path is: cache*;SRV*https://msdl.microsoft.com/download/symbols
The symbol search path tells WinDbg where to look for symbol program database (PDB) files (.pdb). 调试器需要符号文件来获取有关代码模块的信息,例如函数名称和变量名称。
Run the .reload command so WinDbg starts finding and loading symbols files.
.reload
View a list of loaded modules with the lm command.
lm
输出类似于以下示例:
0:000>3: kd> lm start end module name fffff800`00000000 fffff800`00088000 CI (deferred) ... fffff800`01143000 fffff800`01151000 BasicRender (deferred) fffff800`01151000 fffff800`01163000 BasicDisplay (deferred) ... fffff800`02a0e000 fffff800`03191000 nt (pdb symbols) C:\...\ntkrnlmp.pdb fffff800`03191000 fffff800`03200000 hal (deferred) ...
Start the target computer running again with the g (Go) command.
g
Break in to the target computer again by selecting Debug>Break.
运行 dt (显示类型) 命令并检查
_FILE_OBJECT
模块中的nt
数据类型:dt nt!_FILE_OBJECT
输出类似于以下示例:
0:000>0: kd> dt nt!_FILE_OBJECT +0x000 Type : Int2B +0x002 Size : Int2B +0x008 DeviceObject : Ptr64 _DEVICE_OBJECT +0x010 Vpb : Ptr64 _VPB ... +0x0c0 IrpList : _LIST_ENTRY +0x0d0 FileObjectExtension : Ptr64 Void
运行 x (检查符号) 命令并查看模块中的
nt
一些符号:x nt!\*CreateProcess\*
输出类似于以下示例:
0:000>0: kd> x nt!*CreateProcess* fffff800`030821cc nt!ViCreateProcessCallbackInternal (<no parameter info>) ... fffff800`02e03904 nt!MmCreateProcessAddressSpace (<no parameter info>) fffff800`02cece00 nt!PspCreateProcessNotifyRoutine = <no type information> ...
运行 bu (设置断点) 和 bl (断点列表) 命令来设置和检查断点:
使用
bu
命令并在 Windows 调用中设置断点以进入MmCreateProcessAddressSpace
例程。 然后运行bl
命令并验证是否已设置断点。bu nt!MmCreateProcessAddressSpace bl
输出类似于以下示例:
0:000>0: kd> bu nt!MmCreateProcessAddressSpace 0: kd> bl 0 e fffff800`02e03904 0001 (0001) nt!MmCreateProcessAddressSpace
输入
g
(Go) 以让目标计算机运行。g
当 Windows 调用
MmCreateProcessAddressSpace
例程时,目标计算机会进入调试器。如果目标计算机没有立即闯入调试器,请在目标计算机上执行一些作。 例如,打开记事本并保存文件。
使用
.reload
和 k(显示堆栈回溯) 命令查看堆栈跟踪:.reload k
输出类似于以下示例:
0:000>2: kd> k Child-SP RetAddr Call Site ffffd000`224b4c88 fffff800`02d96834 nt!MmCreateProcessAddressSpace ffffd000`224b4c90 fffff800`02dfef17 nt!PspAllocateProcess+0x5d4 ffffd000`224b5060 fffff800`02b698b3 nt!NtCreateUserProcess+0x55b ... 000000d7`4167fbb0 00007ffd`14b064ad KERNEL32!BaseThreadInitThunk+0xd 000000d7`4167fbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
Select View>Disassembly. Then select Debug>Step Over (or select F10).
Enter step commands a few more times as you watch the output in the Disassembly window.
使用 bc (断点清除) 命令清除断点。
bc *
输入
g
(Go) 以让目标计算机运行。g
To break in again, select Debug>Break, or select CTRL-Break.
View a list of all processes with the !process command:
!process 0 0
输出类似于以下示例:
0:000>0: kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** PROCESS ffffe000002287c0 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 001aa000 ObjectTable: ffffc00000003000 HandleCount: <Data Not Accessible> Image: System PROCESS ffffe00001e5a900 SessionId: none Cid: 0124 Peb: 7ff7809df000 ParentCid: 0004 DirBase: 100595000 ObjectTable: ffffc000002c5680 HandleCount: <Data Not Accessible> Image: smss.exe ... PROCESS ffffe00000d52900 SessionId: 1 Cid: 0910 Peb: 7ff669b8e000 ParentCid: 0a98 DirBase: 3fdba000 ObjectTable: ffffc00007bfd540 HandleCount: <Data Not Accessible> Image: explorer.exe
复制进程的地址,例如
ffffe00000d52900
,并使用!process
命令查看进程信息。 将部分<process-address>
替换为进程地址:!process <process-address> 2
进程的输出
ffffe00000d52900
显示进程中的以下线程:0:000>0:000>0: kd> !process ffffe00000d52900 2 PROCESS ffffe00000d52900 SessionId: 1 Cid: 0910 Peb: 7ff669b8e000 ParentCid: 0a98 DirBase: 3fdba000 ObjectTable: ffffc00007bfd540 HandleCount: Image: explorer.exe THREAD ffffe00000a0d880 Cid 0910.090c Teb: 00007ff669b8c000 ffffe00000d57700 SynchronizationEvent THREAD ffffe00000e48880 Cid 0910.0ad8 Teb: 00007ff669b8a000 ffffe00000d8e230 NotificationEvent ffffe00000cf6870 Semaphore Limit 0xffff ffffe000039c48c0 SynchronizationEvent ... THREAD ffffe00000e6d080 Cid 0910.0cc0 Teb: 00007ff669a10000 ffffe0000089a300 QueueObject
Copy the address for a thread, such as
ffffe00000e6d080
, and view the thread information with the !thread command. 将<thread-address>
部分替换为线程地址:!thread <thread-ddress>
线程的
ffffe00000e6d080
输出显示以下摘要信息:0: kd> !thread ffffe00000e6d080 THREAD ffffe00000e6d080 Cid 0910.0cc0 Teb: 00007ff669a10000 Win32Thread: 0000000000000000 WAIT: ... ffffe0000089a300 QueueObject Not impersonating DeviceMap ffffc000034e7840 Owning Process ffffe00000d52900 Image: explorer.exe Attached Process N/A Image: N/A Wait Start TickCount 13777 Ticks: 2 (0:00:00:00.031) Context Switch Count 2 IdealProcessor: 1 UserTime 00:00:00.000 KernelTime 00:00:00.000 Win32 Start Address ntdll!TppWorkerThread (0x00007ffd14ab2850) Stack Init ffffd00021bf1dd0 Current ffffd00021bf1580 Base ffffd00021bf2000 Limit ffffd00021bec000 Call 0 Priority 13 BasePriority 13 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5 ...
View all the device nodes in the Plug and Play device tree with the !devnode command:
!devnode 0 1
输出类似于以下示例:
0:000>0: kd> !devnode 0 1 Dumping IopRootDeviceNode (= 0xffffe000002dbd30) DevNode 0xffffe000002dbd30 for PDO 0xffffe000002dc9e0 InstancePath is "HTREE\ROOT\0" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) DevNode 0xffffe000002d9d30 for PDO 0xffffe000002daa40 InstancePath is "ROOT\volmgr\0000" ServiceName is "volmgr" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) DevNode 0xffffe00001d49290 for PDO 0xffffe000002a9a90 InstancePath is "STORAGE\Volume\{3007dfd3-df8d-11e3-824c-806e6f6e6963}#0000000000100000" ServiceName is "volsnap" TargetDeviceNotify List - f 0xffffc0000031b520 b 0xffffc0000008d0f0 State = DeviceNodeStarted (0x308) Previous State = DeviceNodeStartPostWork (0x307) ...
使用
!devnode
以下命令查看设备节点及其硬件资源:!devnode 0 9
输出类似于以下示例:
0:000>... DevNode 0xffffe000010fa770 for PDO 0xffffe000010c2060 InstancePath is "PCI\VEN_8086&DEV_2937&SUBSYS_2819103C&REV_02\3&33fd14ca&0&D0" ServiceName is "usbuhci" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) TranslatedResourceList at 0xffffc00003c78b00 Version 1.1 Interface 0x5 Bus #0 Entry 0 - Port (0x1) Device Exclusive (0x1) Flags (0x131) - PORT_MEMORY PORT_IO 16_BIT_DECODE POSITIVE_DECODE Range starts at 0x3120 for 0x20 bytes Entry 1 - DevicePrivate (0x81) Device Exclusive (0x1) Flags (0000) - Data - {0x00000001, 0x00000004, 0000000000} Entry 2 - Interrupt (0x2) Shared (0x3) Flags (0000) - LEVEL_SENSITIVE Level 0x8, Vector 0x81, Group 0, Affinity 0xf ...
使用
!devnode
命令查看服务名称为“disk”的设备节点:!devnode 0 1 disk
输出类似于以下示例:
0: kd> !devnode 0 1 disk Dumping IopRootDeviceNode (= 0xffffe000002dbd30) DevNode 0xffffe0000114fd30 for PDO 0xffffe00001159610 InstancePath is "IDE\DiskST3250820AS_____________________________3.CHL___\5&14544e82&0&0.0.0" ServiceName is "disk" State = DeviceNodeStarted (0x308) Previous State = DeviceNodeEnumerateCompletion (0x30d) ...
命令的
!devnode 0 1
输出显示节点的物理设备对象(PDO)的地址。Copy the address of a PDO, such as
0xffffe00001159610
, and view the PDO details with the !devstack command. 将<PDO-address>
部分替换为 PDO 信息:!devstack <PDO-address>
PDO
0xffffe00001159610
线程的输出显示以下设备堆栈:0:000>0: kd> !devstack 0xffffe00001159610 !DevObj !DrvObj !DevExt ObjectName ffffe00001d50040 \Driver\partmgr ffffe00001d50190 ffffe00001d51450 \Driver\disk ffffe00001d515a0 DR0 ffffe00001156e50 \Driver\ACPI ffffe000010d8bf0
Get information about the disk.sys driver object with the !drvobj command and the driver name "disk":
!drvobj disk 2
输出显示有关驱动程序对象的详细信息:
0:000>0: kd> !drvobj disk 2 Driver object (ffffe00001d52680) is for: \Driver\disk DriverEntry: fffff800006b1270 disk!GsDriverEntry DriverStartIo: 00000000 DriverUnload: fffff800010b0b5c CLASSPNP!ClassUnload AddDevice: fffff800010aa110 CLASSPNP!ClassAddDevice Dispatch routines: [00] IRP_MJ_CREATE fffff8000106d160 CLASSPNP!ClassGlobalDispatch [01] IRP_MJ_CREATE_NAMED_PIPE fffff80002b0ab24 nt!IopInvalidDeviceRequest [02] IRP_MJ_CLOSE fffff8000106d160 CLASSPNP!ClassGlobalDispatch [03] IRP_MJ_READ fffff8000106d160 CLASSPNP!ClassGlobalDispatch ... [1b] IRP_MJ_PNP fffff8000106d160 CLASSPNP!ClassGlobalDispatch
命令的
!drvobj
输出显示调度例程的地址,例如CLASSPNP!ClassGlobalDispatch
。 使用以下命令在ClassGlobalDispatch
例程中设置和验证断点:bu CLASSPNP!ClassGlobalDispatch bl
输入
g
(Go) 以让目标计算机运行。g
当 Windows 调用
ClassGlobalDispatch
例程时,目标计算机会进入调试器。如果目标计算机没有立即闯入调试器,请在目标计算机上执行一些作。或者,打开记事本并保存文件。
使用以下命令查看堆栈跟踪:
.reload k
输出类似于以下示例:
2: kd> k Child-SP RetAddr Call Site ffffd000`21d06cf8 fffff800`0056c14e CLASSPNP!ClassGlobalDispatch ffffd000`21d06d00 fffff800`00f2c31d volmgr!VmReadWrite+0x13e ffffd000`21d06d40 fffff800`0064515d fvevol!FveFilterRundownReadWrite+0x28d ffffd000`21d06e20 fffff800`0064578b rdyboost!SmdProcessReadWrite+0x14d ffffd000`21d06ef0 fffff800`00fb06ad rdyboost!SmdDispatchReadWrite+0x8b ffffd000`21d06f20 fffff800`0085cef5 volsnap!VolSnapReadFilter+0x5d ffffd000`21d06f50 fffff800`02b619f7 Ntfs!NtfsStorageDriverCallout+0x16 ...
使用 qd (退出和分离) 命令结束调试会话。
qd
命令摘要
以下链接提供有关本文中介绍的命令的详细信息。
- .sympath(设置符号路径)
- .reload(重新加载模块)
- x(检查符号)
- g (Go)
- dt(显示类型)
- lm(列出已加载的模块)
- k(显示堆栈回溯)
- bu(设置断点)
- bl(断点列表)
- bc(断点清除)
- !process
- !thread
- !devnode
- !devstack
- !drvobj
- qd (退出和分离)
For more information about menu commands like Debug>Break and Help>Contents, see the Get started with WinDbg (user-mode) article.