
简介《Windbg中文调试手册》是一份面向Windows开发、驱动调试与系统运维人员的中文调试参考资料重点解决WinDbg安装配置、实时用户模式与内核模式调试、故障转储分析及蓝屏排查等实际问题。手册围绕新版WinDbg展开涵盖现代化界面、脚本功能、可扩展调试数据模型与TTD时程调试并说明Windows 11及Windows 10 1607以上系统、x64与ARM64架构的支持范围同时梳理安装更新、主机与目标机调试环境、符号文件、KD/NTKD与CDB/NTSD等命令行工具的使用脉络。资源包为1个PDF文件约79.77MB便于在本地检索和系统阅读。已有1117人学习适合希望从入门实验逐步过渡到内核驱动调试与崩溃转储分析的技术人员可据此建立较完整的WinDbg知识框架并用于日常排错。1. 装 WinDbg 前先分清新版调试器和经典版不是一个东西做蓝屏和 dump 分析的人多半碰过这种场面机器上明明装着 WinDbg打开却没有时间轴、没有现代化界面!analyze -v跑起来还时不时提示符号不可用。问题通常不在命令而在装错了版本。经典版 WinDbg 跟着 Windows SDK / WDK 一起分发装完在C:\Program Files (x86)\Windows Kits\10\Debuggers\x64底下而新版 WinDbg前身是 WinDbg 预览版走应用安装程序这套分发方式安装完自动在后台检查更新界面上也换了一套。两者共用同一个Dbgeng.dll调试引擎命令、扩展、工作流是通用的但界面、TTD 支持、更新机制完全是两条路。这份 WinDbg 中文调试手册面向后面这条路线装新版、配符号、能自己独立分析 dump。版本选错等于拿着中文手册对不上菜单后面每一步都要多绕半小时。下面按我实际装机的顺序来。2. 装 WinDbg应用安装程序、SDK 勾选、独立工具集三条路2.1 先对版本系统要求卡在两个硬条件上装之前先确认两件事不确认就是给自己埋坑。新版 WinDbg 支持的操作系统是 Windows 11 的全部版本以及 Windows 10 周年更新1607或更高版本处理器架构只支持 x64 和 ARM64。注意这里没有 x8632 位系统上只能退回去用 SDK 里的经典版调试器这不是配置问题是根本装不上。系统版本号别靠感觉直接查# 查看系统版本与架构1607 对应的内部版本号是 14393 Get-ComputerInfo | Select-Object OsName, OsVersion, OsArchitecture $env:PROCESSOR_ARCHITECTURE # 返回 AMD64 或 ARM64 才符合新版要求OsVersion里形如10.0.14393的值就是判断依据只要主版本是 10.0 且 build 不低于 14393就满足最低要求。PROCESSOR_ARCHITECTURE返回AMD64表示 x64 处理器返回ARM64则是 ARM 平台这两个值都能装新版。返回x86的话后面的步骤基本要全部换成经典版路线越早发现越好。2.2 走应用安装程序新版 WinDbg 的正规装法新版 WinDbg 通过应用安装程序分发在下载页面上点「安装」调试器会自己下载并装好之后定期在后台检查新版本、必要时自动更新。这一步没有让你选路径、选组件的余地装完就是一个能在开始菜单里直接点开的应用。如果拿到的是离线安装包.msix格式用 PowerShell 注册安装# 注册安装本地 msix 包路径换成你实际存放的位置 Add-AppxPackage -Path C:\Install\WinDbg.msix # 装完核对一下是否真的进去了注意别把旧版 WinDbg 预览版当成新版 Get-AppxPackage | Where-Object Name -like *WinDbg* | Select-Object Name, Version, InstallLocationAdd-AppxPackage是应用安装程序的命令行入口对签名合规的包等效于点界面的「安装」。第二条Get-AppxPackage的过滤条件是关键如果你的机器上还残留着从商店装的旧版 WinDbg 预览版这条命令会同时列出两条记录。旧版不会再收到任何更新功能上也已经落后于新版看到两条记录就该把旧的那条卸掉避免以后双击时打开的是老界面 —— 这种「装了两个、打开错的」情况在实际环境里非常常见查起来特别费劲。安装或更新过程中报错的话先看应用的安装日志再谈其他不要上来就重装系统级组件。应用安装程序相关的问题通常是签名信任链、系统时间偏差或者包缓存损坏这类问题在同一个位置反复重试没有意义。2.3 走 SDK 安装器只要调试工具别把整个 SDK 拖下来经典版调试器windbg.exe、命令行调试器cdb.exe和kd.exe、ntsd.exe这些都包含在 Windows 调试工具集合里。这个集合有三种获取方式作为 WDK 的一部分、作为 Windows SDK 的一部分、或者作为独立工具集单独安装。只想要调试器的话用第三种启动 Windows SDK 的安装程序在功能列表里只勾「Windows 调试工具」把其他所有组件全部取消勾选。装完去这两个目录确认# x64 和 x86 两套工具各在各自的目录下同一个引擎、不同的位数 $dirs ( C:\Program Files (x86)\Windows Kits\10\Debuggers\x64, C:\Program Files (x86)\Windows Kits\10\Debuggers\x86 ) foreach ($d in $dirs) { if (Test-Path $d) { Get-ChildItem $d -Filter *.exe | Select-Object -First 8 Name } }这两个目录里放的是同一套工具的两个位数版本windbg.exe、cdb.exe、ntkd.exe都在里面。之所以强调「只勾调试工具」是因为 SDK 的完整安装会额外拖进来一堆编译器、SDK 头文件、模拟器组件对只想调 dump 的人来说纯属占地方而且安装时间长得多、失败概率也更高。如果你机器上已经装了 Visual Studio 和 WDK调试环境会有六个可选项它们都是同一引擎的不同外壳命令层面没有差异随便挑一个顺手的就行不用纠结。2.4 装完之后三个位置PATH 只加一个装完你手上大概率有三种东西开始菜单里的新版 WinDbg、SDK 目录下的经典版windbg.exe、以及命令行用的cdb.exe。我的习惯是 PATH 里只加 x64 那个 Debuggers 目录理由很简单命令行场景下cdb用得最多新版 WinDbg 是应用包本来就不在 PATH 里也没必要强行塞进去。# 把 x64 调试器目录追加到当前用户的 PATH只在当前会话生效确认无误再写 User 级 $dbg C:\Program Files (x86)\Windows Kits\10\Debuggers\x64 $old [Environment]::GetEnvironmentVariable(Path, User) if ($old -notlike *$dbg*) { [Environment]::SetEnvironmentVariable(Path, $old;$dbg, User) }先只改当前会话、验证cdb -version能跑通再决定要不要落到User级这是踩过坑之后养成的习惯 —— PATH 写坏了会让一堆命令集体失效而排查 PATH 问题往往比装调试器本身还费时间。加完目录后必须重开一个终端才生效别在同一个窗口里反复试。3. 配符号_NT_SYMBOL_PATH、.sympath、.symfix 谁说了算3.1 符号路径的三层优先级环境变量、会话命令、默认值符号文件PDB里存的是运行可执行文件时用不到、但调试时极度关键的数据函数名、变量名、行号。没有符号lm里模块会显示成(deferred)或者只有导出符号k打出来的调用栈全是地址加偏移!analyze -v也只能给你一个大概方向。可以这么说WinDbg 里所有让人抓狂的「玄学问题」八成最后都落在符号没配对这一条上。符号路径有三个来源优先级从低到高是调试器内置默认值、_NT_SYMBOL_PATH环境变量、当前会话里用.sympath设的值。会话命令优先级最高覆盖环境变量所以你在两台机器上看到不同的符号行为先怀疑环境变量有没有在别处被设过。在调试器里查当前值0:000 .sympath Symbol search path is: srv* Expanded Symbol search path is: cache*;SRV*Symbol search path is是调试器自己认的短写法Expanded那一行是它实际展开后的结果cache*表示本地缓存SRV*后面跟的是符号服务器。这两行很重要因为.sympath输出里的第一行就是可以直接抄进_NT_SYMBOL_PATH的值不用你手写服务器地址。想做成每台机器开箱即用就把这个短写法落进用户级环境变量# 用户级写入不需要管理员权限对当前用户启动的所有调试器生效 [Environment]::SetEnvironmentVariable(_NT_SYMBOL_PATH, srv*, User) [Environment]::GetEnvironmentVariable(_NT_SYMBOL_PATH, User) # 回读确认 # 想换缓存盘srv*D:\Symbols*缓存目录别放在系统盘临时目录下srv*这个写法告诉调试器「走符号服务器那一套缓存目录用默认的」。默认缓存位置在C:\ProgramData\Dbg\sym从lm输出里那一长串.pdb路径就能看出来。改环境变量后必须重新打开调试器已经启动的进程读不到新值。3.2 .symfix 和 .reload把「符号不可用」这一步处理掉.symfix是把符号路径设成默认值的命令等价于.sympath srv*专治「我不确定该填什么」这种情况。手工配的时候我喜欢.symfix打底再用.sympath追加私有符号目录这样默认符号和自编译符号互不干扰0:000 .symfix $$ 设成默认符号路径等价 .sympath srv* 0:000 .sympath C:\MyApp\x64\Debug $$ 追加私有 PDB 目录加号是追加不是覆盖 0:000 .sympath $$ 回显确认两段都在 0:000 .reload $$ 重新做一次符号搜索 0:000 .reload /f $$ 强制重新加载忽略已缓存的旧符号四个命令各有分工。.symfix负责打底.sympath负责追加且不破坏已有路径 —— 用.sympath而不带加号会把默认那段覆盖掉这是新手最容易犯的错误之一。.reload触发一次初始符号搜索.reload /f是强制模式会无视已经加载的旧符号重新拉一遍符号被换过或者缓存里存了错误版本时用它。还有一点必须说清楚lm输出里大量模块显示(deferred)这不是错误。调试器默认做延迟加载只在你真正用到某个模块的符号时才去解析避免一断下来就把几十个 PDB 全拉一遍。看到(deferred)别急着怀疑配置用x命令去引用一下对应模块它自然会变成(pdb symbols)。3.3 私有符号和源码路径调试自己程序时多做的两步调试自己写的程序符号和源码是两个独立的配置。符号走.sympath源码走.srcpath或.srcfix。有一种情况可以省掉源码那步编译出来的 PDB 里记录了源文件的完全限定路径而源码又原封不动躺在原路径上调试器能顺着 PDB 里的路径直接找到源码。0:000 .symfix 0:000 .sympath C:\MyApp\x64\Debug $$ 私有符号目录和 exe 放一起 0:000 .reload 0:000 bu MyApp!main $$ 先下断点验证符号是否认得这个函数 0:000 bl $$ 列出断点看是否已解析成具体地址bu未解析断点和bp普通断点的区别值得单说bu允许在模块还没加载、符号还没解析的时机就设下断点调试器会在模块加载后自动完成地址解析bp则需要当场解析模块没加载时会失败。调试启动阶段的问题bu几乎是唯一正确的选择。所以顺序上是先.sympath、.reload再bu最后bl确认 —— 如果bl里断点地址是0 e开头说明符号没认上回头查.sympath那条路径是不是写错了。4. 首次跑通用记事本下断点用 !analyze -v 分析崩溃4.1 附加记事本从符号到断点的完整一条链新手最稳的练手对象是notepad.exe因为它一定有、路径也固定通常在C:\Windows\System32。整个过程验证的是符号配置是否真的生效命令一条都不能跳。0:000 .sympath srv* $$ 设符号搜索路径 0:000 .reload $$ 初始符号搜索拉取 notepad 和系统 DLL 的 PDB 0:000 x notepad!wWin* $$ 按掩码查符号确认 PDB 真的解析出来了 0:000 bu notepad!wWinMain $$ 在 WinMain 上下未解析断点 0:000 bl $$ 确认断点已解析到具体地址 0:000 g $$ 放行命中后会自动断下来每一步的输出都有判断价值。x notepad!wWin*会列出匹配的符号名如果返回「符号不可用」或者干脆没输出说明 PDB 没下来重新执行.reload再试。bl的输出里断点前面如果是0 ee表示已启用enabled后面跟的地址是解析结果。g之后记事本会跑到wWinMain入口处停下此时再敲两个命令看现场0:000 lm $$ 列出已加载模块重点看 notepad 是否带 (pdb symbols) 0:000 k $$ 打印当前调用栈有符号时函数名可读lm输出里notepad那一行如果显示(pdb symbols)并跟着一条notepad.pdb的完整路径说明符号链路完全通了。往下大概率还能看到ntdll也是(pdb symbols)其余像KERNEL32、USER32显示(deferred)属于正常延迟加载。k打出调用栈能看到notepad!wWinMain、notepad!__scrt_common_main_seh、KERNEL32!BaseThreadInitThunk、ntdll!RtlUserThreadStart这样一串说明栈回溯也用上了符号。4.2 线程与断点ZwWriteFile 上的现场还原接着把断点下到系统调用上看多线程环境下的现场。这个练习能同时验证符号、断点和线程切换三件事。0:000 bu ntdll!ZwWriteFile $$ 在写文件的内核入口下断 0:000 bl $$ 确认断点状态 0:000 g $$ 放行放行之后在记事本窗口里随便敲点字然后选文件菜单保存代码会中断在ZwWriteFile上。此时先敲k看栈再敲波浪号看线程全貌0:011 ~ $$ 列出进程内所有线程及索引 0:011 ~0s $$ 切到 0 号线程s 是 set 的意思 0:000 k $$ 看 0 号线程的栈 0:000 qd $$ 退出调试器并分离让进程继续跑提示符左边0:011的格式是「处理器编号:线程编号」切换线程用~Nsq是退出并结束被调试进程qd是退出并分离让进程继续活着。这两个命令的区别非常关键分析线上程序时敲错一个字母进程就被你亲手杀掉了。至于~Ns里的N就是~列表里那一列索引号不是线程 ID别混。4.3 分析自己的程序一次整数除零的完整分析调自己写的程序更直接。假设MyApp.exe和MyApp.pdb都在C:\MyApp\x64\Debug源码在C:\MyApp\MyApp用「打开可执行文件」载入MyApp.exe然后0:000 .symfix 0:000 .sympath C:\MyApp\x64\Debug $$ 私有符号和 exe 同目录 0:000 .reload 0:000 bu MyApp!main 0:000 g命中main之后按 F11 单步一路跟进被调函数单步到那行除法时程序会因为整数除零直接崩进调试器。这一步是最痛快的地方源码和命令窗口并排你能看见执行光标停在哪一行不需要猜。0:000 !analyze -v $$ 自动分析这是排障时最该先敲的命令!analyze -v会给出异常记录、出错指令地址、FAULTING_SOURCE_LINE、FAULTING_SOURCE_CODE以及STACK_TEXT。异常码c0000094是整数除零配合FAULTING_SOURCE_LINE_NUMBER直接定位到源文件第几行。这条命令的价值在于它把散落的信息聚在一屏里你不用一条条手工捞。内核模式那边先别急着上手。它默认是两台机器的架构调试器跑在主机上被调试代码跑在目标机上中间通过以太网、USB 2.0/3.0 或串口连接。有条件的话先在虚拟机里把主机目标机这一套搭起来再动真实硬件 —— 直接在物理机上试内核调试蓝屏排查起来会让你怀疑人生。5. 排查装 WinDbg 时最容易翻车的五个点5.1 装完找不到 windbg.exe现象开始菜单里能看到 WinDbg 图标但在C:\Program Files (x86)\Windows Kits\10\Debuggers\x64里翻不到windbg.exe命令行敲windbg也提示找不到命令。原因装的是应用安装程序分发的新版 WinDbg它不往 SDK 目录写任何东西反过来只装了新版那个经典目录根本不会存在。两个版本的文件布局完全独立。解决先确定自己要哪一版。要新版就从开始菜单启动不要找 exe要经典版和cdb就用 SDK 安装器重新跑一次只勾「Windows 调试工具」。判断当前装了什么用Get-AppxPackage | Where-Object Name -like *WinDbg*和Test-Path查目录两条都查一遍最省事。5.2 敲了 .sympath srv* 还是提示符号不可用现象.sympath回显确实是srv*但x notepad!wWin*返回符号不可用lm里全是(deferred)。原因设了路径不等于拉了符号中间还差一次.reload。另一个高频原因是缓存目录C:\ProgramData\Dbg\sym没有写权限PDB 下载中断后留下半截文件后续加载会一直失败。解决先补.reload不行再.reload /f强制重来。还不行就去缓存目录看一眼把对应模块的子目录删掉重新加载。用非管理员账户跑调试器时我一般会把缓存目录挪到用户目录下绕开权限问题。5.3 bu 下断点失败或者断点不命中现象bu notepad!wWinMain执行完没报错bl列出来的地址是0 e开头g放行后进程直接跑完压根没断下来。原因符号没解析成功断点悬在未解析状态。或者模块名写错notepad!和实际加载的模块名不一致。还有一种是自己程序的 PDB 版本和 exe 对不上编译后只替换了 exe 没换 PDB。解决先x notepad!wWin*确认符号能不能查到查不到就回到符号配置。私有符号场景下x MyApp!main更直接。确认 PDB 与 exe 是同一批次编译产物中间改过代码就重新完整编译一次别只 copy 一个文件过去。5.4 32 位和 64 位调试器装混现象用 x86 目录下的windbg.exe去附加一个 64 位进程附加失败或者附加上了但栈完全不可读。原因调试器位数和被调试代码位数必须匹配。Debuggers\x64只能调 64 位代码Debuggers\x86只能调 32 位代码混用不会有任何提示只会给你一堆看不懂的输出。解决先确认目标进程位数再挑对应目录的调试器。自己程序的输出目录里带x64、x86的那一层就是现成的答案。分析 dump 也一样dump 是 64 位进程产生的就必须用 64 位调试器打开。5.5 旧版 WinDbg 预览版残留导致打开的是老界面现象装完新版从开始菜单点开却还是旧界面没有时间轴那套东西功能菜单也对不上中文手册里的描述。原因机器上同时存在旧版 WinDbg 预览版和新版 WinDbg两个应用图标长得像点错了。旧版已经不再接收任何更新功能上会越来越落后。解决用前面那条Get-AppxPackage过滤命令列出全部记录把旧的那条卸掉只留新版。这不是配置问题纯粹是文件残留越早清干净越好否则以后每次排查都要重新确认一遍自己开的是哪个。6. 验证能不能用三条命令和一次 dump 复现装完不算完得有一套固定动作确认这台机器真的能分析 dump。我一般走三步全程不超过五分钟。第一步验证符号链路。随便打开一个调试器会话敲.symfix加.reload然后x ntdll!Rtl*。能列出一串函数名说明系统符号能正常下载和解析。这一步失败后面全是空谈。第二步验证私有符号和源码路径。拿自己编译的一个小程序.sympath指向它的输出目录.reload然后x 模块名!main。能查到符号说明私有 PDB 目录写法没问题。第三步也是最关键的一步真拿一个 dump 文件跑一遍0:000 .symfix 0:000 .reload 0:000 !analyze -v 0:000 lm 0:000 k!analyze -v输出里如果能看到具体模块名加函数名而不是一片纯地址符号就算配到位了。lm里重点看几个关键系统模块是否显示(pdb symbols)k看调用栈能不能打出可读函数名。三条全过这套环境才算真正可用。打开 dump 的方式也顺手确认一下命令行里直接带-z参数指向 dump 文件路径就能加载不需要先开界面再找菜单。这条路子适合写批处理也适合远程连到一台只有命令行的机器上排查。验证项命令通过标准符号服务器连通.symfix.reloadx ntdll!Rtl*列出函数名列表私有符号目录.sympath.reloadx 模块!main能查到自编译符号dump 分析!analyze -v输出含模块名和函数名模块符号状态lm关键模块显示(pdb symbols)栈回溯k栈帧函数名可读说个教训。早几年我在一台新机器上装完调试器图省事只敲了.sympath srv*就去开 dump!analyze -v打出来一屏全是地址我还以为是 dump 本身坏了来回折腾了快两个小时最后发现只是少敲了一次.reload。从那以后我每装一台机器都强制走一遍.symfix→.reload→x ntdll!Rtl*→!analyze -v这四步一步不跳哪怕再急也先跑完再干活。这三个命令花的时间不到一分钟省下来的可能是半天。这份 WinDbg 中文调试手册最适合放在手边当速查用命令、参数、常见报错对照着翻比每次现搜快得多。希望帮到你。本文还有配套的精品资源点击获取