-
-
[原创windbg调试系列——崩溃在ComFriendlyWaitMtaThreadProc
-
发表于: 2019-11-19 18:55 1656
-
前言
这是几年前在项目中遇到的一个崩溃问题,崩溃在了ComFriendlyWaitMtaThreadProc()里,没有源码。耗费了我很大精力,最终通过反汇编并结合原代码才最终搞清楚了事情的来龙去脉。本文的分析还是基于真实项目进行的,中间略去了很多反汇编的分析工作。文末有我整理的测试代码,大家可以实际体验一把TerminateThread()的杀伤力。
背景介绍
大概情况是这样的:程序启动的时候,会通过LoadLibrary()加载插件模块。其中的UIA模块会开启一个工作线程,在工作线程里会安装UIA相关的钩子来监听UIA事件,程序在退出的时候会调用每个插件模块的导出函数做清理工作,然后调用FreeLibrary()释放这个插件模块。UIA模块的清理函数会通知工作线程退出,并等待工作线程一段时间,等待超时就通过TerminateThread()强制杀死工作线程,工作线程收到退出命令后会卸载相关钩子。程序退出时偶尔会崩溃在ComFriendlyWaitMtaThreadProc()中。背景介绍完毕,下面开始分析dump文件。
问题分析
使用windbg载入dump文件,输入.ecxr
从输出结果可以看出是访问到无效的地址0x07acf914了,使用命令!address 07acf914查看该地址的信息:
从输出结果可以看出该地址确实是不可访问的。我们需要看看0x07acf914 是从哪里来的,该值来自edi+4指向的地址所存储的值,那么edi的值是哪里来的呢?让我们看看前几条汇编指令是什么,输入ub 7303f614 L10
说明:
7303f614这个地址是我通过7303f611+3算来的(3是指令长度),这样就可以在输出结果中看到导致崩溃的这条指令啦。当然这里输入ub 7303f611也没关系(我们关心的是edi的值是哪里来的),只不过我们看不到7303f611对应的指令了。
我们发现edi的值来自ebp+8对应的地址内容。研究过反汇编的小伙伴儿应该对ebp+n比较敏感,有木有?在windows下,32位进程中,ebp+8指向了调用约定为__stdcall的函数的第一个参数。ebp+8是否指向了第一个参数,我们需要通过ComFriendlyWaitMtaThreadProc()的调用约定来判断。
输入k查看调用栈:
从调用栈可知,ComFriendlyWaitMtaThreadProc()是在新线程中执行的,通过查看CreateThread()的原型我们可以知道 ComFriendlyWaitMtaThreadProc() 原型应该满足typedef DWORD (__stdcall LPTHREAD_START_ROUTINE)(LPVOID lpThreadParameter); 。
综上可知,ebp+8确实指向了第一个参数,这个参数指向了一个非法的地址!
我猜测有如下两种可能:
调用函数传递了一个合法地址,由于某种原因这个地址无效了。(最后证明,我们的代码里传递了一个栈上的局部变量,但是调用线程挂掉了,栈对应的内存无效了!)
代码中存在bug,传递参数的时候就传的有问题!(可能性太低了,对自己的代码比较有信心
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)