如何更改年龄不匹配的 PDB 以正确匹配?

Posted

技术标签:

【中文标题】如何更改年龄不匹配的 PDB 以正确匹配?【英文标题】:How can you change an age-mismatched PDB to match properly? 【发布时间】:2010-10-19 04:40:45 【问题描述】:

我们的夜间构建过程被破坏了很长一段时间,以至于它生成的 PDB 文件与相应的图像文件的年龄相差几个小时。我已经解决了这个问题。

但是,我想开始使用符号服务器,但由于必须使用这些年龄不匹配的 pdb 文件而无法使用。我通过在 windbg 中使用 .symopt +0x40 方法解决了这个问题。这意味着我必须手动组织我所有的 pdb 文件,并且经过多年的发布,这些文件加起来。

我正在寻找一种方法来修改 windbg 用来标记 pdb 年龄的机制,并强制它与我的图像文件匹配。实用程序ChkMatch 做了类似的事情,但用于 pdb 签名。开发人员在页面“ChkMatch 能够使可执行文件和 PDB 文件匹配,如果它们具有不同的签名但相同的年龄(有关 PDB 签名和年龄的更多信息,请参阅本文)。如果年龄不同,该工具无法使文件匹配。”

我查看了一个十六进制编辑器,甚至找到了看起来与年龄相对应的位,但它必须在内部拉一些更多的技巧,因为我无法让它工作。

有什么想法吗?

编辑: 我不知道这是否有帮助,但在我的特殊情况下,年龄差异是由不必要的重新链接 dll 引起的,这也会重新创建 PDB 文件。但是,我们的构建过程是存储原始 dll(重新链接之前)和重新链接之后的 pdb。我想过以某种方式手动重现这种情况。意思是,强制重新链接 DLL,但在两种情况下都保存 pdb。然后我可以对这两个文件进行二进制比较,看看它们是如何变化的。也许运行某种自动执行此操作的修补软件?通过查看我的控制案例中究竟发生了什么变化,也许我可以对保存在我公司构建过程中的 DLL 和 PDB 做同样的事情?

编辑: 我想到了!!!!感谢第一个答案中的一个 cmet,我查看了“未记录的 Windows 2000 秘密:程序员食谱”一书的 pdf 链接。作者详细介绍了 pdb 文件格式。正如我之前所说,我已经将 pdb 加载到十六进制编辑器中并翻转了一些位,似乎我进行了年龄/签名匹配,但它不起作用。好吧,在使用 W2k 机密书中的实用程序将 pdb“爆炸”到包含的流中之后,我发现它们在流 3 中隐藏了另一个对年龄的引用!!!!!!!!!!一旦我也翻转了那个,它就在windbg中匹配了。这是巨大的!!!!非常感谢....符号服务器我来了!

【问题讨论】:

【参考方案1】:

windbg 不会修改 pdb 的年龄 - 它只会查找它以匹配可执行文件的年龄 - 编译器在它(重新)生成可执行文件和调试文件时会这样做。

现在,根据 debuginfo.com 文章,到达正确的调试目录(codeview 类型),将其与 PDB7 签名匹配并修改可执行文件中的年龄或 GUID 并不难。为什么这不是一个选择?

我猜,你想更新 pdb 吗?恐怕,pdb 是一种专有格式。有多个只读 API(dbghelp.dll 和 dia sdk),但就修改而言,您需要猜测细节才能修改。

【讨论】:

正确,更改签名并不难。但年龄不同。我明确表示我想更新 pdb。类似于 chkmatch 的工作原理。 不错的网站!我现在正在看那本书……也许它会让我走上正轨。我会在/如果我弄清楚什么时候回来报告。 你做主!感谢您推荐该网站,我想通了! 6 年后,微软发布了 PDB 格式规范:github.com/Microsoft/microsoft-pdb。 @pj4533 是否有可能将没有调试信息构建的 dll 与稍后构建的 pdb 匹配?【参考方案2】:

或者您可以使用此处的建议让 windbg 忽略不匹配的签名和年龄:

http://www.debuginfo.com/articles/debuginfomatch.html

...虽然默认情况下 [windbg] 也不允许加载不匹配的调试信息,但 .symopt 调试器命令可以更改默认行为。在我们发出“.symopt+0x40”命令后,调试器将愉快地接受并加载不匹配的 PDB 和 DBG 文件。

希望这会有所帮助。

【讨论】:

【参考方案3】:

虽然正如 SamB 所说,在 PDB(格式 7,我的测试是基于 VS2010 生成的 .exe 和 .pdb,以及 windbg 6.9.0003.113 X86)中有一个额外的年龄参考,所以总共会有 3 个年龄需要修改在 PDB 文件中。不幸的是,SamB 没有告诉我们如何找到魔法 3rd age,stream 3?不!根据我的测试,我提取了100多个pdb流,我尝试了02(如果SamB是0索引)和03,都找不到年龄。

只要你有一个十六进制编辑器和windbg,修复其他两个年龄就很容易了。

查找 GUID 和年龄

使用 symchk 获取不匹配的 PDB 文件的签名(GUID): symchk your.exe /v /s .

通常的输出将包含:

