1. 命令系统概述

1.1 命令分类

WinDbg的命令系统非常丰富,主要分为以下几类:

标准命令

  • 执行控制命令:g, t, p, gu等
  • 断点命令:bp, bl, bc等
  • 内存命令:d, e, s等
  • 寄存器命令:r, rm等
  • 符号命令:x, ln, uf等

元命令(Meta Commands)

以点号(.)开头的命令,用于控制调试器本身:

.help        # 显示帮助
.reload      # 重新加载符号
.sympath     # 设置符号路径
.logopen     # 打开日志文件

扩展命令(Extension Commands)

以感叹号(!)开头的命令,提供专门的分析功能:

!analyze     # 自动分析
!heap        # 堆分析
!handle      # 句柄分析
!process     # 进程分析

1.2 命令语法规则

基本语法

命令 [选项] [参数1] [参数2] ...

地址表示法

# 十六进制地址(默认)
0x401000
401000

# 符号地址
kernel32!CreateFileW
mymodule!MyFunction+0x10

# 寄存器间接寻址
@eax
@esp+4
poi(@esp)

数值表示法

# 十六进制(默认)
0x100
100

# 十进制
0n256
0t256

# 八进制
0o377

# 二进制
0y11111111

1.3 命令历史和编辑

命令历史

↑ / ↓        # 浏览命令历史
Ctrl+R       # 搜索命令历史
!history     # 显示命令历史

命令编辑

Ctrl+A       # 移到行首
Ctrl+E       # 移到行尾
Ctrl+K       # 删除到行尾
Ctrl+U       # 删除整行

2. 执行控制命令

2.1 基本执行控制

继续执行

g                    # 继续执行
g <地址>             # 执行到指定地址
g @eip+5            # 执行到当前指令后5字节

单步执行

t                    # 单步执行(进入函数调用)
p                    # 单步执行(跳过函数调用)
tt                   # 执行到下一个分支指令
pt                   # 跳过到下一个分支指令

高级执行控制

gu                   # 执行到当前函数返回
gu <地址>            # 执行到指定函数返回
pc                   # 执行到下一个call指令
tc                   # 执行到下一个call指令(进入)
ph                   # 执行到下一个分支指令
th                   # 执行到下一个分支指令(进入)

2.2 条件执行

条件断点

# 设置条件断点
bp kernel32!CreateFileW "j (@rcx != 0) 'g'; 'kb'"
# 如果rcx不为0则继续执行,否则显示堆栈

# 计数断点
bp /1 myfunction     # 断点只触发一次
bp /5 myfunction     # 断点触发5次后自动删除

条件执行示例

# 循环执行直到条件满足
.while (@eax < 0x100) { t; r eax }

# 条件分支执行
.if (@eax == 0) { .echo "EAX is zero" } .else { .echo "EAX is not zero" }

2.3 异常处理

异常控制

sxe av               # 在访问违例时中断
sxi av               # 忽略访问违例
sxd av               # 在访问违例时显示信息但不中断

# 查看异常设置
sx                   # 显示所有异常设置
sx av                # 显示访问违例设置

常见异常类型

av                   # 访问违例
bp                   # 断点异常
eh                   # C++异常
clr                  # .NET异常
ld                   # 模块加载
ud                   # 模块卸载

3. 断点管理

3.1 断点类型

软件断点

bp <地址>            # 设置软件断点
bp kernel32!CreateFileW
bp mymodule!MyFunction+0x10
bp 0x401000

硬件断点

ba e 1 <地址>        # 设置执行断点(1字节)
ba r 4 <地址>        # 设置读取断点(4字节)
ba w 4 <地址>        # 设置写入断点(4字节)
ba rw 8 <地址>       # 设置读写断点(8字节)

数据断点示例

# 监视变量修改
ba w 4 mymodule!g_variable

# 监视内存区域访问
ba rw 0x100 0x401000

# 监视堆栈变量
ba w 4 @esp+0x10

3.2 断点管理命令

断点列表和状态

bl                   # 列出所有断点
bl <断点号>          # 显示特定断点信息

断点控制

bc <断点号>          # 清除断点
bc *                 # 清除所有断点
bd <断点号>          # 禁用断点
be <断点号>          # 启用断点

断点修改

# 修改断点命令
bs <断点号> "命令"   # 设置断点触发时执行的命令

# 示例:断点触发时显示寄存器并继续
bs 0 "r; g"

3.3 高级断点技巧

符号断点

# 模块加载时设置断点
bu mymodule!MyFunction
# bu表示unresolved breakpoint,模块加载后自动解析

# 通配符断点
bm mymodule!My*      # 匹配所有以My开头的函数
bm *!CreateFile*     # 匹配所有模块中的CreateFile函数

条件断点高级用法

