La tarea es simple: escriba un programa que se bifurque de manera diferente en x86 (32 bits) y x86-64 (64 bits) utilizando solo caracteres ASCII visibles imprimibles 0x21 ... 0x7e (no se permiten espacios y del) en el código de máquina .
- El montaje condicional no está permitido.
- El uso de llamadas API no está permitido.
- El uso del código de modo kernel (anillo 0) no está permitido.
- El código debe ejecutarse sin causar excepciones tanto en IA-32 como en x86-64 en Linux o en algún otro sistema operativo en modo protegido.
- El funcionamiento no debe depender de los parámetros de la línea de comandos.
- Todas las instrucciones deben estar codificadas en código de máquina utilizando solo caracteres ASCII en el rango de 0x21 ... 0x7e (33 ... 126 decimal). Entonces, por ejemplo.
cpuid
está fuera de los límites (es0f a2
), a menos que use un código de modificación automática. - El mismo código binario debe ejecutarse en x86 y x86-64, pero como los encabezados de los archivos (ELF / ELF64 / etc.) pueden ser diferentes, es posible que deba ensamblarlo y vincularlo nuevamente. Sin embargo, el código binario no debe cambiar.
- Las soluciones deberían funcionar en todos los procesadores entre i386 ... Core i7, pero también estoy interesado en soluciones más limitadas.
- El código debe bifurcarse en x86 de 32 bits pero no en x86-64, o viceversa, pero no es obligatorio usar saltos condicionales (también se acepta salto o llamada indirecta). La dirección de destino de la rama debe ser tal que haya espacio para algún código, al menos 2 bytes de espacio en el que
jmp rel8
encaje un salto corto ( ).
La respuesta ganadora es la que usa menos bytes en el código de máquina. Los bytes en el encabezado del archivo (ELF / ELF64, por ejemplo) no se cuentan, y los bytes de código después de la rama (para fines de prueba, etc.) tampoco se cuentan.
Presente su respuesta como ASCII, como bytes hexadecimales y como código comentado.
Mi solución, 39 bytes:
ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!
hexadecimal: 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
.
Código:
; 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
Respuestas:
7 bytes
Como 32 bits
Como 64 bit
and
borra la bandera de transporte para que la versión de 64 bits siempre salte. Para 64 bits,6641
es la anulación del tamaño del operando seguido derex.b
modo que el tamaño del operandoand
sale como 16 bits. En 32 bits,6641
es una instrucción completa, por lo queand
no tiene prefijo y tiene un tamaño de operando de 32 bits. Esto cambia el número de bytes inmediatos consumidos aland
dar dos bytes de instrucciones que solo se ejecutan en modo de 64 bits.fuente
11 bytes
Utiliza el hecho de que en 32 bits, 0x41 es justo
inc %ecx
, mientras que en 64 bits es elrax
prefijo el que modifica el registro de destino de la siguientepop
instrucción.Escribió esto en OSX, su ensamblador podría ser diferente.
Llámalo con esto:
fuente
7 bytes
No depender del prefijo 66.
32 bits:
AL tendrá el bit 0 establecido después del INC, el segundo Y lo preservará, se tomará la rama.
64 bits:
AL tendrá el bit 0 claro después del primer AND, no se tomará la rama.
fuente
Si solo C9h fuera imprimible ...
32 bits:
El ARPL borrará la bandera Z, haciendo que se tome la rama.
64 bits:
El XOR establecerá la bandera Z, el MOVSXD no lo cambiará, la rama no se tomará.
fuente