使用Windbg排查C++软件安装包安装时被安全软件拦截导致安装堵塞(线程卡住)的问题
目录
1、问题描述
2、初步分析
3、将Windbg附加到安装包进程上进行分析
4、在Windbg中查看相关变量的值,并设置断点进行动态调试
4.1、在Windbg中查看相关变量的值
4.2、在Windbg中使用bp命令设置断点进行动态调试
5、腾讯电脑管家已经退出,但其实时防护服务还在后台运行,仍然会拦截安装包的注册表操作
6、可以到腾讯电脑管家中查看拦截记录,可以尝试去关闭掉腾讯电脑管家对安装包程序的拦截操作
7、让软件“绕过”360安全卫士或金山毒霸的拦截
7.1、360安全卫士的软件认证检测平台
7.2、金山毒霸的软件认证检测平台
8、最后
C++软件异常排查从入门到精通系列教程(核心精品专栏,订阅量已达600多个,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战专栏(重点专栏,专栏文章已更新480多篇,订阅量已达数百个,持续更新中...)https://blog.csdn.net/chenlycly/article/details/140824370C++ 软件开发从入门到实战(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_2276111.html 同事使用我们最新版本的软件安装包安装软件,但安装进度快要跑满时一直没有结束(安装流程卡住了),于是测试人员找到我,让我去分析一下。经排查,是安装包执行到写注册表的操作时被腾讯电脑管家(安全防护软件)识别为危险的行为,将写注册表的操作给拦截,导致安装线程堵塞卡住,所以安装过程始终没有完成。本文详细讲解一下这个问题的排查过程,详细讲述如何使用Windbg去动态调试目标进程(在Windbg中查看变量的值、使用bp命令设置断点进行调试),并给出软件“绕过”360安全卫士与金山毒霸拦截的有效办法,以供大家借鉴或参考。
1、问题描述
某天同事拿到我们最新发布的软件安装包去安装软件,但安装进度快要跑满时,一直没有结束,等了好久一直卡在最后的那一点进度上,如下所示:
一直没有完成安装,应该是安装过程卡在哪里不动了。这个问题是必现的,在任务管理器中将当前未完成安装的进程强行结束掉,重新发起安装,还是有问题。出问题了,就需要去解决,于是测试人员找到我,让我去分析一下。
2、初步分析
为了更自主的控制软件安装包、更方便地定制安装包的UI界面,我们不再借助InstallShield、Inno Setup、NSIS等专业打包工具,我们自己用代码编写一个安装包程序,实现这些打包工具类似的功能,比如:
1)将要打包的所有文件做成压缩包,作为资源添加到安装包工程中。执行安装时从工程资源中将压缩包取出来,然后进行解压。
2)执行文件的拷贝操作。将打包的文件拷贝到安装目录中,并将部分文件拷贝到系统相应的目录中。
3)注册相关控件。程序中可能包含ocx或者ATL控件,这些控件在安装时需要向系统注册。
4)创建桌面快捷方式和开始菜单快捷方式。
5)向注册表中写入安装卸载注册表信息。比如安装路径、版本信息、软件发布者信息等:(以腾讯会议为例)其中最重要的是安装路径信息,卸载程序时需要从注册表中读取安装路径,将文件从安装路径中删除掉。
在开发安装包程序时,要将上述讲到的将程序文件解压并拷贝到安装路径中、创建桌面及开始菜单快捷方式、注册控件、向注册表中写入信息等安装操作放到一个新开的线程中去执行,不能放在程序的UI主线程中执行。因为这些操作比较耗时,直接放在UI线程中去执行,会导致UI线程的堵塞,导致UI界面的堵塞。然后根据当前完成的操作,去更新UI界面中显示的安装进度。
在本问题中,安装进度卡在最后的那一点进度上,估计是安装线程中执行某一安装操作时发生了堵塞或卡死。根据以往的经验,可能是以原因导致的:
1)可能是线程间发生了死锁,导致线程卡死。以前我们遇到过,安装包中的线程与输入法注入模块中的线程之间发生死锁,或者安装包中的线程与系统库中的系统线程之间发生了死锁。
2)可能是安装包中执行了一些对系统安全敏感的操作,被安全防护软件拦截,导致线程卡死(堵在某个操作上不返回)。比如向系统注册控件、写注册表等,会操作Windows系统的关键位置的信息,电脑上安装的安全防护软件(比如腾讯电脑管家、360安全卫士等)觉得这些操作会对系统安全产生影响,可能会弹出让用户选择是否阻止操作的提示框,或者直接进行拦截(不通知用户)。这个场景我们也遇到过。
3、将Windbg附加到安装包进程上进行分析
安装包中的安装线程发生堵塞卡死,不管是线程死锁引发的,还是安全软件拦截导致的,排查起来很简单,只要将Windbg附加到出问题的安装包进程上,输入~*kn命令,将安装包进程中所有线程的函数调用堆栈都打印出来,就可以确定了。
为了看到详细的函数调用堆栈,需要取来堆栈中相关模块的pdb符号文件,对于本例中的安装包,就一个独立的exe模块,所以主要是exe安装包程序的pdb文件。如果要查看系统模块的具体接口,也可以设置系统库pdb在线下载路径:(下图中粗体部分就是系统pdb在线下载地址)
C:\Users\Administrator\Desktop\pdbdir;srv*f:\mss0616*http://msdl.microsoft.com/download/symbols
之前已经多次讲解如何找到对应时间点的pdb文件以及如何给Windbg配置pdb文件路径,此处就不再赘述了。关于pdb文件的详细说明(包含pdb所有的细节),可以查看我之前写的文章:
【C++软件调试技术】什么是pdb文件?如何使用pdb文件?哪些工具需要使用pdb文件?https://blog.csdn.net/chenlycly/article/details/140742876 对于本问题,是安装线程发生了堵塞,我们首先去查看负责执行安装操作的业务线程的函数调用堆栈,看看卡在哪一步操作上。安装包会包含多个线程(相对于软件进程,安装包进程中的线程比较少,最多也就几个线程),如何确定哪个线程才是负责执行安装操作的业务线程呢?其实很简单,直接查看线程函数调用堆栈中调用的函数就能确定了。
将Windbg附加到出问题的安装包进程上,输入~*kn命令将进程中所有线程的函数调用堆栈都打印出来。然后根据需要,将相关模块的pdb文件找来,设置到Windbg中,然后再输入~*kn命令,重新将所有线程的函数调用堆栈打印出来,如下:
根据函数调用堆栈中调用的函数,一眼就能看出2号线程就是执行安装任务的业务线程。从调用堆栈可以看出, 线程并没有发生死锁,所以大概率是系统中安装的安全防护软件拦截导致安装线程卡死的。
继续查看安装线程中的函数,对照着源码得知:
当前安装线程正在向系统注册表中写入内容,可能是系统中安装的安全防护软件认为这个会危害系统安全,直接将写注册表的操作拦截了,导致安装线程卡住不返回了。
在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)
专栏1:(该精品技术专栏的订阅量已达到580多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章已经更新到200篇以上,持续更新中!欢迎订阅!)
C++软件调试与异常排查从入门到精通系列文章汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/125529931
本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!
考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!
专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!
专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达220多个,专栏文章已经更新到480多篇,持续更新中!欢迎订阅!)
C/C++实战进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。
专栏3:
C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!
专栏4:
VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。
专栏5: (本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,专栏文章已经更新到260多篇,持续更新中!欢迎订阅!)
C++ 软件开发从入门到实战(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。
4、在Windbg中查看相关变量的值,并设置断点进行动态调试
最开始同事反馈这个问题时,我们第一反应可能是安全软件拦截导致的,让其将安全软件退出,再重新安装,看看是否还有问题。
询问同事得知,其电脑上只安装了腾讯电脑管家,没有安装其他的安全软件,并且他已经将腾讯电脑管家退出了,但重新安装时还是卡在最后那一点进度上:
始终没完成。
4.1、在Windbg中查看相关变量的值
这就奇怪了,腾讯电脑管家已经退出了,为啥还有问题呢?于是尝试去看函数调用堆栈中相关变量的值,看看是否在写注册表调用接口时传入了异常的参数值。只要点击堆栈前面的序号超链接,就可以查看当前函数中相关变量的值,点击序号03,果然看到了一个异常值:
这个存放字符串的变量居然显示乱码!这个变量值是主调函数传入的,于是查看C++源码,主调函数传入的是常量字符串:
为啥到被调用函数中就变成乱码呢?太奇怪了!
在Windbg中查看相关变量的值,可能是排查问题的关键线索,我之前写过两篇项目中遇到的实战案例排查的文章,可以查看:
通过查看Windbg中变量值去定位C++软件异常问题https://blog.csdn.net/chenlycly/article/details/125731044通过查看Windbg中变量值去定位C++软件异常的又一典型案例分享https://blog.csdn.net/chenlycly/article/details/125793532
4.2、在Windbg中使用bp命令设置断点进行动态调试
为了搞清楚这个问题,想到可以重新运行安装包,将Windbg附加到安装包进程上,然后在问题函数的入口处设置断点,看看在函数入口处传入的参数值是否正常。
将安装包程序重新启动后:
先不要点击下一步,先将Windbg附加到进程上,然后将pdb设置到Windbg中。然后再使用bp命令设置好断点,最后再点击下一步,让安装包开始安装,然后等待执行到设置断点的函数命中断点,然后就可以查看函数入口处传入的参数变量的值了。
如何在Windbg中动态设置断点呢?其实很简单,使用bp命令即可,该命令的具体格式为:bp 模块名(不带后缀名)!函数名+相对函数的偏移地址,我们想在函数的入口处设置断点,直接使用函数名就可以了,不需要加上偏移地址了。
我们的安装包程序名称是QyLink_V9.0.0.2.0.20241025.exe,我们要在函数CProcessLogic::SetUp入口处设置断点,于是打断点的命令可以构建为:
bp QyLink_V9.0.0.2.0.20241025!CProcessLogic::SetUp
此处注意,因为使用到了函数符号,必须先将pdb文件加载进Windbg中,否则Windbg无法识别安装包程序中的函数符号的,断点会设置失败。
执行上述bp命令后,居然提示断点设置失败:
这就有点奇怪了!提示找不到QyLink_V9.0.0.2.0.20241025模块,安装包exe程序名称就是QyLink_V9.0.0.2.0.20241025.exe啊!
于是查看使用~*kn打印出来的堆栈中显示的模块,果然不是exe名称了,做了一些改动:
使用lm vm QyLink_V9.0.0.2.0.20241025*,查看QyLink_V9.0.0.2.0.20241025.exe模块的信息,也看到了模块名称被改编了:
这还是第一次遇到,居然会改编模块名称,可能是模块名称太长的缘故吧!下次在遇到这类问题,就知道是名称改编引起的了。
然后将bp命令修改成bp QyLink_V9_0_0_2_0_20241025!CProcessLogic::SetUp,然后执行该命令,断点就设置成功了。可以执行bl命令,查看当前都设置了哪些断点:
关于查看Windbg命令的详细说明,可以在菜单栏中点击Help->Index,打开Windbg的chm格式的帮助文档:
输入要查看的命令即可。
断点设置好后,回到安装包的界面,点击下一步,开始安装。当执行到设置断点的CProcessLogic::SetUp函数时,命中了断点,此时输入kn命令查看函数调用堆栈,然后点击堆栈前面的序号,就可以查看CProcessLogic::SetUp函数入口处的相关变量的值了。看到函数入口处传入的变量值是正常的:
为啥运行函数中的若干行代码后,变量值就不正常了呢?最后怀疑可能是腾讯电脑管家安全防护模块拦截搞的鬼!
关于何时使用Windbg静态分析dump文件、何时使用Windbg动态调试目标进程、以及使用Windbg静态分析dump文件与动态调试目标进程的一般步骤,可以查看我之前写的文章:
何时使用Windbg静态分析?何时使用Windbg动态调试?https://blog.csdn.net/chenlycly/article/details/131806819使用Windbg分析dump文件排查C++软件异常的一般步骤与要点分享https://blog.csdn.net/chenlycly/article/details/142970834使用Windbg调试目标进程的一般步骤详解https://blog.csdn.net/chenlycly/article/details/135484906
5、腾讯电脑管家已经退出,但其实时防护服务还在后台运行,仍然会拦截安装包的注册表操作
到目前为止,还是没法确定到底是什么原因引起的。怀疑是安全软件拦截导致的,但电脑上唯一安装的安全软件腾讯电脑管家已经退出了,应该不会存在拦截问题了,但重新安装包去安装,问题还是依旧存在。
后来突然想到,虽然安全软件已经退出了,但安全软件的一些实时防护服务模块一直还在后台运行,以前我们用360时经常看到360都退出了,进程列表中还是能看到360的部分实时防护模块在运行。查看任务管理器,果然看到了腾讯电脑管家的实时防护服务模块QQPCRTP.exe仍然在后台运行:
这个QQPCRTP.exe名称中的RTP,应该是Run-Time Protect(实时保护)的缩写)。到腾讯电脑管家的安装目录中找到QQPCRTP.exe,右键点击该文件,查看文件的属性:
该文件确实是腾讯电脑管家的实时防护服务模块,经确认,即便腾讯电脑管家退出了,这个实时防护模块还在后台运行。
那当前就怀疑这些仍在运行的实时防护模块在拦截,但这些实时防护模块运行的权限比较高(使用系统级的高权限),没法在任务管理器中强行终止这些模块。后来不得已,为了验证是否是腾讯电脑管家拦截的,因为实时防护模块无法终止,那我们就直接将腾讯电脑管家卸载掉,卸载时电脑管家会自动将实时防护模块终止的。卸载后,重新运行安装包进行安装,就能正常完成安装了,果然是腾讯电脑管家捣的鬼,即便将腾讯电脑管家退出了,其实时防护模块还在运行,仍然对安装包写注册表的操作进行了拦截!
6、可以到腾讯电脑管家中查看拦截记录,可以尝试去关闭掉腾讯电脑管家对安装包程序的拦截操作
如果腾讯电脑管家开着,可能会弹出如下的提示框:
让用户选择是否要阻止程序当前的操作。
问题排查出来后,想想也许不用卸载腾讯电脑管家,可以尝试到腾讯电脑管家中查看有没有拦截记录,然后取消拦截也许就能解决问题了。具体找到拦截记录的方法是,先在主界面中,点击右侧的“权限管理”:
在弹出的窗口中,点击左侧的“软件安装提示”,然后点击窗口左下角的“自动拦截/允许名单”:
然后在弹出的窗口中可能能看到拦截记录:
7、让软件“绕过”360安全卫士或金山毒霸的拦截
如果机器上安装了360安全卫士、金山毒霸等安全防护软件,可能也会遇到类似的拦截操作,但360安全卫士与金山毒霸都提供了软件认证检测平台,在发布软件版本之前可以将软件的安装包先提交到这些认证平台上检测,检测通过后360安全卫士、金山毒霸就不会拦截改版本的软件了。目前腾讯电脑管家还没有提供这种开放的软件认证检测平台。
软件认证检测平台会在后台对安装包程序本身以及安装安装包释放出来的软件文件进行详细地安全检测与分析,确保程序中没有威胁安全的代码行为,然后针对当前安装包生成特征码,后面就不会对安装包程序以及安装后的软件进行拦截了!
关于360和金山毒霸拦截的实例以及如何跳过它们的拦截,可以查看我之前写的文章:
如何让我们的软件跳过360和金山毒霸的“随意拦截”?https://blog.csdn.net/chenlycly/article/details/120682579
7.1、360安全卫士的软件认证检测平台
360安全卫士的软件认证检测平台,称为360软件开放平台,地址为:https://open.soft.360.cn/
目前使用360安全卫士等安全软件的用户比较多,所以一般我们会将软件的安装包提交到360软件认证检测平台上做检测,减少360安全卫士对安装包以及安装后的软件的拦截。
7.2、金山毒霸的软件认证检测平台
金山毒霸的软件认证检测平台,称为金山软件认证平台,地址为:http://rz.ijinshan.com/
使用金山毒霸的用户好像不太多,是否要将软件安装包提交到金山毒霸的软件认证检测平台做检测,可以根据需要去决定。
8、最后
本文中的安装包被安全防护软件拦截导致线程堵塞的问题,排查起来不难,但涉及到的点比较多,比如在Windbg中使用~*kn查看安装包进程中所有线程的函数调用堆栈、在Windbg中查看相关变量的值、在Windbg中设置断点进行动态调试(包括在Windbg中使用bp命令设置断点进行调试)、腾讯电脑管家虽然退出了但实时防护模块仍然在后台运行等,有一定的实战参考价值!
此外,在Windbg中设置断点进行调试,在日常工作中有个巧妙的用法,比如有的同事在使用我们的软件时可能会忘记登录密码(非软件的开发人员,没法直接调试代码),可以将软件启动起来,先不要发起登录,然后将Windbg附加到软件进程上,在Windbg中使用bp命令在发起登录的接口中设置断点(该接口中传入的是明文的密码),然后在软件中发起登录,然后命中该断点,Windbg中断下来,这样就可以直接去查看函数中局部变量的值,就可以查看到登录密码了!