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.
cpuid
est hors limites (c'est0f 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 rel8
s'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
Réponses:
7 octets
Comme 32 bits
Comme 64 bits
and
efface le drapeau de retenue pour que la version 64 bits saute toujours. Pour 64 bits,6641
c'est le remplacement de la taille de l'opérande suivi derex.b
la taille de l'opérande pour leand
16 bits. Sur 32 bits, l'6641
instruction est complète, elleand
n'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 lesand
deux octets d'instructions qui ne sont exécutés qu'en mode 64 bits.la source
11 octets
Utilise le fait que , dans 32 bits, 0x41 est juste
inc %ecx
, alors qu'en 64 bits , il est lerax
préfixe qui modifie le registre cible des éléments suivantspop
instruction .A écrit ceci sur OSX, votre assembleur peut être différent.
Appelez-le avec ceci:
la source
7 octets
Ne pas compter sur le préfixe 66.
32 bits:
AL aura le bit 0 défini après l'INC, le second ET le conservera, la branche sera prise.
64 bits:
AL aura le bit 0 clair après le premier ET, la branche ne sera pas prise.
la source
Si seulement C9h était imprimable ...
32 bits:
L'ARPL effacera le drapeau Z, provoquant la prise de la branche.
64 bits:
Le XOR mettra le drapeau Z, le MOVSXD ne le changera pas, la branche ne sera pas prise.
la source