2. BugCheck 代码解析:参数含义与常见代码详解
BugCheck 代码(也称 Stop Code)是 Windows 在检测到无法恢复的系统错误时,向调试器或转储文件输出的核心标识。每个 BugCheck 代码对应一类特定的崩溃原因,而代码后的 4 个参数 则提供了故障现场的关键上下文。本章将系统讲解 BugCheck 参数的通用解读方法,并深入剖析 4 个最常见的崩溃代码:0x1A、0x50、0xD1、0x7F。
核心原则: 解读 BugCheck 时,先看代码确定故障大类,再看参数 1 定位具体子类型,最后结合参数 2/3/4 分析内存地址、IRQL 或驱动模块。
2.1 BugCheck 参数通用结构
所有 BugCheck 在蓝屏或转储中均以固定格式呈现:
*** STOP: 0xXXXXXXXX (0xYYYYYYYY, 0xYYYYYYYY, 0xYYYYYYYY, 0xYYYYYYYY)
*** BugCheck 代码 参数 1 参数 2 参数 3 参数 4
四个参数的含义因 BugCheck 代码而异,但存在通用解读规律:
| 参数位置 | 通用含义 | 常见指向 |
|---|---|---|
| 参数 1 | 子类型 / 主要错误标志 | 内存操作类型、IRQL 值、异常记录 |
| 参数 2 | 目标地址 / 对象指针 | 引发错误的内存地址、驱动对象、设备扩展 |
| 参数 3 | 附加信息 / 调用上下文 | 页表项、访问类型、异常模块基址 |
| 参数 4 | 数据 / 指令指针 | 触发崩溃的指令地址、数据内容 |
调试技巧: 在 WinDbg 中使用
!analyze -v 命令后,系统会自动解析参数含义并给出 BUGCHECK_STR 和 DEFAULT_BUCKET_ID,这是快速定位驱动模块的捷径。
2.2 BugCheck 0x1A:MEMORY_MANAGEMENT
0x1A 是内存管理子系统检测到内部状态不一致时触发的崩溃。它通常与 页面错误处理、工作集管理 或 内存描述符列表 (MDL) 损坏有关。
参数 1 子类型详解
| 参数 1 值 | 子类型含义 | 典型触发场景 |
|---|---|---|
0x00000001 |
进程工作集列表损坏 | 驱动错误修改了进程的 VAD 树 |
0x00000002 |
页表项 (PTE) 格式错误 | 驱动直接操作硬件页表导致不一致 |
0x00000003 |
MDL 映射冲突 | 驱动释放 MDL 后仍继续访问内存 |
0x00000004 |
工作集索引越界 | 内存碎片或驱动越界写入 |
0x00000005 |
页面文件操作异常 | 磁盘 I/O 错误或卷影副本损坏 |
实战要点: 0x1A 崩溃中,参数 2 通常指向 损坏的页表项或 VAD 节点。使用
!pte 参数2 可查看页表内容,使用 !vad 参数2 可检查虚拟地址描述符。若参数 2 指向非分页池地址,优先排查 内存管理驱动 (dxgkrnl.sys、rdbss.sys) 或 第三方过滤驱动。
2.3 BugCheck 0x50:PAGE_FAULT_IN_NONPAGED_AREA
0x50 是 Windows 蓝屏中出现频率最高的代码之一。它表示系统试图访问一个 应该在内存中但实际无效的页面,且该访问发生在不允许页面错误的 IRQL 级别(通常 ≥ DISPATCH_LEVEL)。
参数含义与解读
| 参数 | 含义 | 解读方法 |
|---|---|---|
| 参数 1 | 引发错误的内存地址 | 使用 !address 参数1 查看内存区域类型 |
| 参数 2 | 访问类型 (0=读, 1=写, 2=执行) | 写操作 (1) 通常指向缓冲区溢出 |
| 参数 3 | 触发访问的指令地址 | 使用 ln 参数3 定位所属模块 |
| 参数 4 | 页表项内容 (如有) | 若为 0,表示该地址完全未映射 |
快速定位驱动: 在 WinDbg 中执行
!analyze -v 后,查看 IMAGE_NAME 字段。若显示 ntkrnlmp.exe,则需进一步使用 kb 查看调用栈,找到最靠近底层的 第三方驱动模块。常见肇事者:nvlddmkm.sys、rt640x64.sys、e1i65x64.sys。
2.4 BugCheck 0xD1:DRIVER_IRQL_NOT_LESS_OR_EQUAL
0xD1 是驱动开发人员最熟悉的崩溃代码。它表示一个驱动在 过高的 IRQL 级别 上访问了 可分页内存,或者访问了 完全无效的内存地址。与 0x50 的核心区别在于:0xD1 明确指出了 肇事驱动 的模块名。
参数 1 子类型与驱动定位
| 参数 1 值 | 访问类型 | 典型驱动场景 |
|---|---|---|
0x00000000 |
读操作 | 驱动读取已释放的池内存 |
0x00000001 |
写操作 | 驱动写入只读页面或无效地址 |
0x00000002 |
执行操作 | 驱动跳转到非可执行内存 (DEP 触发) |
调试捷径: 0xD1 的 参数 2 是访问的目标地址,参数 3 是触发时的 IRQL。使用
!irql 参数3 可确认 IRQL 值。在 WinDbg 中,!analyze -v 会直接输出 MODULE_NAME 和 IMAGE_NAME,通常不需要手动回溯调用栈。若显示 ntoskrnl.exe,则使用 kb 查看栈顶的 第三方驱动。
2.5 BugCheck 0x7F:UNEXPECTED_KERNEL_MODE_TRAP
0x7F 表示 CPU 在 内核模式 下捕获到了一个 意外异常或陷阱。这通常不是内存访问违规,而是 硬件异常(如除零、调试断点、浮点错误)或 CPU 内部错误(如双重错误、看门狗超时)。
参数 1 陷阱号详解
| 参数 1 (陷阱号) | 异常类型 | 常见原因 |
|---|---|---|
0x00000000 |
除零错误 (Divide by Zero) | 驱动中未检查除数为 0 |
0x00000001 |
调试异常 (Debug Exception) | 驱动触发了断点或单步执行 |
0x00000003 |
断点指令 (Int 3) | 驱动代码中嵌入了调试断点 |
0x00000004 |
溢出异常 (INTO) | 有符号整数溢出 |
0x00000005 |
边界检查异常 (BOUND) | 数组越界访问 |
0x00000006 |
非法操作码 (Invalid Opcode) | CPU 执行了无效指令,通常因代码损坏 |
0x00000008 |
双重错误 (Double Fault) | CPU 在处理异常时又发生异常,通常因栈损坏 |
0x0000000A |
无效任务状态段 (Invalid TSS) | 硬件虚拟化或驱动切换任务出错 |
硬件 vs 软件: 0x7F 崩溃中,若参数 1 为
0x08(双重错误),优先排查 硬件问题:CPU 过热、内存不稳定、主板供电故障。若参数 1 为 0x00 或 0x06,则大概率是 驱动 bug。使用 kb 查看调用栈,找到触发异常的指令所在模块。
2.6 常见 BugCheck 代码速查表
以下表格汇总了实际调试中最高频出现的 10 个 BugCheck 代码,便于快速对照:
| BugCheck 代码 | 名称 | 核心含义 | 常见肇事模块 |
|---|---|---|---|
0x1A |
MEMORY_MANAGEMENT | 内存管理内部状态不一致 | dxgkrnl.sys, 过滤驱动 |
0x50 |
PAGE_FAULT_IN_NONPAGED_AREA | 在非分页区域发生页错误 | nvlddmkm.sys, 网卡驱动 |
0xD1 |
DRIVER_IRQL_NOT_LESS_OR_EQUAL | 驱动在错误 IRQL 访问内存 | rt640x64.sys, e1i65x64.sys |
0x7F |
UNEXPECTED_KERNEL_MODE_TRAP | CPU 捕获意外异常 | 硬件故障 / 驱动代码损坏 |
0x3B |
SYSTEM_SERVICE_EXCEPTION | 系统服务调用异常 | win32k.sys, 显卡驱动 |
0x9F |
DRIVER_POWER_STATE_FAILURE | 驱动电源状态转换失败 | ACPI.sys, 蓝牙驱动 |
0xBE |
ATTEMPTED_WRITE_TO_READONLY_MEMORY | 尝试写入只读内存 | 反病毒驱动, 虚拟化驱动 |
0xC2 |
BAD_POOL_CALLER | 池操作参数错误 | 内存分配驱动, 存储驱动 |
0xCA |
PNP_DETECTED_FATAL_ERROR | 即插即用管理器检测到致命错误 | usbhub.sys, 驱动安装程序 |
0xFE |
BUGCODE_USB_DRIVER | USB 驱动栈异常 | usbport.sys, 第三方 USB 驱动 |
学习路径建议: 建议读者在虚拟机中手动触发一次
0xD1 或 0x50 崩溃(例如使用 NotMyFault 工具),然后使用 WinDbg 执行 !analyze -v,对照本章的参数解读表,亲手验证每个参数的实际含义。这是掌握 BugCheck 分析最有效的方法。