前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一条指令引发的血案···

一条指令引发的血案···

作者头像
轩辕之风
发布2024-07-15 13:44:17
680
发布2024-07-15 13:44:17
举报
文章被收录于专栏:编程技术宇宙

新的一周大家好啊,又要开始搬砖了。

今天给大家分享一个有趣的问题,来增长一点知识。

这是我的从零开始学逆向学习群里一位小伙伴(歌虽无形)遇到的问题,后来研究清楚后,我让他总结分析了一下,我稍微改了一下,分享给大家。

事情是这样的:

公司采购了新软件,想在新软件下沿用旧的一些插件和功能,于是就将旧版本的dll直接替换了新版本。

然而,诡异的现象出现了,win7下能正常运行,但win10却怎么也跑不起来,一运行就会会闪退。

学习逆向,当然要自己来动手解决这个问题,是时候展示真正的技术了。

经过分析,发现了崩溃的地方,有个函数入口的地方反汇编后是长这个样子的:

注意看,入口处,函数开辟栈帧,把栈顶抬高了0x298的大小,使用了sub rsp, 298h指令。

然后是函数结束返回的地方,要恢复栈空间,使用了add, esp, 298h指令:

聪明的你一定已经发现问题了,怎么开始是用的rsp寄存器,后面用的是esp寄存器呢?

PS:esp是x86架构CPU的栈指针寄存器,rsp是x64架构CPU的栈指针寄存器。x64兼容了x86指令集,可以通过esp访问rsp的低32位。

先别往下看,你先猜一下,这里会出问题吗,为什么会出问题呢?

由于前面正好有一堆nop,于是将紧邻着的一个nop从0x90改为0x48,这样一来,就把add esp, 298h这条指令,改成了add rsp, 298h了。保存,然后成功修复了~然后很得意的在群里吹水去了~

但事情并没有结束。过了几天总结文档时,突然意识到:不对!有问题!

修复程序的当时我给开发那边给出了一个解释,是恢复栈指针时出现了错误导致堆栈不平衡。但现在仔细想想,简简单单一句“堆栈不平衡”完全解释不通。

原因是,win7能跑,意味着在win7下add espadd rsp结果是一致的,也就是栈顶指针的高32位空间没变动(或者变动了,但变动前后是一样的),而win10下却变动了。

没有头绪,找风哥帮忙。在风哥建议下又分析了一次。先找出之前的程序崩溃转储,用windbg再看看有没有什么蛛丝马迹。

dump分析出来是这个样子的:

经典的0xC0000005 错误码。但除此之外也似乎没有别的问题了?等等,再看一下寄存器:

发现了一个很可疑的点,在执行了add esp, 298之后,相比rbp,rsp丢失了高32位,这也就是说……高32位被置零了?

再来动态调试验证一下,执行add esp, 298之前:

执行add esp, 298之后:

这里就很明确了,确实在执行add esp后,高32位被置零了。

这是为啥嘞?难道在64位系统下,操作32位寄存器会将64位的高32位置零吗?

好像……确实如此!

风哥做了试验,肯定了这个现象。

继续寻找资料,微软文档里,介绍x64处理器的体系结构时,提到了这么一句:继续寻找资料,微软文档里,介绍x64处理器的体系结构时,提到了这么一句:

换句话说,操作32位寄存器时会自动将64位的寄存器进行0扩展(即填充0),而16位和(高低)8位则不会。

x86-64 Tour of Intel Manuals (x86asm.net)的文章中,记录了这么一段话:

Perhaps the most surprising fact is that an instruction such as MOV EAX, EBX automatically zeroes upper 32 bits of RAX register. This doesn't happen with instructions that only read destination registers, like TEST EAX, EBX. In this case RAX remains unmodified. There is one exception to this rule, CMOVcc instructions, for example CMOVBE. These instructions zero upper 32 bits even if the condition is false, when the move doesn't occur. Another surprising fact is that it is impossible to use 8-bit registers AH, CH, DH, and BH together with one new feature of 64-bit mode, in instruction that requires REX prefix. For example, instruction MOV AH, SIL cannot be encoded in 64-bit mode, because SIL register requires REX 40 prefix. The reason for this is that any of REX prefixes cause remapping of original registers AH, CH, DH, and BH to SPL, BPL, SIL, and DIL: -- 88 EC MOV AH, CH 40 88 EC MOV SPL, BPL

看来在64位系统下,直接操作32位的寄存器是有不少问题的,如果存在对寄存器值的直接操作,那么就会导致高32位清零;如果不存在对值的修改,只是读取,则可能不会出现这种情况。

至此,这个问题就得到解答了~原因就是add esp操作会将rsp的高32位置零,导致栈顶指针变成了一个“野指针”。

等等,还有个问题:为什么win7下能正常跑?这个规则是指令集规范,win7下应该也是一样的吧?跑了一下,win7下是这样的:

哈哈,win7下的栈指针,高32位本来就是0,当然不会有事了~

你有没有猜对呢?

最后,我还有一个问题,这是怎么编译的,是什么样的编译器,入口使用rsp,结尾使用esp?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程技术宇宙 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档