Crash都是发生在某一函数中, 而95%的crash都是由下面两种情况中的一种导致的:
- 发生crash的函数获得了错误的参数.
- 发生crash的函数使用了损坏了的内部数据.
代码的执行过程是对数据进行变化的过程. 对同一段代码, 在相同环境下, 如果使用的数据都相同, 那么执行的结果肯定是唯一的. 如果函数发生崩溃, 那么肯定是使用到的数据跟理想情况有差别. 函数使用的数据来源: 一是函数传入的参数, 二是函数体引用到的成员变量或全局变量.
因此要分析crash dump, 大多数情况都是寻找错误数据的来源. 下面是一些常见的, 导致数据错误的例子:
- 使用了未初始化的变量. 比如没有分配内存的指针, 没有初始化的CriticalSection.
- 错误地计算了函数参数. 比如调用函数的时候弄错了传入参数的顺序, 字符串操作时算错了字符串长度.
- 错误地使用数据导致发生corruption. 比如double free导致heap curruption, 在多线程环境下忘记同步导致据全局变量计算错误, COM的AddRef和Release调用不配对.
- 违背程序逻辑地使用数据. 比如在程序加载必要的资源以前就开始使用这些资源的函数.
由此可见, crash dump的分析完全取决于程序的情况. 能否从crash dump中挖掘出有用的信息, 取决于:
- 对目标程序的熟悉程度. 包括程序的架构, 重要函数的作用, 重要的数据结构, 函数之间的调用逻辑, 关键函数的实现细节.
- 对基础知识的掌握程度, 包括汇编, 异常, 内存, API, 消息, CRT, 等等.
一般来说, 分析一个dump可以通过下面的步骤入手:
- 看清楚是何种异常导致的崩溃.
- 对齐symbol, 找到发生崩溃的函数名字, 以及对应的汇编代码和源代码.
- 列出callstack
- 检查callstack是否合理
- 检查发生崩溃的函数是否得到了正确的参数.
- 检查发生崩溃的函数使用的数据是否正确.
- 结合上面的信息, 构思来龙去脉, 然后用资料来证明, 或者反驳自己的猜想.
- 通过进一步的操作来获取更有意义的资料. 比如激活pageheap后, 重新抓取dump, 或者干脆进行Live debug