Zadanie jest proste: napisz program, który rozgałęzia się inaczej w x86 (32-bit) i x86-64 (64-bit), używając tylko drukowalnych widocznych znaków ASCII 0x21 ... 0x7e (spacja i del nie są dozwolone) w kodzie maszyny .
- Montaż warunkowy jest niedozwolony.
- Używanie wywołań API jest niedozwolone.
- Używanie kodu trybu jądra (pierścień 0) jest niedozwolone.
- Kod musi działać bez powodowania wyjątków zarówno w IA-32, jak i x86-64 w Linuksie lub w innym systemie operacyjnym w trybie chronionym.
- Funkcjonowanie nie może zależeć od parametrów wiersza poleceń.
- Wszystkie instrukcje muszą być zakodowane w kodzie maszynowym, używając tylko znaków ASCII z zakresu 0x21 ... 0x7e (33 ... 126 po przecinku). Więc np.
cpuid
jest poza limitem (it0f a2
), chyba że użyjesz kodu samomodyfikującego. - Ten sam kod binarny musi działać w wersjach x86 i x86-64, ale ponieważ nagłówki plików (ELF / ELF64 / itp.) Mogą być inne, konieczne może być ponowne złożenie i połączenie go. Jednak kod binarny nie może się zmienić.
- Rozwiązania powinny działać na wszystkich procesorach między i386 ... Core i7, ale interesują mnie również bardziej ograniczone rozwiązania.
- Kod musi rozgałęziać się w 32-bitowej wersji x86, ale nie w wersji x86-64 lub odwrotnie, ale stosowanie skoków warunkowych nie jest wymagane (akceptowany jest również skok pośredni lub wywołanie). Docelowy adres rozgałęzienia musi być taki, aby było miejsce na jakiś kod, co najmniej 2 bajty miejsca, w którym
jmp rel8
zmieści się skrót „jump” ( ).
Zwycięską odpowiedzią jest ta, która wykorzystuje najmniej bajtów w kodzie maszynowym. Bajty w nagłówku pliku (na przykład ELF / ELF64) nie są liczone, a żadne bajty kodu po gałęzi (do celów testowych itp.) Również nie są liczone.
Proszę przedstawić swoją odpowiedź jako ASCII, jako bajty szesnastkowe i jako komentarz.
Moje rozwiązanie, 39 bajtów:
ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!
szesnastkowy: 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
.
Kod:
; 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
Odpowiedzi:
7 bajtów
W wersji 32-bitowej
W wersji 64-bitowej
and
usuwa flagę przenoszenia, więc wersja 64-bitowa zawsze przeskakuje. W przypadku wersji 64-bitowej6641
jest to przesłonięcie wielkości operandu, po którym następujerex.b
wielkość operanduand
jako 16-bitowa. W wersji 32-bitowej6641
jest to kompletna instrukcja, więcand
nie ma przedrostka i ma 32-bitowy rozmiar argumentu. Zmienia to liczbę bezpośrednich bajtów zużytych przezand
podanie dwóch bajtów instrukcji, które są wykonywane tylko w trybie 64-bitowym.źródło
11 bajtów
Wykorzystuje fakt, że w wersji 32-bitowej 0x41 jest po prostu
inc %ecx
, podczas gdy w wersji 64-bitowej torax
przedrostek modyfikuje rejestr docelowy poniższejpop
instrukcji.Napisałem to na OSX, twój asembler może być inny.
Nazwij to tak:
źródło
7 bajtów
Nie poleganie na przedrostku 66.
32-bitowy:
AL będzie miał ustawiony bit 0 po INC, drugi AND zachowa go, gałąź zostanie zabrana.
64-bitowy:
AL będzie miał bit zerowy po pierwszym ORAZ, gałąź nie zostanie zajęta.
źródło
Gdyby tylko C9h można było wydrukować ...
32-bitowy:
ARPL usunie flagę Z, powodując przejęcie gałęzi.
64-bitowy:
XOR ustawi flagę Z, MOVSXD nie zmieni jej, gałąź nie zostanie podjęta.
źródło