A tarefa é simples: escreva um programa que se ramifique de maneira diferente em x86 (32 bits) e x86-64 (64 bits) usando apenas caracteres ASCII visíveis e imprimíveis 0x21 ... 0x7e (space e del não são permitidos) no código da máquina .
- Montagem condicional não é permitida.
- O uso de chamadas de API não é permitido.
- O uso do código no modo kernel (anel 0) não é permitido.
- O código deve ser executado sem causar exceções no IA-32 e no x86-64 no Linux ou em outro sistema operacional em modo protegido.
- O funcionamento não deve depender dos parâmetros da linha de comando.
- Todas as instruções devem ser codificadas no código da máquina usando apenas caracteres ASCII no intervalo 0x21 ... 0x7e (33 ... 126 decimal). Então, por exemplo.
cpuid
está fora dos limites (é0f a2
), a menos que você use código auto-modificável. - O mesmo código binário deve ser executado em x86 e x86-64, mas como os cabeçalhos de arquivo (ELF / ELF64 / etc.) Podem ser diferentes, talvez seja necessário montar e vincular novamente. No entanto, o código binário não deve ser alterado.
- As soluções devem funcionar em todos os processadores entre o i386 e o Core i7, mas também estou interessado em soluções mais limitadas.
- O código deve ramificar-se no x86 de 32 bits, mas não no x86-64, ou vice-versa, mas o uso de saltos condicionais não é um requisito (salto ou chamada indireta também é aceita). O endereço de destino da ramificação deve ser tal que exista espaço para algum código, pelo menos 2 bytes de espaço no qual um salto curto (
jmp rel8
) se encaixa.
A resposta vencedora é a que usa menos bytes no código da máquina. Os bytes no cabeçalho do arquivo (ELF / ELF64, por exemplo) não são contados, e os bytes de código após a ramificação (para fins de teste, etc.) também não são contados.
Apresente sua resposta como ASCII, como bytes hexadecimais e como código comentado.
Minha solução, 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
Respostas:
7 bytes
Como 32 bits
Como 64 bits
and
limpa a bandeira de transporte para que a versão de 64 bits sempre salte. Para 64 bits,6641
é a substituição do tamanho do operando, seguida pelorex.b
tamanho do operando paraand
16 bits. Em 32 bits,6641
é uma instrução completa, portantoand
não possui prefixo e possui um tamanho de operando de 32 bits. Isso altera o número de bytes imediatos consumidos,and
fornecendo dois bytes de instruções que são executadas apenas no modo de 64 bits.fonte
11 bytes
Usa o fato de que em 32 bits, 0x41 é justo
inc %ecx
, enquanto em 64 bits é orax
prefixo que modifica o registro de destino dapop
instrução a seguir .Escreveu isso no OSX, seu montador pode ser diferente.
Chame assim:
fonte
7 bytes
Não confiando no 66 prefixo.
32 bits:
AL terá o bit 0 definido após o INC, o segundo AND o preservará e a ramificação será realizada.
64 bits:
AL terá o bit 0 claro após o primeiro AND, a ramificação não será aceita.
fonte
Se apenas C9h fosse imprimível ...
32 bits:
O ARPL limpará o sinalizador Z, fazendo com que o ramo seja executado.
64 bits:
O XOR definirá o sinalizador Z, o MOVSXD não o mudará, o ramo não será utilizado.
fonte