在没有符号和FPO的情况下遍历堆栈(帧指针省略)
下面是应用程序崩溃转储的调用堆栈。报告的崩溃是名为“HelperLibrary”的模块内的访问冲突,我们没有该模块的符号或源代码。调用堆栈看起来不太可能:
0:000> kv
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0028fcec 74ba339a 7efde000 0028fd38 77479ed2
HelperLibrary+0x1014
0028fcf8 77479ed2 7efde000 776a5346 00000000
kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
0028fd38 77479ea5 011212b2 7efde000 00000000
ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
0028fd50 00000000 011212b2 7efde000 00000000
ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])
除了HelperLibrary+0x1014之外,这里没有其他真正的帧,但是我们非常确定堆栈上应该还有其他代码,比如应用程序的主函数
要从这个堆栈重建某些内容,您需要了解谁调用了HelperLibrary+0x1014,即使您没有准确的符号。通常,这是一个遍历EBP引用的问题,但是如果它那么简单,调试器就已经做到了!
好吧,那么EBP怎么样了?
0:000> r ebp
ebp=0034fbfc
0:000> ln ebp
0:000> u ebp
0034fbfc 08fc or ah,bh
0034fbfe 3400 xor al,0
0034fc00 9a33ba7400e0fd call FDE0:0074BA33
0034fc07 7e48 jle 0034fc51
0034fc09 fc cld
0034fc0a 3400 xor al,0
0034fc0c d29e477700e0 rcr byte ptr [
esi-1FFF88B9h],cl
0034fc12 fd std
如果你没有注意到,这不是实际的代码,而是一堆被解释为指令的数据。EBP完全有可能被损坏,指向完全不相关的位置,但也有另一种可能:当前代码正在使用FPO。
什么是FPO?FPO(帧指针省略)是一种优化技术,而编译器使用EBP寄存器作为暂存值来存储杂项数据,就像任何其他通用寄存器一样。如何处理局部变量和函数参数?直接通过ESP。
换言之,当使用FPO(您可以使用/Oy编译开关启用它)时,编译器可以自由地避免使用以前的EBP值创建“真实”堆栈帧。没有从当前EBP值开始的堆栈帧的链接列表。如果没有FPO信息(出现在符号文件中,而我们没有这些信息),调试器将无法执行任何操作。
这使得我们需要反汇编HelperLibrary+0x1014并尝试手动找出它返回的位置。让我们看看HelperLibrary+0x1014(粗体的违规指令)附近:
0:000> u HelperLibrary+0x1014-0x14 L8
HelperLibrary+0x1000:
66951000 56 push esi
66951001 ff157c209566 call dword ptr [6695207c]
66951007 50 push eax
66951008 c60061 mov byte ptr [eax],61h
6695100b ff1578209566 call dword ptr [66952078]
66951011 83c408 add esp,8
66951014 c60661 mov byte ptr [esi],61h
66951017 c3 ret
这看起来,确实,像一个FPO的栈帧-没有EBP被看到。但是,随后的ret指令会转到某个地方,因此我们可以查看ESP并在那里找到返回地址:
0:000> dps esp L1
0034fb9c 66951040 HelperLibrary+0x1040
好的,那么HelperLibrary+0x1040是什么?
0:000> u HelperLibrary+0x1040-0x20 LC
HelperLibrary+0x1020:
66951020 56 push esi
66951021 8bf0 mov esi,eax
66951023 56 push esi
66951024 ff157c209566 call dword ptr [6695207c]
6695102a 50 push eax
6695102b c60061 mov byte ptr [eax],61h
6695102e ff1578209566 call dword ptr [66952078]
66951034 03742410 add esi,dword ptr [esp+10h]
66951038 83c408 add esp,8
6695103b e8c0ffffff call HelperLibrary+0x1000
66951040 5e pop esi
66951041 c3 ret
有点意思。此帧也不使用EBP,因此我们可以预期返回值为ESP+4(因为返回之前的pop指令)。但在这个函数中,我们如何计算ESP的值呢?好吧,假设上一个函数返回。它已经从堆栈中删除了四个字节(返回地址)。ESP的下一个值是ESP+4,我们需要再添加4个字节来解释“pop esi”指令。
0:000> dps esp+8 L1
0034fba4 6695106b HelperLibrary!ImportantFunction+0x1b
好吧!我们正在取得一些真正的进展,我们有一个非常小的偏移量,即使没有符号,ImportantFunction可能是一个导出函数,因此我们在DLL中有它的位置:
0:000> u HelperLibrary!ImportantFunction LD
HelperLibrary!ImportantFunction:
66951050 8b442404 mov eax,dword ptr [esp+4]
66951054 85c0 test eax,eax
66951056 7501 jne HelperLibrary!ImportantFunction+0x9
66951058 c3 ret
66951059 837c240c00 cmp dword ptr [esp+0Ch],0
6695105e 7e0e jle HelperLibrary!ImportantFunction+0x1e
66951060 8b4c2408 mov ecx,dword ptr [esp+8]
66951064 49 dec ecx
66951065 51 push ecx
66951066 e8b5ffffff call HelperLibrary+0x1020
6695106b 83c404 add esp,4
6695106e b801000000 mov eax,1
66951073 c3 ret
这是另一个没有EBP使用痕迹的函数。注意,它有参数,并且它使用来自ESP的直接偏移量来访问这些参数,ESP是FPO的信号。这个函数返回到哪里?好吧,在上一个函数返回后,ESP已经在当前值的ESP+0xC处。ImportantFunction再添加四个字节,然后返回,因此我们需要查看ESP+0x10:
0:000> dps esp+0x10 L1
0034fbac 00ed100f MainApp!wmain+0xf
对!我们离开了动态链接库,回到符号区!因此,重建的堆栈如下所示:
HelperLibrary!…somefunction…
HelperLibrary!…someotherfunction…
HelperLibrary!ImportantFunction
MainApp!wmain
这些都不是调试器提供的。作为参考,这里是相同的调用堆栈与符号(其中包含FPO信息)
0:000> kv
ChildEBP RetAddr Args to Child
003dfee4 668e1040 00000001 668e106b 0000000f
HelperLibrary!AnotherHelperFunction+0x14 (FPO: [0,0,0])
003dfeec 668e106b 0000000f 010c100f 010c20f4
HelperLibrary!HelperFunction+0x20 (FPO: [1,0,4])
003dfef4 010c100f 010c20f4 00000010 00000001
HelperLibrary!ImportantFunction+0x1b (FPO: [3,0,0])
003dff04 010c1191 00000001 00771b78 00771c10
MainApp!wmain+0xf (FPO: [2,0,0])
003dff48 74ba339a 7efde000 003dff94 77479ed2
MainApp!__tmainCRTStartup+0x122 (FPO: [Non-Fpo])
003dff54 77479ed2 7efde000 777db3e9 00000000
kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
003dff94 77479ea5 010c12b2 7efde000 00000000
ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
003dffac 00000000 010c12b2 7efde000 00000000
ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])
在没有符号和FPO的情况下遍历堆栈(帧指针省略)的更多相关文章
- Linux下函数调用堆栈帧的详细解释【转】
转自:http://blog.chinaunix.net/uid-30339363-id-5116170.html 原文地址:Linux下函数调用堆栈帧的详细解释 作者:cssjtuer http:/ ...
- 什么情况下可以不写PHP的结束标签“?>”
我们经常看到有些PHP文件中的代码是只有开始标签,而没有结束标签的,那么什么情况下可以不写这个结束标签,而什么情况下必须写?先来看2个例子: 下面的代码正常运行: <?php echo 1234 ...
- Cygwin 各种情况下中文乱码--终极解决方案
0.引言 本人从进公司以来一直负责公司Android平台下产品的NDK开发,用的工具: 01. Google的adt-bundle(集成了eclipse和sdk) 02. NDK 03. Cygwin ...
- 如何在命令长度受限的情况下成功get到webshell(函数参数受限突破、mysql的骚操作)
0x01 问题提出 还记得上篇文章记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门),我们讲到了一些PHP的一些如何巧妙地绕过数字和字母受限的技巧,今天我要给大家分享的是如 ...
- 一些http或https请求的参数,什么情况下需要urlencode编码
http协议中参数的传输是"key=value"这种简直对形式的,如果要传多个参数就需要用“&”符号对键值对进行分割.如"?name1=value1&na ...
- CentOS 6.5本地yum源、局域网离线yum仓库(断网情况下轻松安装各种依赖包)
在工作中, 公司的服务器大部分都禁止连接外网的,初始化系统,测试某些产品时,往往缺一些软件或依赖包,一个个上传到机器,如此浪费时间,浪费金钱,en...yum能够自动查找并解决rpm包之间的依赖关系, ...
- 介绍下Shell中的${}、##和%%使用范例,本文给出了不同情况下得到的结果。
介绍下Shell中的${}.##和%%使用范例,本文给出了不同情况下得到的结果.假设定义了一个变量为:代码如下:file=/dir1/dir2/dir3/my.file.txt可以用${ }分别替换得 ...
- SQL Server 在缺少文件组的情况下如何还原数据库
SQL Server 在缺少文件组的情况下如何还原数据库 一.背景 我有一个A库,由于a,b两张表的数据量比较大,所以对表进行分区:在把A库迁移到一个新的集群上去,我只备份了A库的主分区过去进行还原为 ...
- 如何在删除ibdata1和ib_logfile的情况下恢复MySQL数据库
昨天,有个朋友对公司内部使用的一个MySQL实例开启binlog,但是在启动的过程中失败了(他也没提,为何会失败),在启动失败后,他删除了ibdata1和ib_logfile,后来,能正常启动了,但所 ...
随机推荐
- Java学习:抽象方法和抽象类的使用
抽象 抽象方法:就是加上abstract关键字,然后去掉大括,直接分号结束.抽象类:抽象方法所在的类,必须是抽象类才行.在class之前写上abstract即可. 如何使用抽象类和抽象方法: 1.不能 ...
- 数据库权限优化,权限设计BigInteger
最近看到了一个项目的权限是根据bigineger来进行计算的菜单权限,觉得还是不错,存储上只需要存储在一个字段里就可以了,通过计算算出该角色的菜单权限即可,效率也非常的快,放在session中也非常的 ...
- c#读取appsetting.json配置文件
asp.net core 取消了web.config配置文件,而将appsetting.json作为了配置文件. 那么,怎么读取相关数据呢?这里我在appsetting.json中添加一些信息 第一种 ...
- 这两个小技巧,让我的SQL语句不仅躲了坑,还提升了1000 倍
原文: https://cloud.tencent.com/developer/article/1465618 本次来讲解与 SQL 查询有关的两个小知识点,掌握这些知识点,能够让你避免踩坑以及提高查 ...
- JAVA中List,Map,Set接口的区别
从三点来分析它们之间的不同: 1.继承的接口不同: List,Set接口都是继承于Collection接口的,而Map接口不是,它是一个顶层接口. 2.自身特点: List:用来处理序列的.对于放于的 ...
- FineReport连接SSAS多维数据库
1.服务器——定义数据连接,如下图: 2.配置SSAS服务器地址及登录名密码: 注意这里的用户名.密码是指的服务器的登录名和登录密码,也就是你远程桌面的登录用户名和密码,不是数据库的登录用户名密码. ...
- linq自定义条件Lambda过滤方法
Public Func<NoramalClass,bool>simpleComare<NormalClass>(string property,object value) { ...
- JavaScript基础内容中的函数详解
函数 函数:即方法 函数就是一段预先设置的功能代码块,可以反复调用,根据输入参数的不同,返回不同的值. 为什么使用函数: 1.方便调用 2.代码重用,利于维护 3.便于修改,便于重构 4.简化逻辑,利 ...
- python3在win10运行CGI
痛苦是保持清醒最好的方式 --秦时明月·奶盖 CGI是什么 CGI是目前由NCSA维护,NCSA定义CGI如下: CGI(Common Gateway interface),通用网关接口,它是一段 ...
- beforeRouteEnter 与 beforeRouteUpdate(watch $route 对象) 的区别
项目 区别 适用场景 网址 beforeRouteEnter beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建.不过,你可以通过 ...