《格蠹汇编》调试笔记
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
《格蠹汇编》调试笔记
以前也就是把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模块是没有问题的。
那么我们继续分析栈帧。
简单来说,是ump o这个模块中的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机制。
既然知道了原因,那么我们现在只要把⽂件的名称改短,问题也就能够解决了。