通过公司内部培训交流以及网上技术沟通得知,很多C++软件开发人员都没有用过GDIView、ProcessExplorer、ProcessMonitor、APIMonitor、Windbg、IDA等高效分析工具,甚至都没听说过,他们分析排查问题的方式还停留在用IDE调试和查看打印日志的老方法上,效率相对较低,所以很有必要给大家普及一下这些高效的分析工具,大家都值得拥有!希望大家在今后的开发工作中能将这些工具用起来,去有效地提升软件开发调试的效率。
有人可能会问,你怎么了解到这么多工具的?其实这些技能都是通过项目实践不断积累得来的,有的是大家都用的,有的是同事或朋友推荐的,有的是网上搜索问题时别人建议的。我已经熟练掌握这些工具的使用,已经将这些工具全面地应用到商业项目的问题分析中,解决了开发调试中遇到的多个问题,极大地提升了分析问题和解决问题的效率。
SPY++是微软IDE软件VisualStudio自带的一个小工具(spyxx.exe),该工具可以查看当前系统中打开的所有窗口的信息(包含显示和掩藏的窗口),在做UI界面编程分析窗口问题时该工具比较有用。
SPY++的主界面如下所示:
使用该工具,我们可以来查看目标窗口的位置、大小、窗口样式、父窗口等信息,如下所示:
对于可见的窗口,可以直接将探测按钮拖动到目标窗口上:
直接查看目标窗口的信息。
对于掩藏的、不可见的窗口,也可以直接在SPY++主窗口中的窗口列表中以窗口标题、类名、窗口句柄等关键字去搜索目标窗口,搜索到后查看窗口属性即可。
这在分析窗口异常崩溃的问题会很有用。
DepenencyWalker是微软提供的一个库依赖查看工具(depends.exe),该工具可以查看二进制文件exe和dll动态库的导出接口信息,也可以查看二进制文件的库依赖关系。这个工具虽然很小,却很实用,是分析软件因为库依赖问题导致的启动报错的最直接工具。该工具是早期VisualStudio自带的工具,现在的VisualStudio不再打包该工具了,可以到DepenencyWalker官网上去下载。
DependencyWalker的主界面如下所示:
2.2.1、排查启动报找不到dll库的问题
一个exe程序的启动过程大概是这样的:
用户启动exe程序时,系统会分配对应的进程空间,首先去读取exe文件中的PE头信息,读出exe主程序依赖的所有dll库信息,然后系统将这些依赖的dll库加载到当前进程空间中(加载到进程空间的dll库包括exe开发者开发的dll库,也包括依赖的一些操作系统的dll库),待依赖的库完成加载之后,再去加载exe文件,然后进入exe文件中的main函数,这样exe程序就启动起来了。
如果加载依赖库时找不到依赖库文件,或者找到的依赖库的版本不对,会直接弹窗报错,提示程序启动失败。DepenencyWalker工具正是用于排查这类问题的,比如程序启动时缺少库报错、在某个dll库中找不到某个接口报错等。
以以前遇到的一个问题为例,程序启动时报如下的错误:
提示找不到MSVCP100D.dll运行时库(D对应debdebug版本的运行时库),程序是release版本的,怎么会依赖debug版本的运行时库呢?估计是底层依赖的dll库(底层的库包括底层的业务库、网络库、协议库、组件库、音视频编解码库等)发布的有问题,错误地把debug版本的库发布到我们release版本的exe产品流中了。
于是用DependencyWalker打开exe程序,查看其依赖的库,看看哪个库依赖了的debug版本的MSVCP100D.dll,可以查到:
是截图中的dll库依赖了debug版本的MSVCP100D.dll,找到该问题库的维护组,让他们重新发布release版本的库即可。
2.2.2、排查动态启动的dll库启动失败的问题
注意打开ProcessExplorer后,要点击上方工具栏中的ViewDLLs图标按钮才能查看目标进程加载的dll列表,然后在该列表中查看目标库有没有启动起来。
假设a.dll没有启动起来,有可能其依赖的库找不到,也可能该库中调用的接口在其依赖的库中找不到。我们再用DependencyWalker直接打开a.dll查看其库依赖的情况即可找到原因。如果库有问题,在其前面会显示红色或黄色警告图标:
黄色图标表示系统中找不到该库,红色图标表示某接口在某个库中找不到。
这里需要注意一下,DependencyWalker打开目标文件后,默认会展开目标文件依赖的所有的库,其中包括很多Windows系统库,我们要通过经验加以甄别,将Windows系统库都折叠起来跳过去(要熟悉一些常见的Windows系统库,比如ntdll.dll、user32.dll、kernel32.dll等。),一般问题都出在我们应用程序的业务库中的。
此外,还有一些api-ms开头的库,在DependencyWalker中显示黄色图标,一般是没问题的。这些api-ms开头的库,一般是应用程序开发者需要打包到产品的安装包中的,这些库在安装时会自动释放到程序的安装路径中的,以腾讯会议的安装目录为例可以看到这些库:
最后有一点需要说明一下,DependencyWalker在win10系统中打开文件时特别慢,有可能是DependencyWalker版本比较老(好几年前出的版本),对win10系统的兼容性不好导致的,打开文件时可能要等上好几分钟才能打开。在这种时候,需要耐心等待一会。
该工具主要用来查看剪切板中包含了哪些剪切板格式的数据,在处理数据的复制与粘贴时很有用!在XP系统中,该工具是系统自带的,但自win7系统开始就不再带该工具了,需要的话可以到XP系统中拷贝出来,也可以到网上去下载。
有人可能会说,这都什么年代了,谁还用XP系统啊!确实,现在确实很难找到还使用XP系统的电脑了。不过我们在2009年刚毕业那些年,一直用的还是XP系统。然后出现了Win7系统,并逐步普及,到最近几年已经完全普及了Win10系统,新版本的Win11都出来了。几年前,微软甚至还说Win10将是Windows系统的终极版本,结果微软还是食言了,近年又搞出了Win11。
如果我们要知道某种自定义剪切板格式对应的数据内容与格式,可以编写类似如下的测试代码,去获取指定剪切板格式的数据内容:
GDIView是nirsoft公司开发的一个用来显示每个进程使用的GDI对象句柄数的工具,它显示了每种GDI对象的数量以及整个进程占用的GDI对象总数,主要用来帮助开发人员追踪软件中的GDI句柄资源泄漏问题。可以自行到GDIView的官网上下载该工具。
GDIView工具的主界面如下所示:
GDI对象是什么:
GDI是GraphicsDeviceInterface(图形设备接口)的缩写,UI界面应用程序在绘制图片、绘制直线等图形、输出文字时都会用到GDI对象。常见的GDI对象有Pen(笔)、Font(字体)、Bitmap(位图)、Brush(画刷)、Region(区域)、DC(设备上下文)等。
GDIView工具在排查GDI对象泄露问题时,非常好用。
2.4.1、GDI对象泄漏
在Windows系统中,一个进程占用的GDI绘图对象不允许超过10000个,当接近或超过10000时程序绘图就会出现异常,甚至出现崩溃闪退的问题。
当程序绘图出现异常时,可以先在Windows系统自带的任务管理器中查看目标进程的GDI对象总个数:
如果GDI对象数接近或者达到10000个,则说明程序中肯定有GDI对象泄露了。然后再使用GDIView工具去查看哪些GDI对象在持续增长,没有释放,这样在排查代码时就比较有针对性了,就能很快定位问题了。
2.4.2、GDI对象泄漏示例
举一个简单的内存泄露的例子,如下所示:
HPENhPen2=::CreatePen(PS_SOLID,5,RGB(255,0,0));HBRUSHhBr=::CreateSolidBrush(RGB(0,255,0));::SelectObject(hMemDC,hBr);HPENhOldPen=(HPEN)::SelectObject(hMemDC,hPen2);::Rectangle(hMemDC,0,0,20,20);::SelectObject(hMemDC,hOldPen);代码中创建了一个pen对象和一个brush对象,结果在这两个对象使用完毕后没有将这两对象给delete掉,这样就导致了GDI对象的泄露。如果上述代码在程序运行的过程中被频繁的执行,则pen对象和brush对象会持续的增长,使用GDIView工具就能看到这两个GDI对象数在持续的增长。
2.4.3、GDI对象泄漏项目实例
以前帮公司webrtc开发维护小组排查过一个DC对象内存泄露的例子,在他们的代码中调用GetDC去获取窗口DC对象,结果在使用完成后没有调用ReleaseDC将DC对象释放掉,问题代码如下:
#ifdefined(WEBRTC_WIN)//修正程序开启DWM导致的鼠标位置问题intdesktop_horzers=GetDeviceCaps(GetDC(nullptr)DESKTOPHORZRES);inthorzers=GetDeviceCaps(GetDC(nullptr),HORZRES);floatscale_rate=(float)desktop_horzers/(float)horzers;relative_position.set(relative_position.x()*scale_rate,relative_position.y()*scale_rate);#endif就导致了DC对象的泄露,导致他们的程序在运行一段时候后程序的窗口显示异常(窗口绘制异常),紧接着程序就发生了崩溃。修改后的代码如下:
#ifdefined(WEBRTC_WIN)//修正程序开启DWM导致的鼠标位置问题HDChDC=::GetDC(nullptr);intdesktop_horzers=GetDeviceCaps(hDC,DESKTOPHORZRES);inthorzers=GetDeviceCaps(hDC,HORZRES);floatscale_rate=(float)desktop_horzers/(float)horzers;relative_position.set(relative_position.x()*scale_rate,relative_position.y()*scale_rate);::ReleaseDC(nullptr,hDC);//增加了这一句,解决GDI对象泄漏问题#endif我们查到有GDI对象泄漏问题,因为之前的版本是没有GDI泄漏的,并且查看svn代码提交记录确定上层最近没修改过与GDI有关的代码,而底层的webrtc库最近修改过几个问题,所以怀疑是webrtc库修改的代码导致的。但维护webrtc的同事说他们新增的代码并没有操作GDI对象去绘图,应该不是他们的问题。后来不得已去他们那边查看webrtc开源库的修改记录,看到了上述有问题的代码,然后他们才承认是他们新增的代码引起的。这可能也是和他们很少搞UI编程有关系吧,对GDI对象泄漏问题不太敏感。
这是大家的思维定势,软件出问题了,最开始总是说这肯定不是我们模块的问题,应该是其他组维护的模块引起的。
ProcessExplorer是Winternals公司(该公司已经被微软收购)开发的增强版任务管理器软件工具(procexp.exe),功能要比Windows系统自带的任务管理器的功能多很多。使用该工具可以查看服务、进程、线程、模块、句柄、磁盘使用状态、网络使用状态等多类信息。ProcessExplorer和下面要讲到的ProcessMonitor都是Winternals公司开发的免费工具,被放置在SysinternalsSuite工具包中。
SysinternalsSuite包含了一系列免费的系统工具,其中有大名鼎鼎的ProcessExplorer、ProcessMonitor、FileMon、RegMon等,如果把系统管理员比喻成战士的话,那么SysinternalsSuite就是战士手中的良兵利器。熟悉和掌握这些工具,并且对Windows的体系有一定的了解,将大幅度的提高日常的诊断和排错能力。
ProcessExplorer的主界面如下所示:
微软在2006年7月收购了Winternals公司,更重要的是,微软通过此并购网罗了该公司的两位重量级创始人MarkRussinovich及BryceCogswell。其中,MarkRussinovich目前担任微软Azure云计算CTO首席技术官。MarkRussinovich在微软开发了非常多的系统工具,比如winobj、sysmon、diskmon和进程监视器,同时他还著有著名Windows内核书籍《WindowsInternals》(中文名为:深入解析Windows操作系统),慢慢地MarkRussinovich已经成为微软的象征。此外,MarkRussinovich还精通逆向工程,震惊世界的索尼BMG光盘复制保护丑闻就是他发现的。
注意,在SysinternalsSuite工具包中,ProcessExplorer缩写成procexp.exe,ProcessMonitor缩写为procmon.exe。
2.5.1、使用ProcessExplorer查看进程的详细信息
1)查看进程启动时传入的命令行启动参数;2)查看进程加载了哪些dll库(通过该功能确定目标dll有没有启动起来),以及这些库的详细信息;3)查看进程的磁盘和网络使用统计信息;4)查看进程的各个线程的信息,可以看到各线程的CPU占用比例和线程的函数调用堆栈(在排查进程CPU占用高时会用到,找到占用CPU高的那个线程);5)查看进程的GPU占用情况(判断程序是否使用GPU硬件编解码);6)查看进程的虚拟内存的占用情况。7)查看进程占用的句柄信息,比如文件句柄、线程句柄、注册表句柄等。
它是我们在日常工作中使用很频繁的一个工具!下面紧接着讲几个使用ProcessExplorer的常用场景,这些场景在我们的项目都有频繁的使用。
2.5.2、查看目标进程的dll库加载情况
如果要查看进程的库占用情况,需要在第一次启动该工具时点击工具栏中的“ViewDLLs”图标按钮,才会显示目标进程加载的库列表。默认情况下显示的是句柄占用信息。
在进程列表中,点击选择目标进程,下方就会显示加载了哪些库,然后取查看这些库的信息,比如占用库的路径,占用库的版本。通过库的路径或版本,确定是否加载了正确版本的库。
此外,代码中有些库可能是动态加载的,可以在库列表中看目标dll库有没有启动起来(加载到进程空间中),如果没启动成功,一般是库依赖的库有问题,就需要使用DependencyWalker去打开启动失败的库,查看该目标库依赖的库是否有问题。
2.5.3、查看目标进程中各线程的CPU占用情况,排查CPU占用高的问题
除了查看依赖的dll库信息,还可以查看目标进程中的所有线程信息。双击进程列表中的目标进程,在弹出的窗口中点击Threads标签页,就能看到进程中的所有线程的信息。比如我们在排查目标进程的高CPU占用问题时,按照上述操作去查看进程中的线程列表,列表中看到一个线程占用了很高的CPU,如下所示:
应该就是这个线程导致进程的高CPU占用了。于是双击该线程,就看到线程当前的函数调用堆栈了,如下:
2.5.4、查看目标进程的GPU占用情况
双击进程列表中的目标进程,在弹出的窗口中,点击GPUGraph标签页,就看可以看到目标进程对GPU占用的情况:
主要用于判断目标进程是否使用了CPU的GPU硬件单元。在很多视频会议、视频监控的软件中,可能会使用CPU上的GPU单元,进行视频的硬编硬解,从而节省对CPU的消耗。直接使用CPU进行视频的编解码操作,我们称之为软编软解,会消耗大量的CPU资源。如果能使用GPU进行硬编硬解,则能有效地较少对CPU的占用。
2.5.5、推荐一款与ProcessExplorer类似的工具ProcessHacker
ProcessHacker是一款类似于ProcessExplorer功能丰富的进程查看管理工具(ProcessHacker.exe),可以查看服务、进程、线程、模块、句柄、磁盘使用状态、网络使用状态等。ProcessHacker主界面如下:
ProcessHacker界面要更酷炫一些,功能上基本和ProcessExplorer差不多的。此外,ProcessHacker是完全开源的,是一位华人软件开发者开发并开源的,可以到ProcessHacker官网上下载开源代码。
ProcessHacker功能很完备,可以到开源代码中查看诸多功能是如何实现的。
该工具的具体使用方法是,先点击工具栏的漏斗按钮,添加要监测的目标进程:
然后在工具栏将不需要监测的活动类型取消掉:(一般我们只监测文件活动和注册表活动)
2.6.1、监测注册表活动
2.6.2、监测文件活动
比如测试同事在测试时发现软件在桌面或其他路径中生成了一些莫名的日志文件,他们需要定位到日志是哪个模块生成的,需要将生成日志文件的代码清理掉(在桌面上生成一些莫名的日志文件,很奇怪的!),此时可以使用该工具对文件活动进行监测。
通过堆栈中函数调用的上下文,就可以看出日志文件是哪个模块、哪个函数生成的,这样就可以到函数中修改代码,将生成日志的操作关闭掉。
APIMonitor是rohitab出品的一个用来探测进程对API函数调用的工具,使用该工具可以窥探软件在实现一些功能时都调用了哪些Windows系统API函数,通过这些探测出来的API函数去估计出目标软件某些功能的实现方法。不仅可以探测出调用了哪些API函数,还能探测出调用API函数时都传递了什么样的参数,然后可以参照这些API函数及参数去实现类似的功能或者去尝试解决在开发软件过程中遇到的一些问题。
APIMonitor工具的主界面如下所示:
2.7.1、APIMonitor帮我们解决问题的实例
该工具非常的有用,帮我们解决了项目中的多个问题。比如在处理我们软件setup安装包的管理员权限问题、64位系统中创建桌面快捷方式时的图标问题,都是通过该工具对某主流软件的安装包的监测,找到了解决问题的办法。
再比如在实现文件传输的打开文件所在文件夹功能时,使用了该工具对某主流软件和迅雷进行监测,找到了解决问题的办法,甚至还发现了迅雷的一个掩藏很深的bug。
还有一个实例,我们软件在打开chm文件时遇到了一些问题,使用该工具监测了Beyondcompare文件内容比较软件,该软件有打开.chm帮助文档的操作,获得了Beyondcompare软件在调用对应的API函数时传递的参数。参照这个参数,解决了我们软件中的问题。
此外,该工具除了可以监测对系统API的调用,还可以监测对第三方库的导出函数的调用。
2.7.2、APIMonitor的具体使用方法
该工具的具体使用方法以监控某安装包程序为例,先将目标监控程序启动起来,在左侧的函数列表中搜索要监控的API,比如ShellExecute和CreateProcess,将搜索到目标函数都勾选上:
然后在左下侧的进程列表中,选择要监控的进程,点击右键菜单中的“StartMonitoring”即可开启监控。然后让目标监控程序继续跑下去,这样就能监控到目标API函数的调用情况了:
Windbg是微软出品的Windows平台下强大的用户态和内核态调试工具,与微软的IDE工具VisualStudio相比,它是一个轻量级的调试工具。所谓轻量级指的是安装快速轻便,但其调试功能,却比VisualStudio更为强大。
Windbg的主界面如下所示:
2.8.1、使用Windbg分析C++软件异常
Windbg是分析与排查C++软件异常的利器,在排查软件异常与崩溃时非常有用,是我们日常工作中用的最多的工具之一!
通过在公司内部及网上调研发现,很多C++程序员普遍在软件调试与异常分析方面比较欠缺,所以很多人这方面的技能都有待加强。他们分析软件异常问题还停留在很原始的状态,要么通过IDE调试,要么通过分析日志去分析。对于很难复现的问题,则很难去用IDE调试排查,而使用日志去排查效率非常低,很难定位问题。而使用Windbg调试器去分析,可以极大地提升排查问题的效率,有些比较典型的异常崩溃,分分钟就能定位出原因,当然这其中可能需要一些经验。
2.8.2、IDE看不到有效的函数调用堆栈,Windbg动态调试却可以看到
在排查软件异常时,一般是通过发生异常时的函数调用堆栈去分析的,但有时即使能用IDE调试源码复现问题,但却看不到有效的函数调用堆栈,也没法做进一步分析。这类场景我们遇到过很多次了。比如一个最典型的场景,当程序中发生线程栈溢出时,VisualStudio会直接退出调试状态,这样也就无法看到异常发生的函数调用堆栈了。再比如,我们在调试状态发生崩溃,但切换到函数调用堆栈页面,却看不到有效的函数调用堆栈。遇到这些问题时,可以尝试使用Windbg启动目标程序调试运行,问题复现后可能就能看到完整的函数调用堆栈了,问题可能很快就能迎刃而解了。
以上个星期帮新人搭建项目的Debug运行调试环境时遇到的问题为例,环境搭建好后启动Debug调试,结果刚启动程序就发生了崩溃,弹出如下的提示框:
VisualStudio中断下来,将提示窗口关闭,切换到callstack函数调用堆栈窗口中查看函数调用堆栈:
但看不到完整的函数调用堆栈。这个崩溃是必现的,既然VisualStudio看不到有效信息,于是想到使用Windbg启动Debug版本exe程序。
因为是exe启动时的崩溃,所以Windbg一启动exe就产生崩溃,Widnbg感知到了异常并中断下来,输入kn命令查看函数调用堆栈,看到了如下的详细函数调用堆栈:
这个函数调用堆栈时有效的,通过堆栈得知崩溃发生在initglobe函数中,并且看到了代码的行号,于是去查看C++源码最终找到出了问题。
在这个问题中,IDE开发工具VisualStudio在崩溃时看不到有效的函数调用堆栈,但使用Windbg动态调试目标程序复现崩溃时是可以看到的。
2.8.3、Windbg静态分析dump文件与Windbg动态调试
使用Windbg分析软件异常有两种方式,一种是静态分析dump文件,一种是使用Windbg动态调试目标程序(可以直接使用Windbg启动目标程序,也可以将Windbg附加到已经运行的目标进程上)。
对于静态分析dump文件的方式,首先需要在软件发生异常时软件自己自动捕获到,并自动生成dump文件。一般我们需要在软件中嵌入异常捕获模块,可以使用Google开源的CrashRpt库(该开源库是有缺陷的,没法做到对所有模块的异常捕获,可以使用微软的detours开源代码中的技术进行改进),在软件发生异常时异常捕获模块感知到,自动生成包含异常上下文的dump文件。这样我们在事后将dump文件取过来分析就可以了。我们在项目上基本使用这种方式去事后分析异常。
2.8.4、Windbg上手与汇编代码
很多人可能觉得Windbg上手比较难,其实Windbg的入门并不难,只需要掌握常用的Windbg命令,了解函数调用的栈分布图,知晓函数调用的栈回溯原理,基本就能用起来了。但如果要深入分析,就需要有较深的软件开发经验和汇编语言基础了。但要熟练掌握软件异常排查的技能,还是需要不断实践,C++异常崩溃的原因是五花八门的,需要在排查问题中不断的积累、不断的总结!排查的问题越多,积累就越多,就越有心得!在问题中增长经验,在总结中升华技能!这是近几年来做这类工作内容的最大心得!
搞C/C++开发,了解汇编语言,掌握一些基础的汇编知识,是很有必要的。这不仅有利于理解C/C++是如何运行的(有些问题需要从汇编代码的角度去看,从汇编代码的角度才能将问题看清楚),而且有利于从较深层次上分析一些复杂的软件异常。有时在分析一些异常崩溃时,需要查看C++源码对应的汇编代码才能最终搞清楚的。程序最终是崩溃在某套汇编指令上,汇编指令才能最本真、最直接地反映问题的所在。
此外,有时我们为了提高代码的执行效率,会直接在C++代码中嵌入一段汇编代码(可能是嵌入一小段代码,也可能是整个函数都是汇编代码实现的)。之前我在协助音视频编解码组的同事排查异常崩溃时,看到了音视频编解码的部门源码,他们直接将一个频繁调用的函数直接用汇编代码去实现,以提升音视频编解码的执行效率。
有人可能会问,经过IDE编译出来的二进制文件中也都是汇编指令,你人为的添加一段汇编代码,都是汇编代码,为啥会有执行速度上的差别呢?因为源代码经过编译器的处理生成的汇编代码在实现上可能不是最优的,这要依赖编译器,而我们人为地添加汇编,可以直接控制汇编代码,保证汇编代码是最优的,不再依赖编译器。
IDA(TheInteractiveDisassembler)是比利时Hex-Rays公司开发的一款强大的交互式反汇编工具,可以直接打开exe或者dll等二进制文件,查看文件中的汇编代码。
有时在分析一些C++软件异常崩溃时,需要查看C++源码对应的汇编代码才能搞清楚,我们一般使用IDA去查看目标模块的汇编代码。程序最终是崩溃在某套汇编指令上,汇编指令才能最本真最直接地反映问题的所在的。有时要搞清楚代码奔溃的本质,还是要看汇编代码的。
IDA是俄罗斯天才程序员IlfakGuilfanov(伊尔法克·吉尔法诺夫)开发的,IDA由比利时Hex-Rays公司出品,IlfakGuilfanov则是公司的CEO。
俄罗斯盛产天才程序员,Java程序员熟知的IDE工具IDEA(IntelliJIDEA)就是3个俄罗斯天才程序员开发的,他们在捷克成立了伟大的JetBrains公司,该公司除了开发出IDEA,还研发出了Python开发环境PyCharm、Web前端开发环境WebStrom、Go语言开发环境GoLand、php语言开发环境PhpStorm等多个被业界广泛采用的重量级IDE开发工具。
被IT界广泛使用的Web及反向代理服Nginx,是俄罗斯人IgorSysoev(伊戈尔·赛索耶夫)开发的。
在大数据领域广泛应用的高性能数据库ClickHouse,是俄罗斯的Yandex公司开发并开源的,字节跳动公司推出的具有更高性能数据库ByteHouse就是在开源ClickHouse的基础上改进而来的。
俄罗斯除了盛产程序员,还擅长产出顶级的数学家和化学家,华为俄罗斯研究所就招募了不少俄罗斯数学家去做基础研究。
2.9.1、IDA程序图标的由来
IDA的程序图标是世界首位计算机程序员,如下所示:
对,你没看错,她是世界上第一位程序员,女程序员,她叫阿达·洛芙莱斯(AdaLovelace)。IDA软件用她的头像作为图标,目的是为了纪念这位伟大的女程序员。
AdaLovelace设计了巴贝奇分析机上解伯努利方程的一个程序,并证明当时的19世纪计算机狂人巴贝奇的分析器可以用于许多问题的求解。她甚至还建立了循环和子程序的概念。由于她在程序设计上的开创性工作,AdaLovelace被称为世界上第一位程序员。
2.9.2、使用IDA协助排查C++软件异常
在我的日常工作中,IDA主要是来辅助Windbg去分析和排查软件异常问题的。分析问题时,我们用IDA打开二进制文件查看汇编代码,一般是对照着C++源码去阅读汇编代码上下文的。
Windbg中也能查看到汇编代码的上下文,但Windbg中显示的汇编代码没有任何注释,很难将汇编代码和C++源代码对应起来的。一般撇开C++源代码直接去读汇编代码上下文是很难的,除非你有非常强大的汇编功底与反汇编能力。
此外,在release下编译代码时,编译器会对代码进行大量的优化,所以有时汇编代码很难和C++源代码一一对应起来。而IDA在解析出二进制文件的汇编代码时,会添加很多注释,在二进制文件有pdb符号库的情况下,IDA会添加更多的注解(有pdb时,可以看到每个函数名称以及函数中的变量信息),所以使用IDA查看汇编代码上下文要方便多了,能很好地将汇编代码和C++源代码对应起来。
2.9.3、使用IDA查看二进制文件的汇编代码,排查第三方安全软件bug
有次客户那边出现崩溃问题,正是公司的技术大牛直接使用IDA查看第三方安全软件的汇编代码定位出来的。我们的软件部署到某个客户的系统中,会出现频繁的崩溃。客户的安全等级比较高,系统中安装了多个第三方安全软件,这些安全软件一般为了实现对目标程度的监控,都会注入到系统的进程中进行实时监测,我们初步怀疑是安全软件注入引起的。但客户说系统中的其他软件运行都没问题,为啥就你们的软件有问题呢?
如果怀疑是第三方安全有问题,必须拿出证据,否则就是你们的软件有问题。这个问题比较棘手,于是找来了公司的顶级大牛,他直接使用IDA去查看注入模块的汇编代码,最终找到了原因。
其中第5个参数和第6个参数都是指针,微软的说明如下:
第五个参数:[out]fromAnoptionalpointertoabufferinasockaddrstructurethatwillholdthesourceaddressuponreturn.第六个参数:[in,out,optional]fromlenAnoptionalpointertothesize,inbytes,ofthebufferpointedtobythefromparameter.
这两个参数都是指针,都是可选参数,我们在某块代码不用这两个参数,于是都传入NULL,正是这两个NULL参数,导致了第三方安全软件的注入模块产生了崩溃。
通过使用IDA查看第三方安全软件的注入模块中recvfrom函数的汇编代码,函数中没有对该函数的第5个指针参数和第6个指针参数做是否为空的判断(这点太不严谨太不专业了!!!),然后就直接给传进来的参数变量赋值了,这样就操作NULL内存地址,产生了内存访问违例,导致了崩溃。拿出证据后,让客户向第三方安全软件提供商反馈,他们也承认是他们软件有上述缺陷的,并进行了相应的修改。
Wireshark是TheWiresharkTeam团队开发的一款开源的网络抓包软件工具,可以直接抓取网卡上收发的网络数据包,是分析网络问题的必备神器,也是我们日常开发工作中用的最多的工具之一!作为IT从业人员,使用Wireshark抓包进行网络数据分析,是一个基本的要求。
现在的软件系统大多数都是基于网络的,不同形态的客户端软件(客户端软件包括PC端、安卓端、iOS端、嵌入式终端、web端等)需要通过网络与远端的服务器进行通信。网络中使用了大量的网络设备实现互联,网络设备上可能配置了大量的安全规则,基于网络的数据通信会遇到各式各样的网络及数据通信问题。
作为C++软件开发人员,是很有必要了解并熟练使用上述这些高效的工具的。其实,不仅仅是开发人员,这些工具对测试人员也有很大的价值。