与其他答案相反,我断言原则上简短的答案是YES。这可能不会以任何方式得到官方支持,但它似乎有效。在这个答案的最后,我展示了一个演示。
在 Linux-x86_64 上,32 位(根据 GDB 源代码,X32 也是如此)进程得到CS
寄存器等于0x23
— GDT 中定义的 32 位环 3 代码段的选择器(其基址是0
)。 64 位进程有另一个选择器:0x33
— 长模式(即 64 位)环 3 代码段的选择器(基础ES
, CS
, SS
, DS
在 64 位模式下无条件地视为零)。因此,如果我们使用目标段选择器进行远跳转、远调用或类似的操作0x33
,我们将相应的描述符加载到阴影部分CS
并将最终形成 64 位段。
这个答案底部的演示使用jmp far
跳转到 64 位代码的指令。请注意,我选择了一个特殊的常量来加载rax
,因此对于 32 位代码,该指令看起来像
dec eax
mov eax, 0xfafafafa
ud2
cli ; these two are unnecessary, but leaving them here for fun :)
hlt
如果我们在 CS 影子部分中执行具有 32 位描述符的操作,则这一定会失败(将在ud2
操作说明)。
现在这是演示(用 fasm 编译)。
format ELF executable
segment readable executable
SYS_EXIT_32BIT=1
SYS_EXIT_64BIT=60
SYS_WRITE=4
STDERR=2
entry $
mov ax,cs
cmp ax,0x23 ; 32 bit process on 64 bit kernel has this selector in CS
jne kernelIs32Bit
jmp 0x33:start64 ; switch to 64-bit segment
start64:
use64
mov rax, qword 0xf4fa0b0ffafafafa ; would crash inside this if executed as 32 bit code
xor rdi,rdi
mov eax, SYS_EXIT_64BIT
syscall
ud2
use32
kernelIs32Bit:
mov edx, msgLen
mov ecx, msg
mov ebx, STDERR
mov eax, SYS_WRITE
int 0x80
dec ebx
mov eax, SYS_EXIT_32BIT
int 0x80
msg:
db "Kernel appears to be 32 bit, can't jump to long mode segment",10
msgLen = $-msg