Branche différemment en x86 / x86-64 en utilisant uniquement des caractères ASCII visibles imprimables dans le code machine

14

La tâche est simple: écrire un programme qui se branche différemment en x86 (32 bits) et x86-64 (64 bits) en utilisant uniquement des caractères ASCII visibles imprimables 0x21 ... 0x7e (espace et del ne sont pas autorisés) dans le code machine .

  • L'assemblage conditionnel n'est pas autorisé.
  • L'utilisation d'appels API n'est pas autorisée.
  • L'utilisation du code en mode noyau (anneau 0) n'est pas autorisée.
  • Le code doit s'exécuter sans provoquer d'exceptions dans IA-32 et x86-64 sous Linux ou dans un autre système d'exploitation en mode protégé.
  • Le fonctionnement ne doit pas dépendre des paramètres de ligne de commande.
  • Toutes les instructions doivent être codées en code machine en utilisant uniquement des caractères ASCII dans la plage 0x21 ... 0x7e (33 ... 126 décimal). Donc par exemple.cpuidest hors limites (c'est 0f a2), sauf si vous utilisez du code auto-modifiable.
  • Le même code binaire doit s'exécuter en x86 et x86-64, mais comme les en-têtes de fichiers (ELF / ELF64 / etc.) Peuvent être différents, vous devrez peut-être l'assembler et le lier à nouveau. Cependant, le code binaire ne doit pas changer.
  • Les solutions devraient fonctionner sur tous les processeurs entre i386 ... Core i7, mais je suis également intéressé par des solutions plus limitées.
  • Le code doit se ramifier en x86 32 bits mais pas en x86-64, ou vice versa, mais l'utilisation de sauts conditionnels n'est pas une exigence (un saut indirect ou un appel est également accepté). L'adresse cible de la branche doit être telle qu'il y ait de l'espace pour du code, au moins 2 octets d'espace dans lequel jmp rel8s'insère un court saut ( ).

La réponse gagnante est celle qui utilise le moins d'octets dans le code machine. Les octets dans l'en-tête du fichier (ELF / ELF64 par exemple) ne sont pas comptés et les octets de code après la branche (à des fins de test, etc.) ne sont pas comptés non plus.

Veuillez présenter votre réponse en ASCII, en octets hexadécimaux et en code commenté.

Ma solution, 39 octets:

ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!

hexadécimal: 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.

Code:

; 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
nrz
la source
1
Vous frappez vraiment le chapeau chaud dans la hutte :)
aditsu quitte parce que SE est MAL
Agréable. Bizarre, mais sympa. Puisque vous définissez la condition gagnante comme "la plus courte", je vais changer la balise en [code-golf] et ajouter quelques nouvelles balises descriptives. Si vous ne les aimez pas, faites le moi savoir.
dmckee --- chaton ex-modérateur

Réponses:

16

7 octets

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

Comme 32 bits

00000000  6641              inc cx
00000002  2521217321        and eax,0x21732121

Comme 64 bits

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

andefface le drapeau de retenue pour que la version 64 bits saute toujours. Pour 64 bits, 6641c'est le remplacement de la taille de l'opérande suivi derex.b la taille de l'opérande pour le and16 bits. Sur 32 bits, l' 6641instruction est complète, elle andn'a donc pas de préfixe et a une taille d'opérande de 32 bits. Cela modifie le nombre d'octets immédiats consommés par les anddeux octets d'instructions qui ne sont exécutés qu'en mode 64 bits.

Geoff Reedy
la source
1
Félicitations pour avoir atteint 1k.
DavidC
ce comportement est spécifique au processeur. Certains systèmes 64 bits ignoreront le préfixe 66 en mode 64 bits.
peter ferrie
@peterferrie Avez-vous une référence pour cela? Ma lecture est qu'un préfixe de 66h est ignoré lorsque REX.W est défini, mais cela n'a que REX.B
Geoff Reedy
excusez-moi, je me trompe. Ce n'est que le transfert de contrôle qui est affecté de cette façon (par exemple, le 66 e8 ne passe pas en IP 16 bits sur Intel).
peter ferrie
7

11 octets

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

Utilise le fait que , dans 32 bits, 0x41 est juste inc %ecx, alors qu'en 64 bits , il est le raxpréfixe qui modifie le registre cible des éléments suivantspop instruction .

        .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

A écrit ceci sur OSX, votre assembleur peut être différent.

Appelez-le avec ceci:

#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;
}
Keith Randall
la source
2

7 octets

Ne pas compter sur le préfixe 66.

$$@$Au!

32 bits:

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

AL aura le bit 0 défini après l'INC, le second ET le conservera, la branche sera prise.

64 bits:

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

AL aura le bit 0 clair après le premier ET, la branche ne sera pas prise.

peter ferrie
la source
0

Si seulement C9h était imprimable ...

32 bits:

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

L'ARPL effacera le drapeau Z, provoquant la prise de la branche.

64 bits:

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

Le XOR mettra le drapeau Z, le MOVSXD ne le changera pas, la branche ne sera pas prise.

peter ferrie
la source