最簡單的方法是使用在64位模式下重新用作REX前綴的單字節inc
操作碼。一個REX前綴對jcc
沒有影響,所以你可以做:
xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc works fine
下面是一個使用syscall
到exit(1)
如果如果運行的32位運行的64位,或int 0x80
到exit(0)
一個完整的Linux/NASM程序。
使用BITS 32和BITS 64可確保它以任何方式組裝到相同的機器代碼。 (是的,我有objdump -d
檢查顯示原始機器代碼的字節)
即使這樣,我用db 0x40
代替inc eax
,這樣可以很清楚有什麼特別的。
BITS 32
global _start
_start:
xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc still works
;jmp .64bit_mode ; uncomment to test that the 64bit code does fault in a 32bit binary
.32bit_mode:
xor ebx,ebx
mov eax, 1 ; exit(0)
int 0x80
BITS 64
.64bit_mode:
lea rdx, [rel _start] ; An instruction that won't assemble in 32-bit mode.
;; arbitrary 64bit code here
mov edi, 1
mov eax, 231 ; exit_group(1).
syscall ; This does SIGILL if this is run in 32bit mode on Intel CPUs
;;;;; Or as a callable function:
BITS 32
am_i_32bit: ;; returns false only in 64bit mode
xor eax,eax
db 0x40 ; 32bit: inc eax
; 64bit: REX.W=0
;nop ; REX nop is REX xchg eax,eax
ret ; REX ret works normally, too
測試和工作。我將它構建兩次以在相同的機器代碼周圍獲得不同的ELF元數據。
$ yasm -felf64 -Worphan-labels -gdwarf2 x86-polyglot-32-64.asm && ld -o x86-polyglot.64bit x86-polyglot-32-64.o
$ yasm -felf32 -Worphan-labels -gdwarf2 x86-polyglot-32-64.asm && ld -melf_i386 -o x86-polyglot.32bit x86-polyglot-32-64.o
$ ./x86-polyglot.32bit && echo 32bit || echo 64bit
32bit
$ ./x86-polyglot.64bit && echo 32bit || echo 64bit
64bit
(從Assembling 32-bit binaries on a 64-bit system (GNU toolchain)建立命令,從在x86標籤維基FAQ部分連接)。
微調:「syscall」在32位模式下對大多數AMD CPU有效。 – Jester
@Jester:謝謝,我想知道爲什麼它在32位模式下沒有投訴而反彙編,並且在早期版本的代碼中彙編。但它確實對我(在Intel Merom上)起作用,以確認我在32位模式下運行了錯誤的分支而得到SIGILL。 ('lea'只是解碼到一個'dec'和一個具有不同但仍然有效的尋址模式的lea)。無論如何,修復了註釋:) –
AMD發明了'syscall'和Intel發明了'sysenter'。當然,AMD在創建64位模式時保留了「syscall」,所以當Intel採用這種模式時,他們也得到了「syscall」。 – Jester