Windows开发,关于通过写代码加载PDB的那些事

最近,接到一个活,要写一个程序,用来批量分析一堆dll和对应的PDB,

其实工作很简单,就是根据一堆偏移,通过PDB文件,找到对应dll里面对应位置的明文符号,

简单的需求,实现起来,通常都很麻烦,

微软的demo中,有一个demo叫做dia,这个demo可以提供类似的功能,

我很高兴地编译了这个项目,但是失败了,加载符号失败,

通过单步调试,越过了若干个小问题(比如msdia120没注册这种),发现出现问题的位置位于 loadDataForExe 这个函数上,

这个函数原型是

virtual HRESULT STDMETHODCALLTYPE loadDataForExe( LPCOLESTR executable, LPCOLESTR searchPath, IUnknown *pCallback) = 0

可以看出,参数1是要解析的可执行文件,参数2是目标dll活着exe,参数3是一个接口,可以为NULL,或者按照微软demo中不用修改。

但是执行之后发现,它给我报错,说路径错误,无法找到目标pdb。

具体错误细节不说了,浪费时间。

说说怎么解决这个问题吧。

解决方式比较简单,

首先确定思路,这个函数不能用,说明它不能自己下载符号了,那么怎么办,

解决办法就是我们下载符号,然后让它直接加载就好了,另一个函数是可以用的 loadDataFromPdb。

确定了思路,那么就要铺平道路,我怎么怎么自己下载符号,

最简单的办法,其实就是使用Windows自己的下载功能,为了尽量少开发,所以可以使用WinDBG内部的功能来自动下载符号。

这样,符号文件有了,加载方式有了,就差如何把符号文件和可执行文件对应起来了,如何对应。

通过分析,我们发现,实际上符号文件落地之后是有个特殊的目录名字的,这个名字很像一个MD5,或者很像一个GUID,

通过分析程序,最终发现,这个目录名字实际上是可执行文件内部记录的一个GUID,再在最后补上一个字符 1。

具体一点,就是这样。

 1 PCHAR pBuffer = (PCHAR)h;
 2 PCHAR pTemp = pBuffer + 0x3C;
 3 pTemp = pBuffer + *(DWORD *)pTemp;
 4 if (!(pTemp[0] == 'P' && pTemp[1] == 'E' && pTemp[2] == '' && pTemp[3] == ''))
 5 {
 6     return NULL;
 7 }
 8 PVOID *pv;
 9 pv = (PVOID*)(pTemp + 0xA8);             //    找到调试节
10 pTemp = pBuffer + (DWORD)*pv;            //    计算调试节指定的内存地址
11 pv = (PVOID *)(pTemp + 4 * 5);           //    找到偏移
12 pTemp = pBuffer + (DWORD)*pv;            //    找到 UUID 基址
13 if (!(pTemp[0] == 'R' && pTemp[1] == 'S' && pTemp[2] == 'D' && pTemp[3] == 'S'))
14 {
15     return NULL;
16 }
17 GUID tGuid = { 0 };
18 memcpy_s(&tGuid, sizeof(tGuid), pTemp + 4, sizeof(tGuid));

先根据可执行文件,找到PE头,从PE头里面找到调试节,

调试节里面找到GUID的偏移地址,

GUID偏移地址的前四个字符固定是RSDS,判断了之后,如果没有问题,

那么之后就是一个GUID的长度,

最后把这个GUID格式化出来变成字符串,最后再在后面补一个字符 '1' 即拼装成了当前文件对应的PDB目录名字,

然后通过这个目录名字可以拼接出一个完整的目录,很像Windows dbghelp.dll 拼接出来的目录,

然后去里面就能找到自己对应的pdb。

整个过程叙述结束。

最后总结一下。

解决pdb无法加载的方法:自己下载,独立加载。

方法:

1:使用WinDBG命令行下载可执行文件对应的PDB文件。

2:根据可执行文件计算PDB路径,寻找目标PDB。

3:找到目标PDB之后,loadDataFromPdb 直接加载PDB就好了。

原文地址:https://www.cnblogs.com/suanguade/p/11351194.html