Vor*_*Dev 4 x86 assembly return x86-64
我最近在 x64 上尝试堆栈溢出练习。在 x86 上执行此操作时,我预计垃圾覆盖地址会出现以下情况(例如“AAAA”):
ret
,(覆盖的)返回地址将(有效)弹出到 EIP 寄存器中在 x64 中,这似乎有所不同(除了上述步骤中 EIP 与 RIP 的互换之外)。当提供“AAAAAAA”垃圾地址时,处理器似乎会在弹出该地址之前进行一些有效性检查。通过观察,在加载地址之前,似乎要求地址的两个最高有效字节为空。否则,会发生段错误。我相信这是由于 x64 中使用了 48 位寻址,但我的印象是以 0xFFFF 开头的地址也是有效的,但这也会产生段错误。
这是对差异的准确描述吗?为什么这个检查是在数据加载到RIP寄存器之前执行的,而另一个有效性检查是在数据加载到RIP寄存器之后执行的?这些说明之间还有其他差异吗?
编辑:为了澄清我的观察,我注意到当提供 8 字节返回地址时,RIP 仍然指向指令的地址ret
,并且 RSP 仍然指向段错误时被覆盖的返回地址。当提供 6 字节返回地址时,当观察到段错误时,覆盖的地址已被弹出到 RIP 中。
有趣的是,RSP 在故障发生之前没有得到更新。因此,故障并非来自非规范地址的代码提取,而是指令ret
尝试将 RIP 设置为非规范地址。
这使得整个 RET 指令出现故障,这意味着它的任何效果都不可见。(因为英特尔的手册没有定义任何部分进度/更新的内容,即使是针对 的故障行为ret
。)
不幸的是,英特尔手册中的操作部分ret
是一堆条件语句,因为它们使用一个块来记录近和远,以及模式和操作数大小的每一种组合。ret
64位模式下的Plain是“IA-32e模式”,操作数大小=64,“near”(不将CS更改为不同的代码段,只是更改RIP)。
这样的话,x86-64正常ret
基本上就可以了pop rip
。
32位模式正常ret
基本就可以了pop eip
。
不多不少。 RIP = *RSP++
。
也可以看看
请注意,规范范围的上半部分始于0xffff800000000000
: 48 位符号扩展至 64。 0xffff7f0000000000
不是规范的。高 16 位必须与第 48 位匹配。
一般来说,x86-64 的设计似乎是为了使 RIP 真正可以仅与虚拟地址宽度一样宽,即 48 或 57 位,而无需保存非规范地址。任何将 RIP 设置为非规范值的尝试都会在尝试时出错,而不是在稍后获取代码时出错。
归档时间: |
|
查看次数: |
3158 次 |
最近记录: |