
缘起
最近,接连在项目中遇到了两个界面无响应的问题。都只发生在客户特定机器上,不方便直接调试,只能抓取 dump 进行事后分析了。
抓取 dump
远程连上可以重现问题的机器,使用 process explorer 初步观察卡死的进程,发现 CPU 占用率很低,经过一段时间的观察,基本确定是一个死锁问题。在卡死的进程上右键,保存完整转储,压缩,发回本地进行分析。
使用 windbg 进行分析
双击抓取的 dump 文件,因为之前已经执行过 windbg.exe -IA,所以默认会通过 windbg 打开 dump 文件。先使用 ~*kvn 粗略浏览一下每个线程的调用栈(因为比较长,这里就不截图了)。经过观察,很快锁定了两个值得进一步查看的线程:一个是主线程(界面线程),因为是界面无响应,肯定要关注界面线程。另外一个是 7 号工作线程。分别看一下这两个线程的调用栈。
主线程的调用栈如下图所示:

注意上图红色高亮部分,主线程通过 SleepConditionVariableCS() 进入等待。
看完主线程,再看 7 号工作线程的调用栈,如下图所示:

7 号线程对 SendMessage() 的调用非常值得怀疑。
猜测整个流程是这样的:主线程不知由于什么原因进入等待状态,而工作线程由于各种各样的原因也进入了等待状态。其中 7 号线程最明显,因为它正在发消息,而主线程此时是无论如何也不会响应这个消息的。
于是,典型的死锁再一次发生了。

加载 AssemblyDesign_Tools.dll 的符号后,查看对应的源码,消除对 SendMessage() 的调用,问题解决!so easy!
说明:工作线程中并没有直接调用
SendMessage(),而是调用了操作界面的相关API,间接调用了SendMessage()给界面线程发消息。
死锁的问题解决了,但是为什么向主线程发个消息就死锁了呢?秉着打破砂锅问到底的原则,我又开始折腾了。下面的内容适合喜欢调试逆向的极客阅读。
深入调查
最开始的思路是:查看主线程在等待的条件变量,然后再调查哪个工作线程会唤醒这个条件变量。奈何 64 位下,前四个参数通过寄存器 RCX, RDX, r8, r9 进行传递,如果这些寄存器没有在栈上存储一份的话,很难查看具体的值。折腾一番后,确实没找到有用的信息,而且就算找到了,也很难找出是哪个线程会执行唤醒操作。
这个死锁问题不像关键段死锁解决起来那么

本文介绍了如何通过windbg和IDA分析远程机器上抓取的dump文件,解决一个由死锁导致的界面无响应问题。作者详细展示了分析主线程和工作线程调用栈的过程,以及如何定位和修复问题,强调了windbg的强大支持在整个分析过程中的作用。
最低0.47元/天 解锁文章
3690





