4. WinDbg 基础命令:!analyze -v、kb、lm、dds、!thread、!process 命令详解

在蓝屏分析中,WinDbg 是核心工具。掌握其基础命令是定位驱动崩溃根源的必备技能。本章将深入解析六个最常用的命令:!analyze -v、kb、lm、dds、!thread 和 !process。这些命令构成了从“发生了什么”到“谁导致的”再到“为什么”的完整分析链路。

核心原则: 蓝屏分析不是随机猜测,而是遵循“先全局、后局部、再细节”的流程。本章的命令正是按照这个逻辑组织的。

4.1 !analyze -v:自动化的第一现场

!analyze -v 是 WinDbg 的“一键诊断”命令。它自动解析崩溃转储文件,提取关键信息并给出初步分析结论。这是每次分析的起点。

命令输出核心字段解读

字段 含义 分析价值
BugCheck Analysis 显示 BugCheck 代码(如 0x000000d1)及其名称(如 DRIVER_IRQL_NOT_LESS_OR_EQUAL) 快速确定崩溃类型,缩小排查范围
Arg1 / Arg2 / Arg3 / Arg4 BugCheck 的四个参数,含义因代码而异 提供崩溃时的内存地址、IRQL 级别、进程上下文等关键线索
PROCESS_NAME 崩溃发生时正在运行的进程名称(如 notepad.exe) 指示哪个用户进程触发了驱动代码执行
MODULE_NAME WinDbg 初步判定的故障模块名称(如 usbhub.sys) 直接指向可疑驱动,但需人工验证
IMAGE_NAME 故障模块的镜像文件名 与 MODULE_NAME 配合,确认驱动身份
STACK_TEXT 崩溃时的完整调用堆栈 最关键的线索,展示函数调用链,定位崩溃点
实战技巧: 不要只看 MODULE_NAME。有时 WinDbg 会误判。必须结合 STACK_TEXT 和 Arg1 等参数交叉验证,才能锁定真正的元凶。

典型输出示例

1: kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.
...
Arg1: 00000000`7a8b9c10, memory referenced
Arg2: 00000000`00000002, IRQL
Arg3: 00000000`00000000, bitfield :
    bit 0 : value 0 = read operation, 1 = write operation
    bit 3 : value 0 = not an execute operation
Arg4: fffff880`0f4a3b80, address which referenced memory
...
PROCESS_NAME:  chrome.exe
MODULE_NAME:   mydrv
IMAGE_NAME:    mydrv.sys
...
STACK_TEXT:
fffff880`0f4a3b80 mydrv+0x3b80
fffff880`0f4a3c00 nt!IofCallDriver+0x55
fffff880`0f4a3c30 nt!IoPageRead+0x1a1
...

4.2 kb:查看调用堆栈

kb 命令显示当前线程的调用堆栈,并附带每个函数调用的参数。它是理解代码执行路径的“地图”。

命令格式与输出解读

0: kd> kb
# Child-SP          RetAddr           Call Site
00 fffff880`0f4a3b80 fffff880`0f4a3c00 mydrv+0x3b80
01 fffff880`0f4a3c00 fffff800`0f4a3c30 nt!IofCallDriver+0x55
02 fffff880`0f4a3c30 fffff800`0f4a3c60 nt!IoPageRead+0x1a1
03 fffff880`0f4a3c60 fffff800`0f4a3c90 nt!MiIssueHardFault+0x1e1
04 fffff880`0f4a3c90 fffff800`0f4a3cc0 nt!MmAccessFault+0x1a1
05 fffff880`0f4a3cc0 fffff800`0f4a3cf0 nt!KiPageFault+0x1e1
06 fffff880`0f4a3cf0 00000000`7a8b9c10 0x0
列名 含义 分析价值
# 堆栈帧编号(0 为当前执行点) 越靠近 0,越接近崩溃现场
Child-SP 子函数的栈指针 用于手动遍历堆栈
RetAddr 返回地址(调用者中的下一条指令) 帮助理解函数调用关系
Call Site 当前帧所在的函数或模块+偏移 直接显示崩溃发生在哪个函数或模块中
常见误区: 堆栈顶部的 mydrv+0x3b80 并不一定是驱动 mydrv.sys 的 bug。它可能只是恰好在这个地址崩溃,而真正的错误原因可能是上层调用者传递了错误参数。务必向上查看 nt!IofCallDriver 等内核函数。

4.3 lm:列出已加载模块

lm 命令列出当前系统或转储文件中加载的所有内核模块(驱动)。它是识别驱动版本、基址和路径的“户口本”。

常用变体

  • lm:列出所有模块,显示基址、大小、名称。
  • lm v:详细模式,显示公司名、版本、时间戳等。
  • lm D mydrv:仅显示名为 mydrv 的模块信息。
  • lm k:仅列出内核模块(驱动)。

输出示例

0: kd> lm k
start             end                 module name
fffff800`0f4a0000 fffff800`0f4a4000   mydrv    (deferred)
fffff800`0f4a4000 fffff800`0f4a8000   usbhub   (deferred)
fffff800`0f4a8000 fffff800`0f4ac000   nt       (pdb symbols)
关键用途: 当 !analyze -v 指向 MODULE_NAME: mydrv 时,使用 lm D mydrv 查看其完整路径、版本和公司信息,确认是否为第三方驱动,并记录版本号用于后续排查或更新。

4.4 dds:显示内存中的数据结构

dds 命令用于显示指定内存地址处的数据,并将其解释为符号(函数名、全局变量等)。它是手动分析堆栈、对象和链表的“显微镜”。

命令格式

dds [地址] [长度]

典型应用场景

  • 分析堆栈参数: 使用 dds [Child-SP] 查看堆栈帧中的参数值。
  • 查看对象: 使用 dds [对象地址] 查看对象的方法表(vtable)或成员变量。
  • 验证链表: 使用 dds [链表头地址] 遍历链表节点。

输出示例

0: kd> dds fffff880`0f4a3b80
fffff880`0f4a3b80  fffff880`0f4a3c00 mydrv+0x3b80
fffff880`0f4a3b88  fffff800`0f4a3c30 nt!IofCallDriver+0x55
fffff880`0f4a3b90  00000000`00000000
fffff880`0f4a3b98  00000000`7a8b9c10
进阶用法: 在分析 DRIVER_IRQL_NOT_LESS_OR_EQUAL 时,Arg1 是访问失败的内存地址。使用 dds Arg1 查看该地址附近的数据,判断它是否属于某个驱动的内存池或设备扩展。

