[原]调试实战——使用windbg调试崩溃在ole32!CStdMarshal::DisconnectSrvIPIDs
前言
最近程序会不定期崩溃,很是头疼!今晚终于忍无可忍,下决心要干掉它!从之前的几个相关的dump
可以猜到是有接口未释放导致的问题,但没有确认到底是哪个接口。本篇总结记录了找到这个接口的过程。
{% note info %}
这是几年前在项目中遇到的一个问题。我对之前的笔记进行了整理重新发布于此。
{% endnote %}
初识问题
用windbg
打开dump
文件,显示如下:
从图中可以很明显的看出来是访问违例(因为红框标识的地址5bbc97f8
的内容是????????
),而且这是一条call
指令,call
调用的地址存储在ecx+8
(0x5bbc97f8
) 处。我们可以用!address address
来查看某个地址的信息,输入!address 5bbc97f8
从图中得知,地址5bbc97f8
对应的内存已经被释放了(State
是 MEM_FREE
)。windbg
还贴心的给出了和此地址相关的一些模块信息。其中有几个是我们自己的模块,从Unloaded
可以看出这几个模块曾经被卸载过。很有可能跟我们的崩溃相关。
至此,我们可以大胆猜测崩溃的原因是模块被卸载后,还要执行其中的代码!:bomb:
下面让我们继续分析,为什么模块卸载后还会执行其中的代码!
寻根溯源
先用.ecxr
切换到发生异常时的上下文,然后用kpn
(k
显示调用栈,p
显示函数的参数,n
显示栈帧编号)查看调用栈,如下图:
从上图可知,11
号线程在调用CoUninitialize()
执行COM
的线程清理工作,frame 0
在调用ole32!CStdMarshal::DisconnectSrvIPIDs()
时崩溃了!从Disconnect
猜测是要断开连接!使用ub 76f8bbe7
(76f8bbe7
是由76f8bbe4
+ 3
得到的,在上一篇文章里介绍过了)查看崩溃前的几条指令:
红框内的指令非常像是虚函数的调用(调用约定是stdcall
,不是thiscall
) eax
是调用类对象的指针(通过入栈方式传递), ecx
指向虚函数表(vtable
),dword ptr[ecx+8]
是虚函数表中的第三项(32
位下指针占4
字节,第一项的偏移是+0
,第二项的偏移是+4
,第三项的偏移是+8
)。回想IUnkown
的前三个函数分别是QueryInterface()
, AddRef()
和 Release()
,可以大胆猜测这里是在调用Release()
,跟上下文也很搭(从CoUninitialize()
推断当前线程正在做COM
清理工作)!我们的猜测很有可能是正确的!
我们先小小总结一下:整个过程应该是这样的,我们使用了一个COM
接口,但是由于某些原因并没有释放,线程在退出的时候,调用CoUninitialize()
做COM
清理工作。当清理到我们的接口的时候,由于接口代码所在的dll
已经被卸载了,从而导致了访问违例!下面我们的任务是找出到底是哪个接口没释放,然后对照代码验证我们的猜测是否正确!
验证
先用lm a 5bbc97f8
确认下该地址属于哪个模块。
看来,地址5bbc97f8
落在AccIME.dll
中,由于AccIME.dll
已经被卸载了,我们需要使用命令.reload /f AccIME.dll=5bbb0000,5bbd8000-5bbb0000
来重新加载已经被卸载的模块,我们可以使用lm vm AccIME
来确认加载成功与否。
从上图可知,我们成功的加载了AccIME
模块,符号也加载成功了。
**友情提示:**如果遇到如下错误,说明我们成功加载了被卸载的dll
,但是加载符号的时候遇到了问题。
可以使用!sym noisy
开启嘈杂模式,然后再次执行.reload
命令,通过输出日志来排查具体原因。排查完毕后可以使用!sym quiet
来关闭嘈杂模式。具体使用方法可通过.hh !sym
查看windbg
帮助文档。
接下来我们用ln 5bbc97f8
查一下该地址附近函数。
至此,我们可以看到是与AccIME!CCfgDpyScheme
类相关的问题,接下来我们可以把重点放到对此类的引用计数的使用上了!
本篇总结暂时写到这,还需要弄明白几个问题:
- 涉及引用计数的相关代码是否有问题。
- 为什么有时候会崩溃,有时候不崩溃。
有结果后会再发一篇总结,敬请期待!(能力有限,没能查出来:cry:)
解决方案
由于没能查出代码哪里有问题。使用临时解决方案先绕过去了:在退出的时候,没有使用FreeLibrary()
显示卸载该模块,交由操作系统来做模块清理工作)。
未弄明白的问题的可能原因:
涉及引用计数的相关代码是否有问题。
Oops, 没查出代码哪里有问题。无法确定问题的根本原因。:sob:
为什么有时候会崩溃,有时候不崩溃。
有时候崩溃
应该是还没断开连接,对应的dll
就被卸载了。有时候不崩溃
应该是在卸载dll
之前,已经成功的断开了连接。
命令总结
!address address
可以查看对应地址的信息,如果一个地址不可访问,那么显示的内容会是????????
。.ecxr
可以让windbg
使用发生异常时的上下文,这样再使用k
等命令时就是发生异常时的相关信息了。u
可以反汇编某个地址对应的代码,ub
可以向前反汇编,b
应该是backward
的缩写。lm a address
可以查看address
所属的模块。ln
可以查看某个地址附近的符号名。.reload /f <image.ext>=<base>,<size>
可以加载模块(甚至是已卸载的)到base
指定的位置,并为之加载符号。关于各个
windbg
命令的用法,可以使用.hh command
进行查看!非常方便,而且非常重要!
参考资料
- 《格蠹汇编》
windbg
帮助文档
[原]调试实战——使用windbg调试崩溃在ole32!CStdMarshal::DisconnectSrvIPIDs的更多相关文章
- [原]崩溃在ole32!CStdMarshal::DisconnectSrvIPIDs
最近项目里遇到一个崩溃,不定期出现,很是头疼!今晚终于忍无可忍,下决心要干掉它!(于是用凉水洗了把脸,开始分析dump)希望凌晨的这篇总结对有相似经历的朋友有所启发!(看之前相关的几个dump可以猜到 ...
- [原]调试实战——使用windbg调试崩溃在ComFriendlyWaitMtaThreadProc
原调试debugwindbgcrash崩溃COM 前言 这是几年前在项目中遇到的一个崩溃问题,崩溃在了ComFriendlyWaitMtaThreadProc()里,没有源码.耗费了我很大精力,最终通 ...
- [原]调试实战——使用windbg调试DLL卸载时的死锁
原调试debugwindbg死锁deadlock 前言 最近我们的程序在退出时会卡住,调查发现是在卸载dll时死锁了.大概流程是这样的:我们的dll在加载的时候会创建一个工作线程,在卸载的时候,会设置 ...
- [原]调试实战——使用windbg调试TerminateThread导致的死锁
原调试debugwindbg死锁deadlock 前言 项目里的一个升级程序偶尔会死锁,查看dump后发现是死在了ShellExecuteExW里.经验少,不知道为什么,于是在高端调试论坛里发帖求助, ...
- [原]调试实战——使用windbg调试excel启动时死锁
原调试debugwindbg死锁deadlock 前言 这是几年前在项目中遇到的一个死锁问题,在博客园发布过.我对之前的笔记进行了整理重新发布于此. 本文假设小伙伴们知道一些基本概念,比如什么是.du ...
- .NET高级调试系列-Windbg调试入门篇
Windbg是.NET高级调试领域中不可或缺的一个工具和利器,也是日常我们分析解决问题的必备.准备近期写2篇精华文章,集中给大家分享一下如果通过Windbg进行.NET高级调试. 今天我们来一篇入门的 ...
- Windbg调试命令详解
作者:张佩][原文:http://www.yiiyee.cn/Blog] 1. 概述 用户成功安装微软Windows调试工具集后,能够在安装目录下发现四个调试器程序,分别是:cdb.exe.ntsd. ...
- Windbg调试命令详解(1)
转载注明>> [作者:张佩][镜像:http://www.yiiyee.cn/Blog] 1. 概述 用户成功安装微软Windows调试工具集后,能够在安装目录下发现四个调试器程序,分别是 ...
- windbg调试.net程序
1. 解决线上.NET应用程序的如下问题: 崩溃 CPU高 程序异常 程序Hang死 2. 安装WinDbg: http://msdn.microsoft.com/en-us/windows/hard ...
随机推荐
- 十四、CI框架之数据库以参数形式插入操作
一.代码如下: 二.使用浏览器打开 三.我们查看数据库,被成功插入数据 不忘初心,如果您认为这篇文章有价值,认同作者的付出,可以微信二维码打赏任意金额给作者(微信号:382477247)哦,谢谢.
- 第42章 AWR报表的使用
第42章 AWR报表的使用exec dbms_gather.gather_table_stats('scott','emp');exec dbms_gather_gather_index_stats( ...
- Android进阶——多线程系列之异步任务AsyncTask的使用与源码分析
AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并主线程中更新UI,通过AsyncTask可以更加方便执行后台任务以及在主线程中访问UI ...
- swiper用axios异步请求后 循环失效
解决方案 使用ajax动态获取数据 当数据还没有收到的时候,swiper组件收到的是跟组件data传过来的空数组,会造成渲染问题 这个时候可以给swiper组件 设置一个 v-if='list.l ...
- chan-vese模型
Python--level set (水平集)和 chan-vese模型 2018年08月28日 10:51:54 GlassySky0816 阅读数:1604 版权声明:本文为博主原创文章,未 ...
- JavaEE--JNDI(下,实现)
参考:https://blog.csdn.net/ouyida3/article/details/46699023 https://www.landui.com/help/show-6158.htm ...
- textField 基本属性
_textField.frame = CGRectMake(0, 0, 200, 50); _textField.delegate = self; _textField.text = str; [_t ...
- [转]Log4j使用总结
Log4j使用总结 一.介绍 Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务 器.NT的事件记录器. ...
- MacOS常用快捷键
command+空格 打开Spotlight command+m 最小化当前窗口 control+command+f 最大化当前窗口 command+q ...
- Python笔记_第四篇_高阶编程_正则表达式_1.正则表达式简介(re模块)
1. 从一个判断手机号的问题引入: 如果给你一个字符串,去判断是否是一个手机号码,我们通过之前的学习可以有如下代码: # 如果用普通的方式去检验一个电话号码非常麻烦. def checkPhone(s ...