Windbg使用说明

Windbg主要有两种方式,一是静态分析dump文件,二是动态调试程序,例如attach到已运行的程序或者使用Windbg启动调试程序

1、dump文件的分类

dump文件有两种,一是全dump文件,二是minidump文件。二者的区别是全dump文件会dump所有的内存,所以这个dump文件会很大,而minidump文件只会dump一些关键的信息,因此会比较小,在生产环境中我们一般生成minidump文件。全dump文件可以通过任务管理器来生成。

2、dump文件生成机制

windows中,其dump文件生成和linux下不一样,linux下会自动生成core dump文件,而windows需要我们在程序中自己配置

2.1 SetUnhandledExceptionFilter

调用SetUnhandledExceptionFilter设置回调函数,在回调函数中调用MiniDumpWithDump生成dump文件。但这种方式需要为每个dll单独设置回调函数,当dll很多的时候代码就很繁琐。

2.2 使用异常捕获库

异常捕获库感知到异常,自动生成包含异常上下文的文件,例如CrashRpt库或者Chrome浏览器开源的Breakpad/qBreakpad/Crashpad等等。

CrashRpt库尝试给每个模块的线程挂载异常回调函数,但是实现方式有缺陷,有些线程没有挂载上。因为它只能保证在CrashRpt之前加载到进程空间的库才能挂载异常回调函数。后续可以用detous开源代码来捕获。

2.3 使用windbg的dump命令

当有时候我们程序的捕获机制失效导致未生成dump文件的时候,我们可以使用windbg attach到程序进行dump。

2.4 使用任务管理器导出dump文件

在任务管理器中找到对应的程序,右键点击进程,导出转储文件。一般在客户环境无法使用windbg且无法生成dump文件的时候。

3 pdb符号库文件简介

  • PDB - Program DataBase File, 程序数据库文件,存放二进制文件中所有函数及变量的符号,还有一些调试用信息,查看完整的函数调用信息以及变量信息都需要用到PDB文件。
  • Visual Studio的工程配置中是默认生成pdb文件的,Debug和Release版本都是如此。
  • PDB文件的加载需要和原始的exe以及dll文件的时间戳相匹配,若不匹配则可能加载失败。
  • Windbg设置加载路径后才能加载PDB文件。

    4 Windbg的下载和安装

4.1 windbg.appinstaller安装

下载该安装包之后,直接打开,即可在app store中直接安装

4.2 windbg.msixbundle

通过打开windbg.appinstaller得到windbg.msixbundle的下载地址,下载完成之后,通过PoweShell命令安装(当我们没有app store的时候)。

Add-AppxPackage -Path C:\Path\App-Package.msixbundle

![[Pasted image 20250422143855.png]]

4.3 windows sdk setup

通过winsdkSetup来安装10.0版本的windbg。这个可以不通过app store来安装。 windows-sdk

5 Windbg静态分析的步骤

  1. 设置source path以及symbol path,即源代码的路径以及pdb文件的路径![[Pasted image 20250422150938.png]]

  2. 导入dump文件,可以在界面上看到初步的问题分析![[Pasted image 20250422151537.png]]
  3. 使用.excr来切换到发生异常的线程以及汇编代码,如下图所示 ![[Pasted image 20250422151801.png]]
  4. 使用kn/kv/kp来查看函数调用堆栈 如果没有pdb文件,函数调用堆栈中看不到具体的函数名以及行号。 DataProc!PCG::DP::DataProcessImplEds::ProcessData中感叹号前面表示的是模块的名称发,后面的是模块的函数名。 ![[Pasted image 20250422152617.png]]
  5. 如果没有堆栈,那么根据函数调用堆栈中的的模块名称,使用lm命令查看模块时间戳,然后去查找对应版本的pdb文件。如下图所示,lm vm DataProcCalcCp显示了DataProcCalcCp的模块信息。![[Pasted image 20250422153233.png]]
  6. 将pdb文件路径设置到windbg中,使用.ecxr重新切换到发生异常的线程中,然后使用kn/kv/kp命令重新查看函数调用堆栈(堆栈会显示具体的函数名和行号)
  7. 如果堆栈中没有显示具体的函数名和行号,应该是pdb文件加载失败。 可能是pdb版本不对;可能是windbg没有加载,可以使用lm命令确认;有时候需要使用.reload /f命令强制加载所有pdb文件。
  8. minidump文件可能无法看到内存中的变量,但是全dump文件是可以的