# 复杂条件断点
bp kernel32!CreateFileW ".if (poi(@esp+4) != 0) { .echo 'File:'; da poi(@esp+4); g } .else { kb }"

# 计数器断点
bp myfunction ".if (@$t0 < 5) { r $t0 = @$t0 + 1; g } .else { kb }"

4. 内存操作命令

4.1 内存显示命令

基本显示命令

d <地址>             # 显示内存(十六进制+ASCII)
db <地址>            # 显示字节
dw <地址>            # 显示字(2字节)
dd <地址>            # 显示双字(4字节)
dq <地址>            # 显示四字(8字节)
df <地址>            # 显示浮点数

字符串显示

da <地址>            # 显示ASCII字符串
du <地址>            # 显示Unicode字符串
dS <地址>            # 智能字符串显示

显示选项

# 指定显示长度
dd 401000 L10        # 显示16个双字
da 401000 L20        # 显示32个字符

# 指定结束地址
dd 401000 401040     # 显示从401000到401040的内容

4.2 内存修改命令

基本修改命令

e <地址> <值>        # 修改内存
eb 401000 90         # 修改字节为0x90
ew 401000 1234       # 修改字为0x1234
ed 401000 12345678   # 修改双字为0x12345678
eq 401000 123456789abcdef0  # 修改四字

字符串修改

ea 401000 "Hello"    # 修改为ASCII字符串
eu 401000 "Hello"    # 修改为Unicode字符串

批量修改

# 填充内存
f 401000 L100 90     # 用0x90填充256字节
f 401000 401100 cc   # 用0xcc填充指定范围

4.3 内存搜索命令

搜索字节模式

s -b 401000 L1000 90 90 90    # 搜索字节序列
s -w 401000 L1000 1234        # 搜索字值
s -d 401000 L1000 12345678    # 搜索双字值

搜索字符串

s -a 401000 L1000 "Hello"     # 搜索ASCII字符串
s -u 401000 L1000 "Hello"     # 搜索Unicode字符串

高级搜索

# 搜索指针
s -q 401000 L1000 kernel32!CreateFileW

# 搜索模式
s -b 0 L?80000000 ff 25 ?? ?? ?? ??  # 搜索JMP指令模式

5. 寄存器操作

5.1 寄存器显示

基本寄存器显示

r                    # 显示所有通用寄存器
rm                   # 显示寄存器掩码格式
rf                   # 显示浮点寄存器
rx                   # 显示SSE寄存器

特定寄存器显示

r eax                # 显示EAX寄存器
r eax,ebx,ecx        # 显示多个寄存器
r @eax               # 显示EAX的值(作为地址)

标志寄存器

r efl                # 显示标志寄存器
.formats @efl        # 显示标志寄存器的各种格式

5.2 寄存器修改

基本修改

r eax = 12345678     # 设置EAX为指定值
r eip = 401000       # 修改指令指针
r esp = @esp + 4     # 修改堆栈指针

标志位修改

r efl = @efl | 0x40  # 设置零标志位
r efl = @efl & ~0x1  # 清除进位标志位

5.3 伪寄存器

WinDbg提供了一些伪寄存器用于特殊用途:

自动伪寄存器

@$ip                 # 当前指令指针
@$sp                 # 当前堆栈指针
@$bp                 # 当前基址指针
@$ra                 # 返回地址

用户定义伪寄存器

r $t0 = 12345        # 设置临时寄存器$t0
r $t1 = @eax + @ebx  # 计算并存储到$t1

# 在脚本中使用
.for (r $t0 = 0; @$t0 < 10; r $t0 = @$t0 + 1) { .echo @$t0 }

6. 符号和模块操作

6.1 符号查找

符号搜索

x *!CreateFile*      # 搜索所有模块中的CreateFile符号
x kernel32!*         # 列出kernel32模块的所有符号
x mymodule!My*       # 搜索mymodule中以My开头的符号

地址到符号转换

ln 401000            # 查找地址对应的最近符号
ln @eip              # 查找当前指令的符号

符号到地址转换

? kernel32!CreateFileW    # 显示符号地址
dd kernel32!CreateFileW   # 显示符号处的内存

6.2 模块信息

模块列表

lm                   # 列出所有加载的模块
lm o                 # 按加载顺序列出模块
lm v                 # 详细模块信息
lm m kernel32        # 显示特定模块信息

模块详细信息

!lmi kernel32        # 显示模块详细信息
!dh kernel32         # 显示模块头信息
!imports kernel32    # 显示导入表
!exports kernel32    # 显示导出表

6.3 函数分析

反汇编函数

uf kernel32!CreateFileW   # 反汇编整个函数
uf /c kernel32!CreateFileW # 显示函数调用
uf /i kernel32!CreateFileW # 显示指令地址

