首页
社区
课程
招聘
[原创]搭建自己的符号服务器(一)——啰嗦篇
2020-10-10 15:54 8768

[原创]搭建自己的符号服务器(一)——啰嗦篇

2020-10-10 15:54
8768

目录

 

啰啰嗦嗦的一篇。。

 

注意:如果你要找的是微软符号服务器,不用往下看了。。。

 

当发生应用异常崩溃、驱动蓝屏时,无论是测试环境,还是生产环境,分析dump文件通常是快速定位问题的一个最佳选择(前提是生成了dump文件)。特别是在生产环境时,既没有源码又没有调试器,这时只有将日志文件、dump文件拷贝回慢慢分析了。
一般常规分析步骤:使用Windbg打开dump文件,设置符号路径源码路径,输入!analyze -v,完事!

注:必须使用与产生dump文件的exe相对应的pdb文件(同时生成)才能进行分析!

 

但是,重点的两个要素往往不能快速准备完成:源码文件和符号文件。特别是,当交付的应用或者驱动已经过去了好几个版本迭代,怎么办?常规做法:从svn或者gitcheckout当初的源码和符号文件版本,继续常规分析(你最好保存了符号文件!)。
当崩溃次数不是很经常时,常规做法并不是很麻烦。但是,当你维护的应用或者驱动很多,手边活儿又很多时,这样一边查看版本,一边扒拉源码,既耗时,又劳心。特别当你忘了备份pdb文件时,挠头去吧……

0x00、常规分析示例

代码见附件。

1、当没有pdb时

打开dump文件,直接!analyze -v

并不知道是出错在哪里。

2、当只有pdb时

配置pdb路径后,再次!analyze -v

已经知道了错误的文件及行号!

这里需要注意,如果是在原开发机器上分析,如果之前的源码还在原路径,即使没有配置源码路径,也会和有源码的情况一样。此时,如果源码版本不对应,可能会显示错误的代码行。

3、当既有pdb又有源码时

配置源码路径后,再次!analyze -v

直接定位到了错误文件的哪一行(一般是箭头的上一行),memcpy向空指针拷贝数据,所以崩了。

0x01、原理

1、dump和pdb

为什么有了pdb和源码便可以分析dump呢?不用说,dump文件中肯定有一些与pdb文件的信息,而pdb文件中存储了一些信息用于分析dump的数据,从上面分析过程可以看出,除了一些用于分析的数据外,肯定还包括源码路径行数等信息。
首先看dump文件信息,使用文本工具打开,搜索.pdb,可以找到

即:dump文件中存储了pdb的信息(不止一个pdb),所以打开dump文件的时候会知道寻找哪个/些pdb。

2、pdb与源码

对于pdb文件格式,微软并没有公开,但是可以通过COM接口来访问pdb文件中存储的信息,示例工程代码参见文末链接。根据链接工程编译出的exe(即网上的pdb inspector,下载见附件),可以查看pdb信息,如下图

附件的pdb inspector不能连续打开pdb文件,如果要打开新的文件,需要退出软件,再打开;如果根据源码重新编译,虽然可以打开新的文件,但显示的仍是第一个打开文件的信息。

 

 

即:pdb文件中确实存储了源码信息。此外,pdb文件中有一串GUID,如果使用hex查看dump文件的话,会发现——pdb路径字符串前面的数据便是这一串GUID,可以说,dump匹配pdb文件并不是仅仅靠路径,而是靠这一串pdb精确匹配(还有age,后续提到)。

 


对于pdb文件格式,不需要深究,我们只需要知道pdb文件很重要,在备份源码的时候,尽量将发布版本的pdb文件一起备份,且保存好(不要外泄)。“在某些情况下,pdb就是你的源码”

3、pdb和exe

这里说一种情况,当你拷贝回了dump文件,也备份了pdb,但是忘了pdb和发布版本的对应关系了(没有放一起,例如,为了防止万一不小心发布应用的时候,把pdb也一并打包出去了,而将源码和pdb分开保存)。
怎么办?——pdb和exe也有对应关系,找到了对应的exe,对应的源码版本也就知道了(如果把发布的版本和对应的源码对应也搞混了……猜吧)。

  • 在exe中也有一个GUID,使用dumpbin工具可以查看,这个GUID便是和pdb文件的GUID对应的,如果不对应,说明两者并不是同时生成的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ...MSVC\14.16.27023\bin\Hostx86\x86>.\dumpbin.exe /headers D:\桌面\Release\DumpDemo.exe
    Microsoft (R) COFF/PE Dumper Version 14.16.27040.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    ……
    Debug Directories
     
         Time Type        Size      RVA  Pointer
     -------- ------- -------- -------- --------
     5F55D0B2 cv            3E 00002218     1218    Format: RSDS, {A3CC46D4-F10E-4703-A393-DA7288A713C1}, 3, D:\Work\DumpDemo\Release\DumpDemo.pdb
     5F55D0B2 feat          14 00002258     1258    Counts: Pre-VC++ 11.00=0, C/C++=23, /GS=23, /sdl=2, guardN=0
    ……

