如果 x86-64 中没有兼容模式开关,我是不是保证不会遇到非 64 位指令?
Posted
技术标签:
【中文标题】如果 x86-64 中没有兼容模式开关,我是不是保证不会遇到非 64 位指令?【英文标题】:Am I guaranteed to not encounter non-64-bit instructions if there are no compatibility mode switches in x86-64?如果 x86-64 中没有兼容模式开关,我是否保证不会遇到非 64 位指令? 【发布时间】:2020-10-22 14:05:13 【问题描述】:我知道理论上 64 位程序可以通过将 CS 更改为 explained here 来切换到 32 位模式,我假设这也适用于切换到 16 位模式。
如果我运行一个我知道没有兼容性开关的 64 位程序,我能保证不会遇到非 64 位指令吗?
我知道 66 和 67 十六进制前缀可以在 16 位和 32 位模式之间切换指令(pg 36),但是这些前缀不会出现在 64 位模式中,对吗?
如果我错了,在 64 位执行中我会遇到哪些非 64 位指令?
我的目标是编写一个 x86-64 解码器,我想知道仅处理 64 位指令案例是否足以满足我的用例(64 位程序)。
【问题讨论】:
【参考方案1】:机器代码的每个字节序列要么解码为指令,要么引发#UD
非法指令异常。 CPU 处于 64 位模式,这意味着如果它们没有故障,它们将被解码为 64 位模式指令。另见Is x86 32-bit assembly code valid x86 64-bit assembly code?(不,不是一般情况)。
如果它是一个由编译器发出的普通程序,它的机器代码中不太可能有任何非法指令,除非有人使用内联 asm,或者使用你的程序反汇编非代码部分。或者一个将部分指令置于实际跳转目标之前的混淆程序,因此简单的反汇编程序会混淆并使用与其实际运行方式不同的指令边界进行解码。 x86 机器码是非自同步的字节流。
TL:DR:在正常程序中,是的,反汇编时遇到的每个字节序列都是有效的 64 位模式指令。
66
和67
不会 切换模式,它们只是切换那条指令的操作数大小。例如66 40 90
仍然是 64 位模式下的 REX 前缀(用于后面的 NOP 指令)。所以它只是一个nop
(xchg ax,ax
),而不是像inc ax
/ xchg eax,eax
那样在32 位模式 中覆盖它来解码。
尝试用nasm -felf32
组装然后反汇编db 0x66, 0x40, 0x90
,然后用nasm -felf64
来查看相同的序列如何在 64 位模式下解码,而不是在 32 位模式下解码。模式。
许多指令编码在 32 位和 64 位模式下是相同的,因为它们共享相同的默认操作数大小(对于非堆栈指令)。例如b8 39 30 00 00 mov eax,0x3039
是 mov eax, 12345
在 32 位或 64 位模式下的代码。
(当您说“64 位指令”时,我希望您不是指 64 位 operand-size,因为事实并非如此。所有操作数大小从 8 到 64-对于大多数指令,位可在 64 位模式下编码。)
是的,假设用户空间程序不会通过远 jmp
来切换模式是安全的。除非您在 Windows 上,否则 WOW64 DLL 出于某种原因会这样做,而不是直接调用内核。 (Linux 有 32 位用户空间使用 sysenter
或其他直接系统调用)。
【讨论】:
所以要明确一点,我的 x86-64 解码器应该处理 Intel 列为 64 位“有效”的 所有 指令,而不管指令的操作数大小如何?例如,pg 1038 显示 pop 8F 具有有效的 16 位 r/m 模式和“不可编码”的 32 位 r/m 模式,所以我应该只处理 16 位和 64 位 pop 8F 指令? @wxz:是的,完全正确。以上是关于如果 x86-64 中没有兼容模式开关,我是不是保证不会遇到非 64 位指令?的主要内容,如果未能解决你的问题,请参考以下文章