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来根据新的路径重新搜索并加载符号文件。

命令行启动

可以通过对 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 的项目文件。<原文出自: jiangxueqiao.com,请尊重原创>

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

常用调试方法

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 命令分为标准命令元命令扩展命令<原文出自: jiangxueqiao.com,请尊重原创>
元命令以.开头,可以提供标准命令没有提供的功能,也内建在调试引擎中。
扩展命令以!开头,用于扩展某一方面的调试功能,实现在动态加载的扩展模块中。

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
tab键       自动完成命令
.hh 查看指定命令的帮助手册
.reload 重新加载符号文件
.restart 重新启动调试目标
qd 退出并与调试目标分离

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

=========== 模块 ===========

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

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

=========== 断点 ===========

快捷键F9,打开源文件,选中对应行,使用快捷键即可设置断点,亦可使用下面方式:

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个字节的写断点

=========== 线程/堆栈 ===========
~ 显示调试进程中的线程信息
~. 显示当前线程信息
~# 显示触发当前异常或调试事件的线程信息
~[Number]s 切换线程
~0s 切换到0号线程,对于绝大多数GUI程序来说,0号线程就是UI线程

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

k[b|v] [n] 查看当前线程的调用堆栈,kb显示前3个参数,kv显示FPO信息,n指定显示的帧的数量
~*kb [n] 显示所有进程调用堆栈
.frame [/r] 0x13 切换到指定的栈帧,以便查看调用参数、变量,指定 /r 会显示寄存器的值
使用鼠标点击栈帧前索引也可以切换栈帧

=========== 进程 ===========

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


!process 内核模式下,显示指定进程的信息,包括EPROCESS
!process 1a5c 0 显示PID为0x1a5c的进程信息,第2个参数0表示显示最少的信息
!process 0 0 notepad.exe 显示notepad.exe进程的信息,第1个参数0表示不使用PID匹配,第2个参数0表示显示最少的信息

=========== 内存及寄存器操作/反汇编 ===========

u 查看当前正要执行的代码
uf funName 显示整个函数的反汇编
a 修改当前指令,输入修改的指令按Enter结束

!address xxxxxx 查看指定内存地址的信息
s –a 00400000 L53000 “Wrong” 搜索内存,如以ASCII码的形式从00400000处开始往后53000个字节搜索字符串“Wrong”
eb 0012ff78 'a' 'b' 写入内存,如从内存地址0012ff78开始依次写入后面的值

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

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


dt [module!]Name [Field] [Address] 显示类型信息,如结构体。Address支持使用@前缀指定寄存器或伪寄存器;
Name支持通配符;
使用-r[1~9]递归显示子类型;
dt _EPROCESS 显示_EPROCESS结构体的完整成员结构
dt _EPROCESS InheritedFromUniqueProcessId 显示_EPROCESS结构体中指定成员的信息
dt _EPROCESS ffffe58c1ebaf080 显示指定地址的结构体的成员结构及个成员的值

r [reg] 显示所有寄存器的值
其中,reg 除了支持常规寄存器,还支持伪寄存器: $exentry, $ip, $teb, $peb, $ra, $retreg, $csp, $tpid, $tid
$t0, $t1, ..., $t19是可以自定义的20个伪寄存器

r eax=1 修改寄存器的值

poi 指针解引用,poi(xxxx)

=========== 异常分析 ===========

.ecxr 显示当前异常的上下文信息
!analyze -v 详细显示当前异常信息,常用于分析dmp文件
!analyze -show <code> 查看蓝屏BUGCHECK_CODE的含义

=========== 句柄与锁 ===========

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

=========== 内核模式 ===========

!pool xxxxxx 内核模式下,查看指定内存地址是否是非页内存
!irql 内核模式下,查看触发中断前,CPU的中断请求级别

=========== 辅助计算 ===========

? 计算表达式的值
?i 查看局部变量i的地址,会以10进制和16进制同时显示
?? 计算C++表达式的值