菜鸟科技网

windbg dump命令如何高效分析内存转储?

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

windbg dump命令如何高效分析内存转储?-图1
(图片来源网络,侵删)

核心概念:什么是 Dump?

在调试的上下文中,"dump" 指的是将内存中某个特定区域的内容以可读的格式显示出来,WinDbg 不会改变目标进程或内存的实际内容,它只是“读取”并“展示”给你看。


基本语法

dump 命令的基本语法如下:

dump [options] [address]
  • options: 可选参数,用于控制输出格式和内容。
  • address: 必需参数,指定要转储的内存地址,这个地址可以是:
    • 一个直接的十六进制地址(0x00000000)。
    • 一个变量名或符号名(myVariablentdll!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 会从这个新地址开始转储字符串。

    windbg dump命令如何高效分析内存转储?-图2
    (图片来源网络,侵删)

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
  • 用途: 一个方便的命令,无需关心目标程序是 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): 强制转储,即使地址看起来无效或未对齐。

    windbg dump命令如何高效分析内存转储?-图3
    (图片来源网络,侵删)
  • /i (Iterate): 用于转储数组或链表,它会重复调用一个函数(通常是获取下一个元素的函数)来遍历数据结构,这在分析 Windows 内核链表(如 LIST_ENTRY)时非常有用。

  • /n (No Symbols): 不解析符号,只显示原始的十六进制数据,当你怀疑符号文件(PDB)有问题时,这个选项很有用。

  • /w (Wide): 在 dt 命令中,显示 Unicode 字符串字段时,使用更宽的列格式。

  • /z (Size): 在 dt 命令中,显示结构体中每个字段的大小(以字节为单位)。


实战示例

假设我们有一个程序崩溃了,我们想分析原因。

  1. 加载 Dump 文件:

    .loadby sos clr
    // 如果是内核调试
    .loadby sos mscordacwks
  2. 查看线程栈:

    !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])
    ...
  3. 分析结构体: 从上面的栈中,我们看到在 MyClass::MyMethod 的偏移 0x30 处有一个参数,假设我们知道这是一个 MyStruct 指针。

    dt MyProject!MyStruct poi(rip+0x30)

    这会显示 MyStruct 的具体内容,帮助我们检查数据是否正确。

  4. 检查字符串: 假设 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 根据程序位数自动选择 dddq
dps Dump Stack w/ Symbols 查看调用栈和符号信息 (分析崩溃关键)
dt Dump Type 根据结构体/类定义格式化显示内存 (分析数据结构神器)

掌握 dump 系列命令是成为 WinDbg 高手的第一步,建议你多加练习,尝试在自己的程序中设置断点,然后用这些命令来观察变量和内存的变化。

分享:
扫描分享到社交APP
上一篇
下一篇