其他答案相反,我認為原則上的答案是是 。 這可能不以任何方式正式支持,但它似乎可以工作。 在這個答案的最後我展示了一個演示。
在 Linux-x86_64,32位( 根據GDB的源代碼,X32也) 進程獲取 CS register 等於 0x23 — a 在 GDT ( 它的基礎是 0 ) 定義的32位 環 3碼段的選擇器。 64位進程獲取另一個選擇器: 0x33 —一個長模式選擇器( 例如 。 64位) 環 3代碼段( 。ES,CS,SS,DS的基在 64位模式下無條件處理為零) 。 這樣,我們在 0x33的陰影部分進行遠跳轉。遠距離或者類似的操作時,將載入對應的描述符,並在1 位段結束。
這個答案底部的演示使用 jmp far 指令跳轉到 64位代碼。 請注意,我選擇了一個特殊的常量載入到 rax 中,因這裡為 32位代碼提供了dec eax
mov eax, 0xfafafafa
ud2
cli ; these two are unnecessary, but leaving them here for fun :)
hlt
如果我們在( 將在 ud2 指令上提升 SIGILL ) 陰影部分中執行 32位描述符,這必須失敗。
下面是演示( 用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