dump 命令是 WinDbg 中最核心、最常用的命令之一,用于转储和检查内存中的数据,它功能非常强大,可以用来查看变量、对象、线程栈、内存页等各种信息。

核心概念:什么是 Dump?
在调试的上下文中,"dump" 指的是将内存中某个特定区域的内容以可读的格式显示出来,WinDbg 不会改变目标进程或内存的实际内容,它只是“读取”并“展示”给你看。
基本语法
dump 命令的基本语法如下:
dump [options] [address]
options: 可选参数,用于控制输出格式和内容。address: 必需参数,指定要转储的内存地址,这个地址可以是:- 一个直接的十六进制地址(
0x00000000)。 - 一个变量名或符号名(
myVariable或ntdll!RtlAllocateHeap)。 - 一个表达式(
myStruct + 8)。
- 一个直接的十六进制地址(
最常用的 dump 命令及其用途
dump 命令有很多子命令,但最常用的是以下几种,我们通常会省略 dump 关键字,直接使用子命令,da 代替 dump da。
da (Dump ASCII Strings)
- 功能: 从指定地址开始,以 ASCII 格式转储以空字符 (
\0) 结尾的字符串。 - 用途: 快速查看 C 风格的字符串,如文件名、路径、消息等。
- 示例:
da poi(pMyStringPointer)解释:
poi()是 "Pointer at address" 的缩写,它会获取pMyStringPointer地址处存放的指针值,da会从这个新地址开始转储字符串。
(图片来源网络,侵删)
du (Dump Unicode Strings)
- 功能: 从指定地址开始,以 Unicode (UTF-16) 格式转储以空字符结尾的字符串。
- 用途: 这是 Windows 调试中最常用的字符串转储命令,因为 Windows 内部广泛使用 Unicode 字符串。
- 示例:
du 0x00007ff8`5a1b2a10 du g_szWindowTitle
dc (Dump Constant Bytes)
- 功能: 以十六进制和 ASCII 字符的形式转储内存内容,这是查看原始内存布局的“瑞士军刀”。
- 格式:
dc [address] [length] - 输出格式:
地址: 十六进制数据 ASCII字符 - 示例:
dc 0x00007ff8`5a1b2a10 20这会从
0x...2a10地址开始,转储 20 个字节的数据。
dd (Dump Double Words)
- 功能: 以 32 位双字 (DWORD) 的形式转储内存内容。
- 用途: 在 32 位程序或查看 32 位数据结构时非常有用。
- 示例:
dd 0x00000001`23456780
dq (Dump Quad Words)
- 功能: 以 64 位四字 (QWORD) 的形式转储内存内容。
- 用途: 在 64 位程序中这是最常用的数据查看命令,因为指针和大部分数据类型都是 64 位的。
- 示例:
dq 0x00007ff8`5a1b2a10
dp (Dump Pointer-Sized Words)
- 功能: 根据当前目标程序的位数(32位或64位),以指针大小的单位转储内存。
- 在 64 位程序中,
dp等同于dq。 - 在 32 位程序中,
dp等同于dd。
- 在 64 位程序中,
- 用途: 一个方便的命令,无需关心目标程序是 32 位还是 64 位,它总能正确显示指针大小的数据。
- 示例:
dp g_pMyObject
dps (Dump Stack with Symbols)
- 功能: 显示当前线程的调用栈,并尝试解析符号,这是分析崩溃或函数调用流程的关键命令。
- 用途:
- 查看函数调用链。
- 分析崩溃时的堆栈回溯。
- 查看函数参数和局部变量(如果开启了符号)。
- 示例:
dps dps L0x20 // 显示当前栈指针上下 0x20 字节范围内的栈内容
dt (Dump Type / Display Type)
- 功能: 这是最强大的命令之一,它根据一个结构体或类的定义来格式化转储内存内容。
- 用途: 查看复杂的 C/C++ 结构体、Windows 内核对象、COM 对象等。
- 语法:
dt [module!]TypeName [address] [fields] - 示例:
- 查看一个
CONTEXT结构体(保存CPU寄存器状态):dt ntdll!CONTEXT 0x00007ff8`5a1b2a10 - 查看一个自定义的结构体
MyStruct:dt MyProject!MyStruct 0x00007ff8`5a1b2a10 - 只查看结构体的特定字段:
dt ntdll!PEB 0x00007ff8`5a1b2a10 ImageBaseAddress
- 查看一个
高级选项和技巧
dump 命令支持一些通用选项,可以改变其行为。
-
/c(Count): 指定要转储的单位数量。dc 0x... /c 10: 转储 10 个字节。dq 0x... /c 4: 转储 4 个 64 位整数。
-
/f(Force): 强制转储,即使地址看起来无效或未对齐。
(图片来源网络,侵删) -
/i(Iterate): 用于转储数组或链表,它会重复调用一个函数(通常是获取下一个元素的函数)来遍历数据结构,这在分析 Windows 内核链表(如LIST_ENTRY)时非常有用。 -
/n(No Symbols): 不解析符号,只显示原始的十六进制数据,当你怀疑符号文件(PDB)有问题时,这个选项很有用。 -
/w(Wide): 在dt命令中,显示 Unicode 字符串字段时,使用更宽的列格式。 -
/z(Size): 在dt命令中,显示结构体中每个字段的大小(以字节为单位)。
实战示例
假设我们有一个程序崩溃了,我们想分析原因。
-
加载 Dump 文件:
.loadby sos clr // 如果是内核调试 .loadby sos mscordacwks -
查看线程栈:
!clrstack // 然后切换到崩溃的线程 ~0s dps输出会显示函数调用栈,
0:000> dps 00000000`00000000 : 00007ff8`5a1b2a10 : 0x00007ff8`5a1b2a10 (clr!JITutil_DomainLocalLock::TryEnter+0x5 [d:\...\clr\src\vm\jitutil.cpp @ 215]) 00000000`00000018 : 00007ff8`5a1b2a20 : 0x00007ff8`5a1b2a20 (MyProject!MyClass::MyMethod+0x30 [c:\...\myclass.cpp @ 50]) 00000000`00000030 : 00007ff8`5a1b2a30 : 0x00007ff8`5a1b2a30 (MyProject!main+0x50 [c:\...\main.cpp @ 20]) ... -
分析结构体: 从上面的栈中,我们看到在
MyClass::MyMethod的偏移0x30处有一个参数,假设我们知道这是一个MyStruct指针。dt MyProject!MyStruct poi(rip+0x30)这会显示
MyStruct的具体内容,帮助我们检查数据是否正确。 -
检查字符串: 假设
MyStruct中有一个文件名字符串指针。du poi(0x00007ff8`5a1b2a10 + 0x20)
| 命令 | 全称 | 主要用途 |
|---|---|---|
da |
Dump ASCII | 查看以 \0 结尾的 ASCII 字符串 |
du |
Dump Unicode | 查看以 \0 结尾的 Unicode 字符串 (最常用) |
dc |
Dump Constant | 查看原始字节,以十六进制和 ASCII 形式显示 |
dd |
Dump Double Words | 查看以 DWORD (32位) 为单位的数据 |
dq |
Dump Quad Words | 查看以 QWORD (64位) 为单位的数据 (64位调试最常用) |
dp |
Dump Pointer-sized | 根据程序位数自动选择 dd 或 dq |
dps |
Dump Stack w/ Symbols | 查看调用栈和符号信息 (分析崩溃关键) |
dt |
Dump Type | 根据结构体/类定义格式化显示内存 (分析数据结构神器) |
掌握 dump 系列命令是成为 WinDbg 高手的第一步,建议你多加练习,尝试在自己的程序中设置断点,然后用这些命令来观察变量和内存的变化。
