-
-
[翻译]手把手教你修复被破坏的堆栈
-
发表于: 2019-9-27 22:16 8687
-
English: http://blogs.microsoft.co.il/sasha/2011/07/20/manual-stack-walking/
栈被破坏了可一点都不好玩儿!尤其是当你在分析crash dump或者程序发生异常的时候,我猜首先要做的事,可能就是先查看一下儿堆栈调用。
但是发现当前线程的栈被破坏了,你的主要分析工具也无法显示堆栈,这可咋办哩?
尽管如此,有时候也可以修复被破坏的堆栈。我已经出了一些关于.NET和C++调试的教程,但是大家的要求,我也会再展示一个例子
.net 调试:http://sela.co.il/syl/syllabus.aspx?CourseCode=DNDebug
c++调试:http://sela.co.il/syl/syllabus.aspx?CourseCode=CPPDbg
你可以从http://s.sashag.net/ocrImV这个网盘中下载本次例子中需要的dump文件、符号文件以及程序源码文件!
OK,我们使用WinDbg(32-bit)打开dump文件,获取如下信息:
看到当前指令的地址是0x00000000的时候就猜到一定不是什么好事儿,意味着指令指针寄存器(EIP)已经被破坏了。同时你也可以看到EBP也被破坏了,EBP的值也是0x00000000,
这也是为什么k命令无法显示任何堆栈信息的原因。
好在ESP寄存器的值看起来是个正常的值,也不能确定这个值一定正常的,但是我们可以尝试读取ESP指向的内存,我们我们能正常读取它指向的内存数据,那几乎100%确定,ESP仍指向的是栈地址。
因为这是一个mini-dump文件,只包含和栈相关的内存数据。
如果ESP确实指向原始栈地址,我们可以尝试手动查看栈上的数据,寻找和返回地址相似的数据。在返回地址前面,我们应该能看到一个之前PUSH到栈里面的EBP的值,这个EBP是上一个函数的EBP。
其实每一个返回地址前面的EBP都保存的是上一个函数的EBP地址,除非开启了栈帧优化(FPO),关于FPO我打算以后再写一篇文章专门来介绍。
关于FPO以及其历史的介绍,网上有一篇文章讲的很详细: https://blogs.msdn.microsoft.com/larryosterman/2007/03/12/fpo/
如果你忘记了堆栈的内存布局,可以来复习一下Intel x86 函数调用约定: http://www.unixwiz.net/techtips/win32-callconv-asm.html
下面是ESP包含的原始栈信息,这时可以设置好指向BatteryMeter.pdb文件的符号路径,方便使用dds命令时正确的显示符号信息:
首先很高兴能看到ESP指向的内存是dump文件中的有效内存,说明我们正在查看堆栈。这里有几个地址很有可能是返回地址,并且紧接在它们前面的地址就是上一个函数堆栈的EBP。
我们可以挨个检查这些保存在栈上的EBP值指向的内存,如果是在栈上,说明这个栈是可以被我们看到的。
我们可以一直这样遍历EBP,直到最后一层栈帧,根据目前已经获取的堆栈信息,我们发现在栈被破坏之前,有个名为RecurseDeep的函数,一直在调用自己,至少调用了4次。
WinDbg有一个命令可以帮我们来重建堆栈,我们只需要猜测一下ESP、EBP、EIP的值,它就会按照我们给定的值去构造一个合理的堆栈。
我们可以假设EBP就是我们刚才找到的第一个在栈上的EBP的值,EIP可以假设为刚才EBP后面的返回地址,而ESP可以和EBP的值一样,然后就可以看到Windbg输出以下信息:
此时,我们已经利用已知极少的信息,将一个被破坏堆栈恢复成了正常的调用堆栈的样子,并且帮助我们定位了导致堆栈被破坏的罪魁祸首!
查看BatteryMeter!RecurseDeep的源码,就可以找出导致堆栈破坏的根本原因:原来这个破坏堆栈的函数,并没有破坏自己的栈帧,而是返回到了之前函数的栈帧的地方,并用零覆盖了一小块内存区域
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)