Ramo in modo diverso in x86 / x86-64 utilizzando solo caratteri ASCII visibili stampabili nel codice macchina


14

L'attività è semplice: scrivere un programma che si ramifica in modo diverso in x86 (32 bit) e x86-64 (64 bit) utilizzando solo caratteri ASCII visibili stampabili 0x21 ... 0x7e (spazio e del non consentiti) nel codice macchina .

  • L'assemblaggio condizionale non è consentito.
  • L'uso delle chiamate API non è consentito.
  • L'uso del codice in modalità kernel (ring 0) non è consentito.
  • Il codice deve essere eseguito senza causare eccezioni sia in IA-32 sia in x86-64 in Linux o in altri sistemi operativi in ​​modalità protetta.
  • Il funzionamento non deve dipendere dai parametri della riga di comando.
  • Tutte le istruzioni devono essere codificate nel codice macchina usando solo caratteri ASCII nell'intervallo 0x21 ... 0x7e (33 ... 126 decimale). Quindi ad es. cpuidè fuori dai limiti (è0f a2 ), a meno che non si utilizzi un codice di modifica automatica.
  • Lo stesso codice binario deve essere eseguito in x86 e x86-64, ma poiché le intestazioni dei file (ELF / ELF64 / ecc.) Potrebbero essere diverse, potrebbe essere necessario assemblare e ricollegarlo. Tuttavia, il codice binario non deve cambiare.
  • Le soluzioni dovrebbero funzionare su tutti i processori tra i386 ... Core i7, ma sono interessato anche a soluzioni più limitate.
  • Il codice deve essere ramificato in x86 a 32 bit ma non in x86-64 o viceversa, ma l'utilizzo di salti condizionali non è un requisito (è accettato anche il salto o la chiamata indiretta). L'indirizzo di destinazione del ramo deve essere tale che vi sia spazio per un po 'di codice, almeno 2 byte di spazio in cui si jmp rel8inserisce un salto breve ( ).

La risposta vincente è quella che utilizza meno byte nel codice macchina. I byte nell'intestazione del file (ad esempio ELF / ELF64) non vengono conteggiati e non vengono conteggiati neanche i byte di codice dopo il ramo (a scopo di test ecc.).

Si prega di presentare la risposta come ASCII, come byte esadecimali e come codice commentato.

La mia soluzione, 39 byte:

ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!

esadecimale: 66 68 6F 74 66 68 61 74 66 68 69 74 66 68 75 74 66 68 6F 74 66 68 61 74 66 68 69 74 66 68 75 74 5F 48 33 3C 24 74 21.

Codice:

; can be compiled eg. with yasm.
; yasm & ld:
; yasm -f elf64 -m amd64 -g dwarf2 x86_x86_64_branch.asm -o x86_x86_64_branch.o; ld x86_x86_64_branch.o -o x86_x86_64_branch
; yasm & gcc:
; yasm -f elf64 -m amd64 -g dwarf2 x86_x86_64_branch.asm -o x86_x86_64_branch.o; gcc -o x86_x86_64_branch x86_x86_64_branch.o

section .text
global main
extern printf

main:
    push    word 0x746f     ; 66 68 6f 74 (x86, x86-64)
    push    word 0x7461     ; 66 68 61 74 (x86, x86-64)
    push    word 0x7469     ; 66 68 69 74 (x86, x86-64)
    push    word 0x7475     ; 66 68 75 74 (x86, x86-64)

    push    word 0x746f     ; 66 68 6f 74 (x86, x86-64)
    push    word 0x7461     ; 66 68 61 74 (x86, x86-64)
    push    word 0x7469     ; 66 68 69 74 (x86, x86-64)
    push    word 0x7475     ; 66 68 75 74 (x86, x86-64)

    db      0x5f            ; x86:    pop edi
                            ; x86-64: pop rdi

    db      0x48, 0x33, 0x3c, 0x24
                            ; x86:
                            ; 48          dec eax
                            ; 33 3c 24    xor edi,[esp]

                            ; x86-64:
                            ; 48 33 3c 24 xor rdi,[rsp]

    jz      @bits_64        ; 0x74 0x21
                            ; branch only if running in 64-bit mode.

; the code golf part ends here, 39 bytes so far.

; the rest is for testing only, and does not affect the answer.

    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop

    jmp     @bits_32

@bits_64:
    db      0x55                    ; push rbp

    db      0x48, 0x89, 0xe5        ; mov rbp,rsp
    db      0x48, 0x8d, 0x3c, 0x25  ; lea rdi,
    dd      printf_msg              ; [printf_msg]
    xor     eax,eax
    mov     esi,64

    call    printf
    db      0x5d                    ; pop rbp

    NR_exit equ 60

    xor     edi,edi
    mov     eax,NR_exit     ; number of syscall (60)
    syscall

