Pugnator/pdbmatch

GitHub: Pugnator/pdbmatch

通过修补 PDB 的 GUID/Age 标识,使调试器加载与可执行文件或崩溃转储近似的符号文件,解决符号丢失或不匹配时的调试难题。

Stars: 0 | Forks: 0

# pdbmatch — 用户指南 我们都知道这类问题:你有一个崩溃转储文件,但是所需的 PDB 丢失了、找不到了,或者你只有一个来自不同构建版本的 PDB。如果没有匹配的符号,Visual Studio 和 WinDbg 会拒绝加载该文件。**pdbmatch** 通过修补 PDB 标识来匹配转储中的可执行文件,从而解决了这个问题。 ## 背景 当可执行文件被构建时,链接器会在 EXE 和 PDB 中都嵌入一个唯一的标识符(GUID + Age)。调试器在加载符号之前,会使用它来验证它们是否匹配。即使是完全相同的源代码重新编译也会产生不同的 GUID,因此任何不匹配都会阻止符号加载。 **pdbmatch** 从可执行文件(或从转储中提取的映像)中读取 GUID 和 Age,并将它们写入 PDB,使调试器能够接受它。 ## 构建 pdbmatch 是一个使用 CMake 和 MSVC 构建的 Windows C++11 项目。 ``` cmake -B build -S . cmake --build build --config Release ``` 生成的二进制文件是 `build/Release/pdbmatch.exe`。 ## 命令 | 命令 | 参数 | 描述 | | --- | --- | --- | | `--check` | ` ` | 显示两个文件的调试信息并报告它们是否匹配 | | `--patch` | ` ` | 修补 PDB 标识以匹配可执行文件。首先会创建一个 `.bak` 备份 | | `--extract` | ` ` | 从完整的内存转储中提取 PE 模块映像 | | `--version` | | 打印版本 | | `--help` | | 打印用法 | 传给 `--check` 和 `--patch` 的文件参数是顺序无关的 —— pdbmatch 会通过内容来检测 PE 还是 PDB。 ### 退出代码 | 代码 | 含义 | | --- | --- | | 0 | 成功 / 匹配 | | 1 | 不匹配(仅限 `--check`) | | 2 | 无效参数 | | 3 | 解析或 I/O 错误 | | 4 | 不支持的格式 | ## 场景 1:检查 PDB 是否与可执行文件匹配 ``` pdbmatch --check myapp.exe myapp.pdb ``` 当 PDB Age 与可执行文件不同步时(ILTCG 增加了 DBI 流的 Age,但没有更新 PE 记录)的输出示例: ``` Executable: pdbmatch.exe Debug info file: pdbmatch.pdb Executable: TimeDateStamp: 6a05c3b6 Debug info: 2 ( CodeView ) TimeStamp: 6a05c3b6 Characteristics: 0 MajorVer: 0 MinorVer: 0 Size: 76 RVA: 00006f60 FileOffset: 00005760 CodeView format: RSDS Signature: {6b819250-29a3-4d3f-9aa7-3c7a207c74c1} Age: 1 PdbFile: C:\git\automation_tools\pdbmatch\build\pdbmatch.pdb Debug info: 12 ( Unknown ) TimeStamp: 6a05c3b6 Characteristics: 0 MajorVer: 0 MinorVer: 0 Size: 20 RVA: 00006fac FileOffset: 000057ac Debug info: 13 ( ILTCG ) TimeStamp: 6a05c3b6 Characteristics: 0 MajorVer: 0 MinorVer: 0 Size: 720 RVA: 00006fc0 FileOffset: 000057c0 Debug information file: Format: PDB 7.00 Signature: {6b819250-29a3-4d3f-9aa7-3c7a207c74c1} Age: 2 Result: Mismatched ``` 完全匹配时的输出示例: ``` Executable: pdbmatch.exe Debug info file: pdbmatch.pdb Executable: TimeDateStamp: 6a05c3b6 Debug info: 2 ( CodeView ) TimeStamp: 6a05c3b6 Characteristics: 0 MajorVer: 0 MinorVer: 0 Size: 76 RVA: 00006f60 FileOffset: 00005760 CodeView format: RSDS Signature: {6b819250-29a3-4d3f-9aa7-3c7a207c74c1} Age: 1 PdbFile: C:\git\automation_tools\pdbmatch\build\pdbmatch.pdb Debug info: 12 ( Unknown ) TimeStamp: 6a05c3b6 Characteristics: 0 MajorVer: 0 MinorVer: 0 Size: 20 RVA: 00006fac FileOffset: 000057ac Debug info: 13 ( ILTCG ) TimeStamp: 6a05c3b6 Characteristics: 0 MajorVer: 0 MinorVer: 0 Size: 720 RVA: 00006fc0 FileOffset: 000057c0 Debug information file: Format: PDB 7.00 Signature: {6b819250-29a3-4d3f-9aa7-3c7a207c74c1} Age: 1 Result: Matched ``` ## 场景 2:修补 PDB 以匹配可执行文件 ``` pdbmatch --patch myapp.exe myapp.pdb ``` - 在进行任何更改之前,会在 `myapp.pdb.bak` 处创建一个备份。 - PDB Info 流(Stream 1)和 DBI 流(Stream 3)的 Age 都会被更新 —— 这是让 Visual Studio 接受该文件所必需的。 - 成功时输出为 `PATCHED`,如果不需要修补则输出 `MATCH`。 ## 场景 3:从崩溃转储开始工作 当你只有一个转储文件(没有原始二进制文件)时,请先使用 `--extract` 从转储内存中重构 PE,然后再修补 PDB。 **步骤 1 — 从转储中提取模块映像** ``` pdbmatch --extract crash.dmp myapp.exe C:\work\myapp_from_dump.exe ``` 模块名称会与存储在转储模块列表中的基本名称进行不区分大小写的匹配。输出是一个重构的磁盘 PE 布局(文件偏移量,而非虚拟地址)。 **步骤 2 — 确认不匹配** ``` pdbmatch --check C:\work\myapp_from_dump.exe myapp.pdb ``` **步骤 3 — 修补 PDB** ``` pdbmatch --patch C:\work\myapp_from_dump.exe myapp.pdb ``` **步骤 4 — 在 Visual Studio 或 WinDbg 中加载转储** 将符号路径指向包含已修补 `myapp.pdb` 的目录。调试器现在应该会接受它。 ## 使用 ProcDump 收集完整转储 ``` procdump -ma -t -x C:\dumps myapp.exe [args] ``` | 标志 | 含义 | | --- | --- | | `-ma` | 完整内存转储 | | `-t` | 在进程终止时转储 | | `-x [args]` | 启动进程、监控、在退出或异常时转储 | ## 技术说明 - **GUID 匹配** — 调试器将 PE 的 RSDS CodeView 记录中的 GUID 和 Age 与 PDB 中的 Stream 1 (PDB Info) 和 Stream 3 (DBI) 进行比较。这三者必须一致。MSVC 的 ILTCG 可能会生成 Stream 3 Age 领先于 PE Age 的 PDB;pdbmatch 会修补这两者。 - **备份** — `--patch` 在写入之前总是会创建 `.bak`。如果无法创建备份,PDB 将保持不变。 - **提取的 PE 差异** — 重构的二进制文件在运行时修改的页面(重定位的代码、修补的 IAT 等)上会与磁盘上的原始文件不同。这是正常现象,不会影响 GUID/Age 匹配。 ## 项目布局 ``` src/ main.cpp Command dispatch and output formatting common/ File I/O, GUID formatting, error codes pe/ PE parser (debug directory, CodeView RSDS) pdb/ MSF parser, PDB Info stream patch/ PDB identity patcher (Info + DBI streams) dump/ Minidump reader and module image extractor ```
标签:Bash脚本, C++, CMake, GUID Age修改, MSVC, PDB符号文件, PE文件解析, Visual Studio, WinDbg, Windows开发, Windows调试, 二进制分析, 云安全运维, 云资产清单, 内存转储提取, 安全意识培训, 崩溃转储分析, 开发辅助工具, 数据擦除, 符号加载, 逆向工程