Die Aufgabe ist einfach: Schreiben Sie ein Programm, das in x86 (32-Bit) und x86-64 (64-Bit) unterschiedlich verzweigt und nur druckbare sichtbare ASCII-Zeichen 0x21 ... 0x7e (Leerzeichen und Entf sind nicht zulässig) im Maschinencode verwendet .
- Bedingte Montage ist nicht zulässig.
- Das Verwenden von API-Aufrufen ist nicht zulässig.
- Die Verwendung von Kernel-Mode-Code (Ring 0) ist nicht zulässig.
- Der Code muss ohne Ausnahmen sowohl in IA-32 als auch in x86-64 unter Linux oder in einem anderen Betriebssystem im geschützten Modus ausgeführt werden.
- Die Funktion darf nicht von Befehlszeilenparametern abhängen.
- Alle Anweisungen müssen im Maschinencode mit nur ASCII-Zeichen im Bereich von 0x21 ... 0x7e (33 ... 126 Dezimal) codiert werden. Also zB.
cpuid
ist außerhalb der Grenzen (es ist0f a2
), es sei denn, Sie verwenden selbstmodifizierenden Code. - Derselbe Binärcode muss in x86 und x86-64 ausgeführt werden. Da sich die Dateiköpfe (ELF / ELF64 / etc.) Jedoch möglicherweise unterscheiden, müssen Sie ihn möglicherweise erneut zusammenstellen und verknüpfen. Der Binärcode darf sich jedoch nicht ändern.
- Lösungen sollten auf allen Prozessoren zwischen i386 ... Core i7 funktionieren, aber ich bin auch an eingeschränkteren Lösungen interessiert.
- Der Code muss in 32-Bit-x86 verzweigen, jedoch nicht in x86-64 oder umgekehrt. Die Verwendung von bedingten Sprüngen ist jedoch nicht erforderlich (indirekter Sprung oder Aufruf wird ebenfalls akzeptiert). Die Verzweigungszieladresse muss so sein, dass Platz für Code vorhanden ist, mindestens 2 Byte Platz, in den ein kurzer Sprung (
jmp rel8
) passt.
Die gewinnende Antwort ist diejenige, die die wenigsten Bytes im Maschinencode verwendet. Die Bytes im Dateiheader (z. B. ELF / ELF64) werden nicht gezählt, und die Code-Bytes nach der Verzweigung (zu Testzwecken usw.) werden ebenfalls nicht gezählt.
Bitte präsentieren Sie Ihre Antwort als ASCII, als hexadezimales Byte und als kommentierten Code.
Meine Lösung, 39 Bytes:
ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!
hexadezimal: 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
Antworten:
7 Bytes
Als 32 Bit
Als 64 Bit
and
Löscht das Übertragsflag, sodass die 64-Bit-Version immer springt. Bei 64-Bit6641
ist das die Überschreibung der Operandengröße, gefolgt vonrex.b
der Operandengröße für dasand
ergibt 16 Bit. Bei 32-Bit-6641
Befehlen handelt es sich um vollständige Befehle, daherand
hat der Befehl kein Präfix und eine 32-Bit-Operandengröße. Dadurch wird die Anzahl der unmittelbaren Bytes geändert, die durch dieand
Angabe von zwei Bytes an Befehlen verbraucht werden , die nur im 64-Bit-Modus ausgeführt werden.quelle
11 Bytes
Verwendet die Tatsache, dass in 32-Bit 0x41 gerade ist
inc %ecx
, während in 64-Bit dasrax
Präfix das Zielregister der folgendenpop
Anweisung ändert .Schrieb dies auf OSX, Ihr Assembler könnte anders sein.
Nennen Sie es mit diesem:
quelle
7 Bytes
Ich verlasse mich nicht auf das Präfix 66.
32-Bit:
Bei AL wird nach dem INC Bit 0 gesetzt, bei dem zweiten AND wird es beibehalten und die Verzweigung wird ausgeführt.
64-Bit:
Bei AL wird Bit 0 nach dem ersten UND gelöscht, die Verzweigung wird nicht genommen.
quelle
Wenn nur C9h druckbar wäre ...
32-Bit:
Die ARPL löscht das Z-Flag, wodurch die Verzweigung ausgeführt wird.
64-Bit:
Das XOR setzt das Z-Flag, das MOVSXD ändert es nicht, der Zweig wird nicht genommen.
quelle