小明思考

高性能服务器端计算
posts - 70, comments - 428, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

debug to fix crash

Posted on 2012-04-27 15:34 小明 阅读(2683) 评论(5)  编辑 收藏 引用 所属分类: C/C++Debug
最近遇到一个Windows Office Communicator 2007 崩溃的问题,有些意思,写下来跟大家分享。

【现象】
我们公司内部使用office communicator来做内部人员的IM工具,使用的是一个定制版本(plugin), 可以跟公司内部的组织架构做整合。我使用的OS是Windows 7 32bit,一开始使用并无问题,在某次windows update之后,发现没法添加好友,在其他的同事的windows 7机器也出现这个问题,windows XP 上并无这个问题。由于我并没有源码,只能在汇编这一级别进行调试。

大概的过程是这样的,在communicator上点击按钮后,会打开一个IE窗口,这个页面会使用一个公司的ActiveX控件,崩溃会发生在这个控件中,无法使用该功能。

【调试】
在没有调试器的情况下,windows只会给你一个出错提示,没有任何有用信息。这次我使用的Windbg,使用"Windbg -I"设置成默认的事后调试器。

当崩溃发生的时候,自动打开windbg:


从上面的出错信息,可以得到以下几点信息:
1.崩溃进程是iexplorer.exe(使用lmf命令)
2.崩溃的模块是AddContact.dll(这个正是定制的DLL)
3.崩溃的指令是mov ecx,dword ptr [eax],这条指令相当于ecx = *eax;但是由于eax =0 ,导致了一个空指针访问,从而崩溃了。

让我们看看为什么eax会等于0,反汇编看看更多
可以看出eax指向了ebp+8的地址,我们知道vc的函数调用堆栈是:(可以参考我以前的一篇文章:vc6函数调用浅析
ebp+8实际上函数的第一个参数。

接下来我们使用kb命令来看看crash stack:(要先设置好symbol的path,我没有AddContact.dll的pdb), 发现第一个参数确实为0

我们必须要看上一帧的调用情况,从上图可以看出返回地址是0b163df8(AddContact的基址是0x0b160000,偏移量是3df8)。
我标记了关键的四条指令:
mov     esi,dword ptr [ebp+8]
mov     eax,dword ptr [esi+0B4h]
push    eax
call    AddContact!DllUnregisterServer+0x640a (0b16755f)
可以看出esi指向的第一个参数,eax指向的是参数的B4偏移处,而这个eax正是作为第一个参数(记住函数调用是从右向左入栈)


从前面的kb命令可以看出第二帧的第一个参数是0080cd88,使用db命令来查看内存,奇怪的是这个值并不为0!

【尝试解决问题1】
我的目标是让程序不再崩溃,所以我能不能把崩溃的指令去掉来解决问题呢?使用OllyDBG来改改看吧。我们把函数调用的部分都是NOP来填充。然后右键使用[copy to executable]来另存为一个新的DLL

确实,使用这个patch之后,确实不会再crash,能正常显示页面,但是仍然无法添加好友。
所以简单的屏蔽崩溃指令,并不能解决问题,我们必须寻求深层次的崩溃原因

【调试2】
从上面的调试结果看,最大的疑点是为什么崩溃帧的第一个参数变成0,这也是crash的最根本原因。因为刚才我们使用的都是事后调试,无法跟踪得到更详细的信息。
这里遇到一个比较棘手的问题,因为新打开的IE窗口是一个新建的进程,我们根本来不及attach到该进程,它已经崩溃了,我们如何调试呢?有一个办法,是attach到该进程的父进程,然后使用" .childdbg 1"来调试子进程。

使用process explorer可以看出,该浏览器的父进程为DCOM的service进程,我们attach到这个进程


使用childdbg 1命令:


果然当新窗口打开的时候,windbg捕捉到。

下一步我们要去设置断点,我想在AddContact 偏移处3de0的设置断点,但是由于iexplorer进程刚刚被启动,AddContact模块设置还没有被load,不知道基址,无法设置断点。(由于安全的原因,windows 7的module每次被load到一个随机地址).

我先使用windbg设置一个Module Load的命令,当load AddContact停下来,这样我就可以设置断点。

在windbg中设置Event Filters:


果然停下来了:


可以看到AddContact.dll 的基址为0x0b9e0000,设置断点 "bp 0b9e3de0",然后输入g执行到断点。然后输入t单步调试


发现确实eax不为0,所以传入的参数并不为0,但是后来被改变了。
如何跟踪栈上面的数据变化?我们使用ba命令来监视,这样一旦有人改变,就能停下来让我们知道:


继续执行,发现在执行COM QueryInterface 的时候改变了那个参数的值。


看看汇编代码,可以发现正是这句话让第一个参数变为0,而返回值80004002,这是一个COM 的错误值,找不到interface:

看看堆栈信息,可以发现要查找的COM interface为{e1af1028-b884-44cb-a535-1c3c11a3d1db}


通过google,我们发现这个interfaces是windows communcaitor的IMessengerGroup

所以根本原因是找不到这个interface,为了验证,我在注册表的HKCR\Interface下面查找此键,果然没有发现,在正常的机器上却能找到。我怀疑是windows update的时候这个interface被移除了,所以我们只要重新注册这个interface就可以了。

【总结】
1. 利用.childdbg命令调试子进程
2. Event Filters可以有效的帮助调试DLL
3. ba命令是神器,帮助你监控数据
4. 调试要胆大心细,不放过任何细节,真相就在下一秒,坚持....


Feedback

# re: debug to fix crash  回复  更多评论   

2012-04-27 16:41 by jianc
楼主NX

# re: debug to fix crash  回复  更多评论   

2012-04-28 09:52 by flyliying
楼主功力深厚

# re: debug to fix crash[未登录]  回复  更多评论   

2012-04-28 16:01 by sand
太牛了

# re: debug to fix crash[未登录]  回复  更多评论   

2012-04-29 20:49 by jans2002
WinDbg的精彩教程!

# re: debug to fix crash  回复  更多评论   

2014-03-24 10:50 by debug
相当专业

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理