函数参数分析

!analyze -f          # 分析当前函数
.fnent @eip          # 显示函数入口信息
.fnret               # 显示函数返回信息

7. 调用堆栈分析

7.1 堆栈显示

基本堆栈命令

k                    # 显示调用堆栈
kb                   # 显示堆栈和参数
kp                   # 显示堆栈和完整参数
kv                   # 显示详细堆栈信息

堆栈选项

k 10                 # 显示10层堆栈
kb L20               # 显示20层堆栈和参数
kn                   # 显示带编号的堆栈

7.2 堆栈帧操作

切换堆栈帧

.frame 0             # 切换到第0帧
.frame 2             # 切换到第2帧
.frame /r 1          # 切换到第1帧并显示寄存器

堆栈帧信息

.frame               # 显示当前帧信息
dv                   # 显示当前帧的局部变量
dt                   # 显示类型信息

7.3 多线程堆栈

线程切换

~                    # 显示所有线程
~0s                  # 切换到线程0
~*k                  # 显示所有线程的堆栈
~*kb                 # 显示所有线程的详细堆栈

线程状态

!runaway             # 显示线程CPU使用情况
!locks               # 显示锁信息
!cs                  # 显示临界区信息

8. 调试技巧和最佳实践

8.1 高效调试技巧

命令组合

# 设置断点并显示上下文
bp kernel32!CreateFileW "kv; da @rcx; g"

# 循环监视变量
.while (1) { dd mymodule!g_counter L1; .sleep 1000 }

# 条件日志
bp myfunction ".if (@eax > 100) { .logopen c:\debug.log; .echo 'Large value:'; r eax; .logclose }"

脚本自动化

# 创建调试脚本文件 debug.wds
.echo "Starting debug session"
.sympath srv*c:\symbols*https://msdl.microsoft.com/download/symbols
.reload
bp kernel32!CreateFileW
g

# 执行脚本
$$>< c:\debug.wds

8.2 性能调试技巧

性能计数器

!perfctr             # 显示性能计数器
.time                # 显示时间信息
!runaway 7           # 显示详细的线程时间信息

内存使用分析

!heap -s             # 堆统计信息
!heap -stat -h 0     # 详细堆统计
!address             # 虚拟内存使用情况
!vm                  # 虚拟内存统计

8.3 错误诊断技巧

自动分析

!analyze -v          # 详细自动分析
!analyze -hang       # 分析挂起问题
!analyze -f          # 分析当前函数

异常分析

.exr -1              # 显示最后一个异常记录
.cxr                 # 显示上下文记录
!cppexr              # 分析C++异常

8.4 调试会话管理

会话保存和恢复

.dump /ma c:\crash.dmp    # 创建完整内存转储
.opendump c:\crash.dmp    # 打开转储文件
.writemem c:\mem.bin 401000 401100  # 保存内存到文件

日志管理

.logopen c:\debug.log     # 打开日志文件
.logappend c:\debug.log   # 追加到日志文件
.logclose                 # 关闭日志文件

9. 常见问题和解决方案

9.1 符号问题

符号加载失败

# 检查符号路径
.sympath

# 强制重新加载符号
.reload /f

# 详细符号加载信息
!sym noisy
.reload

符号不匹配

# 检查模块版本
lm v m kernel32

# 验证符号
!chksym kernel32

# 手动加载符号
.reload /i kernel32

9.2 调试器连接问题

附加进程失败

# 检查进程权限
!process 0 0 notepad.exe

# 使用管理员权限
# 重新启动WinDbg as Administrator

# 检查防病毒软件干扰

内核调试连接问题

# 检查调试设置
bcdedit /debug on
bcdedit /dbgsettings

# 验证连接
.reboot

9.3 性能问题

调试器响应慢

# 禁用不必要的符号加载
.symopt +0x40

# 限制输出长度
.prefer_dml 0

# 使用本地符号缓存
.sympath cache*c:\symcache;srv*https://msdl.microsoft.com/download/symbols

内存使用过高

# 清理符号缓存
.reload /u

# 限制历史记录
!history -c

# 重启调试会话

小结

本章详细介绍了WinDbg的基本命令和调试技巧:

  1. 命令系统:掌握了标准命令、元命令和扩展命令的使用
  2. 执行控制:学习了程序执行控制和异常处理
  3. 断点管理:了解了各种断点类型和高级断点技巧
  4. 内存操作:掌握了内存显示、修改和搜索命令
  5. 寄存器操作:学习了寄存器查看和修改技巧
  6. 符号分析:了解了符号查找和模块分析方法
  7. 堆栈分析:掌握了调用堆栈和多线程调试
  8. 调试技巧:学习了高效调试和问题诊断方法

下一章将学习WinDbg的高级调试功能和专项分析技术。