《格蠹汇编》调试笔记

以前也就是把Windbg作为一个调试所写驱动的调试器。只进行源码级的调试,运用的比较浅显。

最近研读张银奎老师的《软件调试》获益良多,刚好与之配套的《格蠹汇编》提供了老师大量的调试经验以及实验环境。不拿来好好实践一番简直浪费。

 

0X01调试笔记之侦查广告插件

俗话说:工欲善其事,必先利其器。对于调试领域来说,更是如此。我们不但要在自己的计算机中安装有WinDbg,还应当使用JIT调试的方法。因为对于Windows系统中的应用程序的崩溃问题,JIT调试是非常有效的办法,而且JIT调试还是Windows系统和WinDbg密切配合,从而实现高效调试的典范。这样,当我们的程序崩溃时,调试器就能够自动地执行,配合以相应的指令,就能够很快地定位程序出错的位置,从而进行修改。

本节叫侦查广告插件,实际上是用JIT确定老发生崩溃的错误框是哪个程序弹出来的。

 

第一步 在注册表中设置JIT调试
我们首先需要设置JIT调试。这里需要打开注册表,对于32位的Windows系统而言,其位置在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug:

这里的Auto表示是否启动JIT调试,如果这个值为1,则是启动JIT调试,如果为0,说明不启动。而下面的Debugger保存的是欲启动的调试器的路径及参数信息。

在执行 AcsVio.exe(人为制造一个崩溃,弹出错误提示框) 之前需设置WinDbug为默认的即时调试器,设置为在cmd行输入C:\WinDbg\windbg_CN.exe -I 命令

 

 

这是我们执行提供的AcsVio.exe(一个崩溃的exe文件)

 

第二步 利用JIT调试来分析软件问题

可以看到WinDbg已经自动弹出了,并且在出问题的位置断了下来。上图中第一个红框说明了程序的问题:Access violation,也就是访问违例。而第二个红框则是给出了出问题位置的反汇编代码。可以看到,程序将0赋给了[ecx],这个地址读写发生了错误,就出现了访问违例的情况。

下面输入指令:
0:000>.symfix c:\Symbols
这条语句用于设置符号路径。然后输入:
0:000>kPL

其中k表示显示栈回溯的信息,P表示将所有参数也显示出来,L用于隐藏原始代码:

 

可以看到问题就在main函数里面。通过栈的回溯,我们就能够看到系统都调用了那些函数,从而进行进一步的分析。

 

0X02从堆里抢救丢失的博客

相信很多使用计算机的朋友都曾经遇到过在编辑文章时,由于自己的不小心或者是计算机的问题,而丢失了刚刚编辑好的文档的情况。 虽然说现在的文字编辑软件一般也渐渐支持了自动存盘的功能,使得出现这样的“悲剧”的几率大大降低了,但是数据丢失的情况,偶尔还是会发生的, 而且自动存盘的功能也会有失灵的时候。一旦出现了这种情况,那么这就需要我们掌握一定的调试技术来尝试恢复这些数据。因为一般来说,这些数据是保存在我们的内存中的, 理论上来说,只要这块内存区域的数据没有被改写,那么我们就有办法找到那些丢失的数据。

 

第一步 打开目标文件

我们首先打开WinDbg,然后选择File菜单下的Open Crash Dump,载入我们这次所研究的文件ieblog.dmp。可见WinDbg已经为我们打开了文件,并且已经停下来了:

虽然说作者是在网上写的博客文章,但是事实上这篇文章的本体依旧是存在于本地计算机内存中的某个区域中的。由于作者很清楚自己的博客中有哪些内容,因此我们可以在内存中检索博文中相应的字符串。这里可以输入如下指令:

>s –u 0 L800 “当年在交大”

上述命令的意思是,以Unicode的形式在内存中查找“当年在交大”这几个字,查找范围是从0到800。

回车后发现WinDbg并没有返回结果,那么说明我们这里所指定的800这个范围太小了,这里修改一下,将上述语句中的800改成8000000,然后再次尝试查找:

 

可以看到这次检索出了很多个结果。之所以能够在内存中搜索到这么多的结果,有可能是作者在写作的过程中对文章进行了多次保存,或者说是系统自动进行了保存,于是就在内存中留下了痕迹。那么我们下一步的操作就是确认究竟是哪个位置保存的是完整的博文。当然这里我们依旧可以使用s -u命令来查找博文中最后的几个字符,从而定位博文的范围。或者可以使用如下命令:

>du 001b5942 L1000

上述命令的意思是从内存地址为0x001b5942的位置开始,以Unicode的形式显示内存中的内容,一共显示800个结果。WinDbg输出如下:

 

 很显然已经找到了!

第二步 提取内存中文章

可以发现,这个地址正是博文真实存放的地址。既然已经在内存中找到了这篇文章,那么下一步就是 将其从内存中提取出来。可以使用如下命令:

>.writemem e:\blog_1.txt 001b5942 L1458

这行命令的意思是,将内存起始位置为0x001b5942处的内容读取出来,一共读取1458个字符,并命名为blog_1.txt,保存在E盘根目录下:

然后来到E盘根目录下,打开blog_1.txt文件:

 

 

可见虽然我们已经把博文提取出来了,但是却是以乱码的形式显示的。而且虽然说是乱码,但是可以发现这些文字已经很接近于我们的中文字了。其实之所以会出现这样的结果,就是因为我们的系统没能有效地识别这些编码。为了解决这个问题,这里可以利用十六进制编辑工具,比如WinHex打开blog_1.txt文件,然后在文件开头加上Unicode的标识符FFFE,这样系统就能够以Unicode字符的形式进行解析了。

 

