2016-06-27 41 views
9

È possibile che gli stessi byte del codice macchina siano in grado di determinare se sono in esecuzione in modalità 32 o 64 bit e quindi fare cose diverse?frammento di codice macchina poliglotta x86-32/x86-64 che rileva la modalità a 64 bit in fase di esecuzione?

, ad esempio scrivere polyglot codice macchina.

Normalmente è possibile rilevare in fase di compilazione con i macro #ifdef. Oppure in C, potresti scrivere un if() con una costante in fase di compilazione come condizione, e fare in modo che il compilatore ottimizzi l'altro lato di esso.

Questo è utile solo per casi strani, come forse l'iniezione di codice, o solo per vedere se è possibile.


Vedi anche: un polyglot ARM/x86 machine code per diramare a indirizzi diversi a seconda che l'architettura decodifica il byte.

risposta

9

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 tag wiki).

+0

Correzione minore: 'syscall' è valido sulla maggior parte dei cpu AMD in modalità a 32 bit. – Jester

+0

@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 :) –

+0

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