[SYMCHK] ------------------------------------
SymbolCheckVersion  0x00000002
Result              0x00010001
DbgFilename         CPP_Snippet.dbg
DbgTimeDateStamp    0x00000000
DbgSizeOfImage      0x00000000
DbgChecksum         0x00000000
PdbFilename         E:\zrf\C_CPP\CPP_Snippet.pdb
PdbSignature        6D8D99B0-E96B-4093-9D97-8BDC5152B6E0
PdbDbiAge           0x00000188
修正 2 个更容易的年龄

搜索 GUID 的最后一部分:8BDC5152B6E0,因为只有最后一部分是字节序无关的大端/小端问题,它与 pdb 文件中的完全相同。小心搜索原始十六进制值,以使其更准确,您应该验证 GUID 中的其他值(需要在 X86 中反转字节顺序)完全匹配。在 PDB 文件中将恰好找到 2 个 GUID,伴随的年龄正好在 GUID 的第一个字节之前。修改它。就是这样!

我找出第三个年龄的蛮横方法。

转储 PDB 文件的十六进制数,每行一个字节(2 个十六进制数)。 od -v -t x1 your.pdb | sed 's/^[0-9a-f]* //;s/ /\n/g' > age_offset.txt

获取每个匹配年龄的行号,在我的情况下,它是 4 个连续的行,其值为 88 01 00 00, vim age_offset.txt :g/88\n01\n00\n00/s/^/\= (line('.') . ':')/

这是一个 ex 模式命令,最近版本的 vim 应该支持它。

:v/:/d

这将删除所有不包含':'的行,剩余的行是行号 是每个匹配年龄的偏移量。

:%s/:.*//

这将修剪 :88 并单独保留偏移量。

:%s/.*/\=(子匹配(0) - 1)/

这个命令将每个数字减1,我这样做是因为vim中的行号是1-index,并且每个年龄的字节偏移量应该是0-index,以使co-worker实用程序满意。

:w

保存文件

现在我们得到一个文本文件,其中每一行都包含一个十进制数字表示一个偏移量,从这个偏移量开始,接下来的 4 个字节就是你梦想年龄的候选对象。

接下来我尝试修改每个潜在的年龄,然后尝试通过 symchk 进行检查,直到匹配为止,每次只会修补一个偏移量。

首先,我将备份一个修改了 2 个年龄(和 GUID)的 PDB。我们称之为 ori.pdb

这是完成繁重工作的批处理脚本:

for /F usebackq %%i in (`type age_offset.txt`) DO (
  copy /y ori.pdb CPP_Snippet.pdb
  @rem dd if=ori.pdb bs=1c count=4 skip=%%i | xxd -g1 | grep "88 01 00 00" || echo "Bad data at %%i" && goto exit
  dd if=pdb_age.dat of=CPP_Snippet.pdb bs=1c count=4 seek=%%i conv=notrunc
  symchk CPP_Snippet.exe /s . && echo "Found it at offset %%i" && goto exit
  )
:exit

很幸运,我在第 38 个偏移处找到了正确的位置。

这不是尝试错误修复要修补的正确偏移量的最快方法,但它适用于我,这是我的原型,以确保只有 1 个额外的年龄需要修复,否则,可能的组合是巨大的(我已经111 岁的候选人尝试),因此尝试错误的方式不实用。

我认为编写一个实用程序以更快的方式完成相同的工作非常容易。

顺便说一句:根据我的测试。 chkmatch 可能会报告匹配,而 symchk 和 windbg vs 认为它​​不匹配。

windbg 命令 !itoldyouso 匹配,而 .reload /f your_module.exe 仍然无法匹配。

修复3个年龄后,不仅windbg,visual studio也可以加载pdb文件。

【讨论】:

不知道它是否有效...我需要将年龄 2 更改为 1,字节序列 02000000 似乎更常见。 18845 个可能的位置。 :) 仍在等待脚本完成。 对我不起作用(VS 2013 - v120 工具集)。我花了 3 或 4 天时间来创建一个调用 ChkMatch.exe 的工具,然后是 CDB 来获取 PDB 年龄,然后像你所做的那样强制日期。我已将 PDB 中所有 9546 个可能的年龄逐一替换(在每个年龄之后调用 Symchk),但没有得到匹配。【参考方案4】:

使用symopt命令启用SYMOPT_LOAD_ANYTHING:

.symopt+ 0x40

来自docs:

此符号选项可减少符号处理程序在尝试匹配符号时的挑剔性。

此选项在所有调试器中默认关闭。调试器运行后,可以分别使用.symopt+0x40.symopt-0x40 将其打开或关闭。

此选项在 DBH 中默认关闭。 DBH 运行后,可分别使用symopt +40symopt -40 开启或关闭。

更多信息请见http://ntcoder.com/bab/2012/03/06/how-to-force-symbol-loading-in-windbg/

【讨论】:

以上是关于如何更改年龄不匹配的 PDB 以正确匹配?的主要内容,如果未能解决你的问题,请参考以下文章

如何更新本地标签以匹配远程?

如何重新格式化 CSV 文件以匹配正确的 CSV 格式

如何比较两个数组并在id匹配的情况下更改名称?

转移分解分布以匹配更多聚合水平分布

PDB 与 .exe 不匹配

如何正确设置 100% DIV 高度以匹配文档/窗口高度?