项目里安装了UIA相关的钩子来监听UIA相关事件,退出的时候偶尔会崩溃在ComFriendlyWaitMtaThreadProc中,如下
从上图可以
看出 是访问到无效的
地址
了,用!address
07acf914
看下地址信息
确实是不可访问的,但是为啥呢?用ub看了下前面的代码
从红框中我们
可以看出edi是指向第一个参数(ebp+8指向第一个参数),用k命令看下调用栈,如下
原来
ComFriendlyWaitMtaThreadProc 是线程回调函数啊,那么edi就指向了传入的参数!通过查看CreateThread的原型我们可以知道
ComFriendlyWaitMtaThreadProc
原型是
typedef DWORD ( __stdcall *LPTHREAD_START_ROUTINE )(
LPVOID
lpThreadParameter
);
综上可知,参数lpThreadParameter出问题了,指向了一个非法的地址!
有两种猜测:
1. 调用线程用了一个栈上的局部变量,但是调用线程挂掉了,栈上的内容无效了!
2. 代码中存在bug,传递参数的时候就传的有问题!(可能性太低了)
单纯的从dump中看不出更多的信息了!于是我决定给
ComFriendlyWaitMtaThreadProc
下断点,
看看是否能找到是谁创建了这个线程!
bu uiautomationcore!ComFriendlyWaitMtaThreadProc
g
断下来后用~*k查看当前所有线程,逐一查看,是否有可疑线程,发现11号线程很可疑
经过一系列的反汇编,逻辑确认,最终确认11号线程是我们要找的线程!
结论:
当主程序退出时,0号线程(
主线程
)做清理工作,会等待11号线程(卸载钩子)一定的时间,如果超时了就将其强行杀死(正是这个TerminateThread的调用导致了崩溃)! 18号线程会用11号线程传过来的线程参数,如果11号线程被意外杀死了,那么11号线程中的操作就是未定义的!!!至此真相大白!(中间还有很多相关细节太琐碎了,这里直接写出了结论)
说明:
uia在装钩子和卸载钩子的时候都会做类似的操作,而且会做很多次,具体没再仔细研究!
高能预警:
不要使用TerminateThread!!!事实再次残酷的证明了,调用TerminateThread不会有好果子吃!