6 Windbg动态调试

  1. 将windbg附加到进程上,两种方式,一是程序先启动,将windbg附加到程序进程上;二是直接使用windbg启动程序
  2. 成功attach到进程之后,windbg会自动中断进程,此时输入命令g使程序继续运行
  3. 程序在运行过程中发生异常时,windbg会在异常处停止,此时可以通过命令查看异常处的函数调用堆栈以及变量
  4. 加载pdb,查看更详细的函数调用堆栈(这个步骤也可以在一开始做)
  5. 若客户机器有时间限制,那么可以dump文件,(全dump文件或者折中的minidump文件),待后续分析

7 常用命令

bp – 断点快捷指令
▸ 功能:在指定位置埋下”陷阱”,程序运行到这里自动暂停
▸ 示例:bp MyApp!main → 在MyApp程序的main函数处设卡

bu – 延迟断点
▸ 功能:给尚未加载的代码提前设卡(比如等待DLL加载后触发)
▸ 示例:bu MyPlugin!Init → 当MyPlugin模块载入时在Init函数暂停

bm – 批量设卡
▸ 功能:用*号通配符批量拦截多个函数
▸ 示例:bm MyApp!* → 拦截MyApp所有函数入口

ba – 内存监视
▸ 功能:当指定内存被读写时自动暂停(最多监控4字节范围)
▸ 示例:ba r4 0x12345678 → 监控4字节内存区域的读取行为

lm – 模块清单
▸ 功能:显示已加载的代码模块信息(配合x指令使用更佳)

x – 符号探测器
▸ 功能:快速查找函数/变量地址(支持通配符搜索)
▸ 示例:x MyApp!*Print* → 列出所有含Print关键字的符号

g – 放行指令
▸ 功能:让暂停的程序继续奔跑(就像点击绿色播放按钮)

p – 步过
▸ 功能:执行当前行代码,遇到函数调用不进入

n – 冻结当前线程
▸ 效果:当前线程挂起,其他线程照常运行

m – 解冻当前线程
▸ 效果:被挂起的线程恢复执行

wt – 智能追踪
▸ 功能:记录函数内部所有调用轨迹(排查复杂调用链神器)

.cls – 清屏
▸ 功能:长时间调试后记得清理屏幕信息

d系列 – 内存检测 ▸ db → 以字节形式显示内存(类似十六进制查看器)
▸ dt → 按数据结构解析内存(需提前加载符号表)
▸ dv → 查看局部变量值(快速定位变量异常)

r系列 - 打印寄存器的值

  • r rax -> 打印寄存器rax的值
  • r eax -> 打印寄存器eax的值

dump - dump文件生成
.dump /m C:/dumps/myapp.dmp → 缺省选项,生成标准的minidump, 转储文件通常较小,便于在网络上通过邮件或其他方式传输。 这种文件的信息量较少,只包含系统信息、加载的模块(DLL)信息、 进程信息和线程信息。

▸ .dump /ma C:/dumps/myapp.dmp → 带有尽量多选项的minidump(包括完整的内存内容、句柄、未加载的模块,等等),文件很大,但如果条件允许(本机调试,局域网环境), 推荐使用这种dump。

▸.dump /mFhutwd C:/dumps/myapp.dmp → 带有数据段、非共享的读/写内存页和其他有用的信息的minidump。包含了通过minidump能够得到的最多的信息。是一种折中方案。

汇编代码窗口的开启

To open or switch to the Disassembly window, choose Dissasembly from the View menu. (You can also press ALT+7 or select the Disassembly button on the toolbar. ALT+SHIFT+7 will close the Disassembly Window.)

de-booing-the-windbg

Share: X (Twitter) Facebook LinkedIn