Windbg调试速查手册

Windbg 是 Microsoft 公司推出的免费的、带 GUI 的调试器,支持 Source 和 Assembly 两种模式的调试。

Windbg 不仅可以调试应用程序,还可以进行系统内核调试,Windbg 支持的平台包括 X86、IA64、AMD64。

符号文件

在菜单项 File -> Symbol File Path 中设置符合文件搜索路径,如:

1
D:\symbol_path;SRV*D:\symbolslocal*http://msdl.microsoft.com/download/symbols

意思是先从D:\symbol_path中找符号文件;如果没找到,就去服务器中下载并保存到D:\symbolslocal目录中。

可以使用.sympath+ 命令来添加其他目录到搜索路径中,如.sympath+ D:\other_symbol_dir。 然后使用.reload来根据新的路径重新搜索并加载符号文件。

可执行文件路径

在菜单项 File -> Image File Path 中设置,此项是在加载 dump 时,设置可执行文件 exe、dll 的路径的。

源代码目录

在菜单项 File -> Source File Path 中设置源代码目录。如果当前指令指针在源代码范围内,就会自动跳出源文件窗口。源文件窗口中的操作和 vs 类似。如果没有跳出,可以单机菜单项 Open Source File 手动选择源文件

命令行启动

可以通过对 windbg 加入启动参数的方式来指定符号文件路径和源码路径等等。

1
start "" "%~dp0/Debuggers_x86/windbg.exe" -Q -y "D:\symbolslocal*http://msdl.microsoft.com/download/symbols" -srcpath "srv*C:\CodeCache"

内核调试常用命令行:

1
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k net:port=50010,key=jspq4rggw9ge.3ps1mwc6ye9fe.vm89wpoz72jb.1ldc14kg2ex39 -y "D:\symbolslocal*http://msdl.microsoft.com/download/symbols"

工作空间

Windbg 会保存每个你调试的工程的信息,这些信息包括调试项目的属性、参数、会话状态、调试器设置、及图形界面信息,类似于 ide 的项目文件。

每个调试的工作空间信息默认保存在
HKEY_CURRENT_USER\Software\Microsoft\Windbg\Workspaces中,在这个键下一般有 4 个子键 User、Kernel、Dump、Explicit, 他们分别保存用户态调试,内核态调试、转储文件调试、以及手动保存(Save Workspace As)的工作空间信息。

窗口界面介绍

名称 热键 用途
Command Atl+1 输入命令、显示命令结果和调试信息输出
Watch Atl+2 观察指令全局变量、局部变量和寄存器的信息
Locals Atl+3 自动显示当前函数的所有局部变量
Registers Atl+4 观察和修改寄存器的值
Memory Atl+5 观察和修改内存数据
Call Stack Atl+6 栈中记录的函数调用序列
Disassembly Atl+7 反汇编
Scratch Pad Atl+8 白板,可以用来做调试笔记等
Processes and Threads Atl+9 显示所有调试目标的列表,包括进程和线程等
Command Browser Ctrl+N 执行和浏览命令

常用调试方法

IFEO

在“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options”下新建一个以待调试程序名命名的子项,然后在这个子项中新建一个名为DebuggerREG_SZ类型的值,将值设置为 windbg 的全路径,比如:C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86\windbg.exe。

这样,再运行待调试的程序时,操作系统就会先启动 windbg,并把要调试程序的路径传递给他。

内核远程调试

首先,在被调试机器(如虚拟机)上使用如下命令配置开启调试模式:

1
2
bcdedit /debug on
bcdedit /dbgsettings net hostip:192.168.50.124 port:50010

其中,hostip 为运行 Windbg 的调试机器的 IP,需要确保在被调试机器上可以 ping 通该 IP。在运行完第二条命令之后,会生成一串 Key,记住该 Key。

然后,在被调试机器上使用如下命令行参数运行 Windbg(其中 port 需要与上面命令保持一直,key 为上面命令生成的 Key):

1
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k net:port=50010,key=jspq4rggw9ge.3ps1mwc6ye9fe.vm89wpoz72jb.1ldc14kg2ex39 -y "SRV*D:\symbolslocal*http://msdl.microsoft.com/download/symbols"

最后,保持 Windbg 开启,并重启被调试机器(如虚拟机),此时被调试机器会自动连接上 Windbg。

常用命令

windbg 命令分为标准命令元命令扩展命令
.开头的元命令,可以提供标准命令没有提供的功能,也内建在调试引擎中。
!开头的扩展命令,用于扩展某一方面的调试功能,实现在动态加载的扩展模块中。