0X03拯救发疯的Windows 7

在我们的日常生活中,会接触到各种各样的软件。这些软件的编写者在开发该软件时,往往并不会对其做非常全面的测试,这就导致了当这款软件被不同的用户所使用时,不可避免地会出现各种各样的问题。如果只是功能上出现问题,那么危害往往不大。但是如果是软件中存在着致命的漏洞,那么很可能就会被别有用心者所利用,从而危害到用户的计算机系统。甚至还可能出现这样一种情况,有时候用户的一些无心的输入,而软件中没有相应的处理机制,于是也就导致了软件乃至系统的崩溃。那么这些问题,其实都可以利用调试技术进行解决。

 

首先打开WinDbg,然后在File菜单下选择Open Crash Dump,打开WERA7FB.tmp.mdmp文件。此时WinDbg就已经帮我们断下来了:

 

在上图的红框中可以发现,出现问题的位置在280号进程中的2a4号线程。所出现的问题是缓冲区的溢出。由于缓冲区的溢出是与我们的栈空间紧密相关的,因此我们现在可以看一下栈上的情况。在WinDbg中输入以下命令:

>kn

 其中的k用于显示给定的线程中的栈帧的信息,而参数n可以显示帧的编号。WinDbg得到如下输出:

 

可以看到这里出现了几个以Wer开头的函数,wer的意思是Windows Error Report,说明这个进程在终止前调用了WER设施,这正是我们能够得到这个转储文件的原因。8号栈帧中的函数是UnhandledExceptionFilter,这是位于kernel32.dll中的用于处置未处理异常的核心函数,它也是系统在终止掉一个进程前做最后处理的地方,应用程序错误对话框和JIT调试都是从这个函数发起的。

由张银奎老师所编写的《软件调试》的第12章就深入地讨论了这个问题。而在这个函数的下方,一般就是我们所要查找的导致异常的函数位置了。看一下9号栈帧,这个函数的名称叫做_report_gsfailure,其所在的模块为umpo.dll。说到这里我想要强调的是,我们在以后的分析过程中,如果说遇到了我们并不熟悉的模块,那么我建议大家应当查看一下模块的基本信息,可以在WinDbg输入如下指令:

>lmvm umpo

上述命令中lm表示列出模块的信息,v表示将详细信息显示出来,最后的m表示需要进行模块名称的匹配。

WinDbg的输出如下:

 

可见,这里已经列出了关于umpo这个模块的比较详细的信息。我之所以强调让大家多关注一些不太常见的模块,就是因为很多恶意模块往往是“三无”产品,比如对于没有版本号、没有厂家、没有产品名称等,但却存在于Windows的重要目录,那么这个模块就很值得怀疑了。当然这里的umpo模块是没有问题的。

那么我们继续分析栈帧。简单来说,是umpo这个模块中的UmpoAlpcSendPowerMessage函数出现了缓冲区溢出的情况。当这个函数要返回的时候,编译在函数中的溢出检查代码检测出了溢出,于是调用_report_gsfailure函数来报告错误。这种检测溢出的方式通常称为基于Cookie的溢出检查,简称为GS机制。

简单来说,GS机制就是在可能发生溢出的函数所使用的栈帧起始处(EBP-4的位置)存放一个称为Cookie的整数,在函数返回时检查这个Cookie是否完好,如果被破坏了,就说明函数中发生了溢出。部署和检查Cookie的代码都是编译器在编译时加入到函数中的。

因为我们已经知道函数UmpoAlpcSendPowerMessage出现了缓冲区溢出的错误,那么我们可以具体看一下当前的Cookie以及EBP等的值都是多少。可以在WinDbg中输入如下命令:

>dd 009afb30-4 L4

上述命令中的dd表示以双字的形式显示内存中的内容,009afb30表示出问题函数的EBP,减去4则是Cookie值的位置,L4表明显示4个结果。WinDbg的输出如下:

 

 可以看到第一个00640064就是Cookie值,父函数的EBP也是00640064,而返回地址则是006a002e。可以发现,这些值都不像是正规的内存地址,反而像是ASCII码值。看来由于出现了缓冲区溢出的问题,使得一些重要的数据都被冲掉了,变成了其它的数值。下面我们可以看一下出问题的函数的变量空间的情况。那么该如何确定这个范围呢?首先用两个函数的保存有EBP的位置的地址相减,也就是0x009AFB30减去0x009AF924,得出的结果为0x20C。但是对于_report_gsfailure这个函数而言,它的下方还包含有Cookie值以及EBP的地址,一共占据了8个字节的空间,所以还应当用0x20C减去8,也就是0x204,就是问题函数UmpoAlpcSendPowerMessage的变量空间了。那么下面就来看一下这个空间中发生了什么:

>db 009afb30-204 L210

上述命令的意思是以字节的形式显示内存地址为0x009AFB30减去0x204位置的内容,显示210个结果:

 

 

可以看到这里出现了一个超长的文件名,那么我们就可以知道,这就是出现缓冲区溢出问题的源头了。可见上图中第一个红框就是EBP,而第二个红框则是返回地址的位置。现在可以知道,因为Umpo模块中的函数UmpoAlpcSendPowerMessage接收到了一个比预想长度还要长的参数,于是就发生了缓冲区的溢出情况,从而触发了GS机制。既然知道了原因,那么我们现在只要把文件的名称改短,问题也就能够解决了。

 

posted on 2016-07-13 21:10  zbility  阅读(2871)  评论(1编辑  收藏  举报

导航