4.5 !thread:查看线程信息

!thread 命令显示指定线程的详细信息,包括线程环境块(TEB)、栈基址、优先级、等待状态等。它是分析“谁在干活”和“谁在等待”的关键。

命令格式

!thread [线程地址]

输出核心字段

字段 含义 分析价值
THREAD 线程对象地址 用于后续引用
ThreadId 线程 ID 关联到进程中的具体线程
Stack 线程栈的基址和限制 判断栈是否溢出
Priority 当前优先级 判断是否为实时线程
WaitTime 等待时间(秒) 判断线程是否卡死
State 线程状态(Running, Waiting, Ready 等) 判断线程是否活跃
注意: 蓝屏时,崩溃线程通常是当前线程。使用 !thread 查看其状态,确认它是否在等待某个资源(如锁、IRP)时发生了异常。

4.6 !process:查看进程信息

!process 命令显示指定进程的详细信息,包括进程环境块(PEB)、句柄表、虚拟地址空间布局、加载的模块列表等。它是分析“在哪个进程上下文中崩溃”的核心命令。

命令格式

!process [进程地址] 0x1f

参数 0x1f 表示显示所有详细信息(包括线程、模块、句柄等)。

输出核心字段

字段 含义 分析价值
PROCESS 进程对象地址 用于后续引用
ProcessId 进程 ID 关联到任务管理器中的进程
SessionId 会话 ID 判断是否为系统进程或用户进程
ImageFileName 进程镜像名称 与 PROCESS_NAME 对应
ThreadCount 线程数量 判断进程是否异常
HandleCount 句柄数量 判断是否存在句柄泄漏
实战场景: 当 !analyze -v 显示 PROCESS_NAME: chrome.exe 时,使用 !process 0xfffffa8001234567 0x1f 查看其加载的模块列表,确认是否有第三方驱动注入或挂钩。

4.7 命令协同分析流程

下图展示了从崩溃转储到定位驱动根源的完整命令协同流程:

WinDbg 命令协同分析流程 !analyze -v 获取 BugCheck 代码、参数、初步结论 kb 查看崩溃线程的调用堆栈 lm 列出所有加载模块,确认驱动版本 dds 分析堆栈参数和内存数据结构 !thread / !process 确认线程状态和进程上下文 定位驱动崩溃根源 分析原则 1. 先全局后局部 2. 先自动后手动 3. 交叉验证结论 4. 不轻信单一命令 常见陷阱 • MODULE_NAME 可能误判 • 堆栈顶部不一定是元凶 • 忽略进程上下文 • 忘记验证驱动版本

4.8 命令对比速查表

命令 核心用途 输出重点 分析阶段
!analyze -v 自动诊断,获取初步结论 BugCheck 代码、参数、MODULE_NAME、STACK_TEXT 第一阶段:全局概览
kb 查看当前线程调用堆栈 函数调用链、返回地址、参数 第二阶段:定位崩溃点
lm 列出所有加载模块 模块基址、大小、名称、版本 第二阶段:确认驱动身份
dds 显示内存数据并解析为符号 堆栈参数、对象方法表、链表节点 第三阶段:深入分析
!thread 查看线程详细信息 线程状态、栈信息、优先级、等待时间 第三阶段:分析线程上下文
!process 查看进程详细信息 进程 ID、镜像名、线程数、模块列表 第三阶段:分析进程上下文