Il modo più semplice è utilizzare i codici opzionali inc
a un byte che vengono riutilizzati come prefissi REX in modalità 64 bit. Un prefisso REX ha alcun effetto sulla jcc
, in modo da poter fare:
xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc works fine
Ecco un programma completo di Linux/NASM che utilizza syscall
-exit(1)
se eseguito come 64bit, o int 0x80
per exit(0)
se eseguito come 32bit.
L'uso di BITS 32 e BITS 64 assicura che esso si assembli allo stesso codice macchina in entrambi i casi. (E sì, ho controllato con objdump -d
per mostrare i byte codice macchina raw)
Anche così, ho usato db 0x40
invece di inc eax
, per rendere più chiaro ciò che è speciale.
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
provato e funzionando. Lo costruisco due volte per ottenere metadati ELF diversi attorno allo stesso codice macchina.
$ 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
(costruire comandi da Assembling 32-bit binaries on a 64-bit system (GNU toolchain), legata dalla sezione FAQ nella x86 tag wiki).
Correzione minore: 'syscall' è valido sulla maggior parte dei cpu AMD in modalità a 32 bit. – Jester
@Jester: Grazie, mi stavo chiedendo perché è stato smontato senza lamentarsi in modalità 32 bit e assemblato in una versione precedente del codice. Ma ha funzionato per me (su un Intel Merom) per confermare che ho ottenuto un SIGILL dall'esecuzione del ramo sbagliato in modalità 32 bit. (il 'lea' si limita a decodificare a' dec' e una lea con una diversa ma ancora valida modalità di indirizzamento.) Comunque, ha corretto il commento :) –
AMD ha inventato 'syscall' e Intel ha inventato' sysenter'. Ovviamente AMD ha mantenuto 'syscall' quando creava la modalità a 64 bit, così quando Intel lo adottò, ottenne anche' syscall'. – Jester