我知道理论上64位程序可以通过将CS更改为explained here来切换到32位模式,并且我认为这也适用于切换到16位模式。
如果我运行一个我知道没有兼容性开关的64位程序,是否保证不运行非64位指令?
我知道66和67十六进制前缀可以在16和32位模式(pg 36)之间切换指令,但是这些前缀不会在64位模式下显示,对吗?
如果我错了,我在64位执行中会遇到什么非64位指令?
我的目标是编写一个x86-64解码器,我想知道对于我的用例(64位程序)而言,仅处理64位指令用例是否足够。
答案 0 :(得分:3)
机器码的每个字节序列要么作为指令解码,要么引发#UD
非法指令异常。在CPU处于64位模式下,这意味着如果它们没有故障,它们将被解码为64位模式指令。另请参见Is x86 32-bit assembly code valid x86 64-bit assembly code?(不,一般而言)。
如果它是编译器发出的普通程序,则除非有人使用内联汇编或使用您的程序反汇编了非代码部分,否则它的机器代码中不太可能有任何非法指令。或是将部分指令置于实际跳转目标之前的混淆程序,因此,简单的反汇编程序会混淆并以与指令实际运行方式不同的指令边界进行解码。 x86机器代码是无法自同步的字节流。
TL:DR:在普通程序中,是的,反汇编时遇到的每个字节序列都是有效的64位模式指令。
66
和67
不会不切换模式 ,它们只是切换该指令的操作数大小。例如66 40 90
仍然是64位模式下的REX前缀(用于随后的NOP指令)。因此,它只是一个nop
(xchg ax,ax
),不会像在inc ax
/ xchg eax,eax
中那样在32位模式中覆盖它进行解码。
尝试先组装db 0x66, 0x40, 0x90
,然后再组装nasm -felf32
,然后再组装nasm -felf64
,以查看同一序列如何在64位模式下解码,而不是在32位中那样模式。
许多指令编码在32位和64位模式下都相同,因为它们共享相同的默认操作数大小(对于非堆栈指令)。例如b8 39 30 00 00 mov eax,0x3039
是mov eax, 12345
在32位或64位模式下的代码。
(当您说“ 64位指令”时,我希望您不要指的是64位操作数大小,因为不是这种情况。所有操作数大小从8到64-大多数指令都可以在64位模式下对该位进行编码。)
是的,可以安全地假设用户空间程序不会通过执行远jmp
来切换模式。除非您在Windows上,否则WOW64 DLL出于某种原因会这样做,而不是直接调用内核。 (Linux有32位用户空间使用sysenter
或其他直接系统调用。)