相信你已经找到GUID了,我不知道怎么加粗或标红……此外,大牛blackint3的ARK工具Openark也很方便地提供了查看(里面还有很多实用工具,推荐!),一并上图

可以看出,该工具可以方便查看GUID,和dumpbin一致(当然一致啦!)。另外,还将符号ID组合了起来(GUID+后面的数字age),如果你曾经下载过微软官网符号,本地符号子文件夹的名称就是这个符号ID,一并上图(图床吱呀呀~)。

而pdb文件中的GUID可以使用PDB inspector查看,如图中所示,它们的对应关系即:GUID+age。

4、进一步

有了dump和pdb、pdb和源码的关系,windbg才能根据dump文件搜索到对应的pdb文件,而由pdb文件中的源码信息,windbg会自动到对应路径去下载源码。

  • 如果将pdb文件中的源码信息定制为我们的版本控制系统——源文件索引
  • 再将pdb文件存入集中的一个位置——符号服务器
    那么,分析dump就会变的更便利,只需要在Windbg中配置好符号服务器路径,保证源码服务器可以正常连接,便可以分析dump了(windbg自动会寻找下载符号文件和源码)。

5、应用场合

  • 发布版本众多,每次dump对应的pdb,pdb对应的源码均要手动查找对应
  • 随时随地方便地分析dump

参考网址:
1、pdb文件解析:https://www.codeproject.com/Articles/37883/Symbols-File-Locator


[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2020-10-10 15:55 被comor编辑 ,原因: 添加附件
上传的附件:
收藏
点赞3
打赏
分享
最新回复 (2)
雪    币: 1378
活跃值: (3067)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
风中小筑V 2020-11-1 17:06
2
0
原来这玩意儿是靠 GUID 匹配的..怪不得我拿 hash 工具计算发现不一样..
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
shenxiaolong 2022-7-21 11:40
3
3

不建议使用 !analyze -v 命令,因为这个命令的设计原意不是用于用户态程序调试分析的,而且分析准确度并不高。
用户态程序dump分析,建议使用excr , 速度快,效果好, !analyze -v 并不能提供比excr命令更有价值的信息,却又冗慢无比(excr 在任何情况下都是秒出结果,而!analyze -v至少得几秒钟,复杂情况下可能得十分钟,极端情况下甚至hang住导致windbg崩溃)。

pdb的GUID是由三部分组成的: 校验和 + 时间戳 + age , 刚刚编译出来的pdb age为0 ,然后每修改pdb一次age就加1,可以从pdb的GUID来看出这个pdb文件被修改过多少次,正常情况下内部pdb是一次,外部pdb是两次,有特殊目的的pdb次数更多。 当然这个GUID也可以自己来生成的。 如果更新了pdb文件,也要更新校验和,如果不用工具更新,就得自己更新,通常情况下使用工具的时候,工具会自动更新这个校验和的。

对于无pdb文件的分析,最关键的一点就是获取crash点的模块及模块偏移值(offset),有了这两个信息,就可以脱离windbg来定位crash的源文件和行号了: 如果这个.dmp是完全转储的,就可以从这个.dmp文件中导出所有的程序模块。然后用dumpbin来分析该模板,查找每个函数的起始结束地址。再结合前面找到的offset,来确定这个offset在哪个函数的范围内,从而定位到函数。定位到函数后,再计算  模块offset-函数名地址 ,得到crash地址相对于函数的offset,有了函数名,函数地址,函数offset,能做的事情就比较多了,如果函数不是特别复杂的话,可以直接反汇编这个函数地址  : uf  funcAddr ,然后把 [ funcAddr , offset ] 这一块的汇编代码复制到notepad++中分析,通常情况下再结合源代码很容易肉眼定义出crash的源文件,函数及行号的,还可以结合crash线程的ddstack数据,!dds , !dsp 数据来分析定位得更快,更准确)
如果这个函数特别复杂的话,通常需要用调试器(windbg或者VS,适用于源代码变化不大的情况--比如.dll的文件大小变化很轻微并且编译链接选项没有变化): 加载程序,再利用获得的offset来计算新的crash地址addr,再ln addr (windbg,或者!addr addr),或者在VS环境下设置地址断点 (源代码路径),然后双击断点带到对应的源文件行处。


这儿有一些windbg辅助分析的脚本,可以极大地提高dmp分析的效率及简化过程: https://github.com/shenxiaolong-code/WinScript/tree/master/Windbg/script

其中的crash分析,死锁分析已经做到接近自动化了,内存泄露分析,高CPU分析,按照逐步提供已经极大地简化过程了。

最后于 2022-7-21 11:52 被shenxiaolong编辑 ,原因: 错别字
游客
登录 | 注册 方可回帖
返回