Windbg 中默认数字为十六进制,可以使用0n前缀表示十进制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
tab键       自动完成命令
.hh 查看指定命令的帮助手册
.reload 重新加载符号文件
.restart 重新启动调试目标
qd 退出并与调试目标分离

p 单步执行,快捷键F10
g 运行程序,快捷键F5
t 单步步入,快捷键F11
gu 返回函数调用处,快捷键Shift+F11
pt 运行到函数返回处

bp $exentry 在程序入口点设置断点,$exentry是一个伪寄存器
bp 0x00401030 在地址0x00401030处设置断点
bp TestModule!TestFunc 在TestModule模块中的TestFunc函数处设置断点,前提是该模块符号已经加载
bp TestModule!TestClass::SetValue 在模块TestModule的TestClass类成员函数SetValue处设置断点
bp @@C++(TestModule!MyTestClass::SetValue) 与上面一样,语法不同,C++语法,上面的为MASM语法

bl 显示设置的断点列表
be 激活断点
bd 禁用断点,用法:bd 1表示禁用1号断点,bd *表示禁用所有断点
bc 删除断点,用法:bc 1表示删除1号断点,bc *表示删除所有断点
ba 设置内存访问断点,语法:ba [access] [size] addr,其中,access为r=读,w=写,e=执行;size取1,2或4。
如:ba r 1 0044108c 在内存0044108c的位置开始的下一个字节的读断点
ba w4@@C++(&i) 给变量i地址下4个字节的写断点


u 查看当前正要执行的代码

k 查看当前调用堆栈
~*kb 显示所有进程调用堆栈
.frame /r 0x13 切换到指定的栈帧,以便查看调用参数、变量,指定/r会显示寄存器的值

~ 查看调试进程中的线程信息
~. 当前线程信息
~# 致当前异常或调试事件的线程信息
~[Number]s 线程切换

!teb 线程环境块
!gle 显示当前线程的GLE

a 修改当前指令,输入修改的指令按Enter结束
s –a 00400000 L53000 “Wrong” 以ASCII码的形式从00400000处开始往后53000个字节搜索字符串“Wrong”

d 显示内存地址的值,语法:d{type} [/c#] addr [L#]
其中,type 按指定类型显示:
b= Byte
w = WORD
d = DWORD
q = QWORD
a = 按ASCII字符输出直到\0
u = 按Unicode字符输出直到\0
/c 每行显示数据数量
L 显示指定类型的数据的个数

?i 查看局部变量i的值,会以10进制和16进制同时显示
eb 0012ff78 'a' 'b' 从内存地址0012ff78开始依次写入后面的值

r 显示所有寄存器的值,语法:r [reg]
其中,reg除了支持常规寄存器,还支持伪寄存器: $exentry, $ip, $teb, $peb, $ra, $retreg, $csp, $tpid, $tid

dt [module!]Name [Field] [Address] 查看类型信息,如结构体。Address支持使用@前缀指定寄存器或伪寄存器
dt _EPROCESS 显示_EPROCESS结构体的完整成员结构
dt _EPROCESS InheritedFromUniqueProcessId 显示_EPROCESS结构体中指定成员的信息
dt _EPROCESS ffffe58c1ebaf080 显示指定地址的结构体的成员结构及个成员的值

!address xxxxxx 查看指定内存地址的信息
!pool xxxxxx 查看指定内存地址是否是非页内存

dv /t 查看当前作用域下局部变量的值,指定/t会显示变量类型

poi 指针解引用,poi(xxxx)

.ecxr 显示当前异常的上下文信息
!analyze -v 详细显示当前异常信息,常用于分析dmp文件

| 所有进程列表
|. 当前进程信息
|# 导致当前异常或调试事件的进程信息
|[Number]s 进程切换
!peb 进程环境块

!process 内核模式下,显示指定进程的信息,包括EPROCESS
!process 0 0 notepad.exe 显示notepad.exe进程的信息,第2个参数0表示显示最少的信息
!process 1a5c 0 显示进程ID伪0x1a5c的进程信息,第2个参数0表示显示最少的信息


!locks 查看进程中有些锁处于锁定状态
!cs -l 查看处于锁定状态的关键区
!handle 000000c0 f 查看句柄000000c0的信息

lm 查看已加载的模块信息,语法:lm [olfv],其中:
o: 列出已加载模块
l: 包含符合信息
f: 包含模块路径
v: 包含详细信息

lm a 0x41000 查看包含指定地址的模块
lm vm Test* 查看符合Test*名称的模块信息,v表示显示详细信息,m表示通配符模式

? 计算表达式的值
?? 计算C++表达式的值