Задача проста: написать программу, которая разветвляется по-разному в x86 (32-разрядный) и x86-64 (64-разрядный), используя только печатные видимые символы ASCII 0x21 ... 0x7e (пробел и del не допускаются) в машинном коде ,
- Условная сборка не допускается.
- Использование вызовов API в не разрешено.
- Использование кода режима ядра (кольцо 0) не допускается.
- Код должен работать без исключений как в IA-32, так и в x86-64 в Linux или в некоторых других ОС с защищенным режимом.
- Функционирование не должно зависеть от параметров командной строки.
- Все инструкции должны быть закодированы в машинном коде с использованием только символов ASCII в диапазоне 0x21 ... 0x7e (десятичное число 33 ... 126). Так, например.
cpuid
выходит за пределы (это0f a2
), если вы не используете самоизменяющийся код. - Один и тот же двоичный код должен выполняться в x86 и x86-64, но поскольку заголовки файлов (ELF / ELF64 / и т. Д.) Могут отличаться, вам может понадобиться собрать и снова связать его. Однако двоичный код не должен изменяться.
- Решения должны работать на всех процессорах между i386 ... Core i7, но меня также интересуют более ограниченные решения.
- Код должен переходить в 32-разрядной версии x86, но не в x86-64, или наоборот, но использование условных переходов не является обязательным (также допускается косвенный переход или вызов). Целевой адрес ветвления должен быть таким, чтобы для некоторого кода было место, по крайней мере, 2 байта, в которые
jmp rel8
помещается функция jump ( ).
Победившим ответом является тот, который использует наименьшее количество байтов в машинном коде. Байты в заголовке файла (например, ELF / ELF64) не учитываются, и любые байты кода после ветви (для целей тестирования и т. Д.) Также не учитываются.
Пожалуйста, представьте свой ответ в виде ASCII, в виде шестнадцатеричных байтов и в виде комментария кода.
Мое решение, 39 байт:
ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!
шестнадцатеричное: 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
.
Код:
; 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
Ответы:
7 байт
Как 32 бит
Как 64 бит
and
очищает флаг переноса, поэтому 64-битная версия всегда переходит. Для 64-битных6641
это переопределение размера операнда, за которым следуетrex.b
размер операнда дляand
16-битного значения. На 32-битной6641
инструкции является полной, поэтомуand
не имеет префикса и имеет размер 32-битного операнда. Это изменяет количество непосредственных байтов, потребляемыхand
предоставлением двух байтов инструкций, которые выполняются только в 64-битном режиме.источник
11 байт
Использует тот факт, что в 32-битном 0x41 является просто
inc %ecx
, в то время как в 64-битном этоrax
префикс, который изменяет целевой регистр следующейpop
инструкции.Написал это на OSX, ваш ассемблер может отличаться.
Назовите это с этим:
источник
7 байт
Не полагаясь на префикс 66.
32-бит:
AL будет иметь бит 0, установленный после INC, второй AND сохранит его, ветвь будет принята.
64-бит:
AL будет иметь бит 0, очищенный после первого AND, ветвь не будет взята.
источник
Если бы только C9h можно было печатать ...
32-бит:
ARPL очистит флаг Z, в результате чего ветвь будет занята.
64-бит:
XOR установит флаг Z, MOVSXD не изменит его, ветвь не будет взята.
источник