@bits_32:
    lea     edi,[printf_msg]
    mov     esi,32
    call    printf

    mov     eax,NR_exit
    int     0x80

section .data

printf_msg: db "running in %d-bit system", 0x0a, 0

1
Hai davvero colpito il cappello caldo nella capanna :)
aditsu smettila perché SE è EVIL

Bello. Strano, ma carino. Dato che stai impostando la condizione vincente su "più breve", cambierò il tag in [code-golf] e aggiungerò alcuni nuovi tag descrittivi. Se non ti piacciono, fammelo sapere.
dmckee --- ex gattino moderatore

Risposte:


16

7 byte

0000000: 6641 2521 2173 21                        fA%!!s!

A 32 bit

00000000  6641              inc cx
00000002  2521217321        and eax,0x21732121

A 64 bit

00000000  6641252121        and ax,0x2121
00000005  7321              jnc 0x28

andcancella il flag carry in modo che la versione a 64 bit salti sempre. Per 64 bit 6641è la sostituzione della dimensione dell'operando seguita da rex.bquindi la dimensione dell'operando per la andviene fuori come 16 bit. Su 32 bit 6641è un'istruzione completa, quindi andnon ha prefisso e ha una dimensione di operando a 32 bit. Ciò modifica il numero di byte immediati consumati anddando due byte di istruzioni che vengono eseguite solo in modalità 64 bit.


1
Congratulazioni per aver raggiunto 1k.
DavidC

questo comportamento è specifico della CPU. Alcuni sistemi a 64 bit ignoreranno il prefisso 66 in modalità 64 bit.
Peter Ferrie,

@peterferrie Hai un riferimento per questo? La mia lettura è che un prefisso 66h viene ignorato quando è impostato REX.W ma questo ha solo REX.B
Geoff Reedy

mi scusi, mi sbaglio. È interessato solo il trasferimento del controllo (ad es. 66 e8 non passa a IP a 16 bit su Intel).
peter ferrie il

7

11 byte

ascii: j6Xj3AX,3t!
hex: 6a 36 58 6a 33 41 58 2c 33 74 21

Usa il fatto che in 32 bit, 0x41 è giusto inc %ecx, mentre in 64 bit è il raxprefisso che modifica il registro di destinazione di quanto seguepop istruzione.

        .globl _check64
_check64:
        .byte   0x6a, 0x36      # push $0x36
        .byte   0x58            # pop %rax
        .byte   0x6a, 0x33      # push $0x33

        # this is either "inc %ecx; pop %eax" in 32-bit, or "pop %r8" in 64-bit.
        # so in 32-bit it sets eax to 0x33, in 64-bit it leaves rax unchanged at 0x36.
        .byte   0x41            # 32: "inc %ecx", 64: "rax prefix"
        .byte   0x58            # 32: "pop %eax", 64: "pop %r8"

        .byte   0x2c, 0x33      # sub $0x33,%al
        .byte   0x74, 0x21      # je (branches if 32 bit)

        mov     $1,%eax
        ret

        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        mov     $0,%eax
        ret

Scritto su OSX, il tuo assemblatore potrebbe essere diverso.

Chiamalo con questo:

#include <stdio.h>
extern int check64(void);
int main(int argc, char *argv[]) {
  if (check64()) {
    printf("64-bit\n");
  } else {
    printf("32-bit\n");
  }
  return 0;
}

2

7 byte

Non basarsi sul prefisso 66.

$$@$Au!

32-bit:

00000000 24 24 and al,24h
00000002 40    inc eax
00000003 24 41 and al,41h
00000005 75 21 jne 00000028h

AL avrà il bit 0 impostato dopo INC, il secondo AND lo conserverà, il ramo verrà preso.

64-bit:

00000000 24 24    and al,24h
00000002 40 24 41 and al,41h
00000005 75 21    jne 00000028h

AL avrà il bit 0 libero dopo il primo AND, il ramo non verrà preso.


0

Se solo C9h fosse stampabile ...

32-bit:

00000000 33 C9 xor  ecx, ecx
00000002 63 C9 arpl ecx, ecx
00000004 74 21 je   00000027h

L'ARPL cancellerà la bandiera Z, causando la presa del ramo.

64-bit:

00000000 33 C9 xor    ecx, ecx
00000002 63 C9 movsxd ecx, ecx
00000004 74 21 je     00000027h

XOR imposterà il flag Z, MOVSXD non lo modificherà, il ramo non verrà preso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.