In x86 / x86-64 anders verzweigen, wobei nur druckbare sichtbare ASCII-Zeichen im Maschinencode verwendet werden

14

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. cpuidist außerhalb der Grenzen (es ist 0f 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
nrz
quelle
1
Sie haben wirklich den heißen Hut in der Hütte geschlagen :)
Aditsu
Nett. Seltsam, aber nett. Da Sie die Gewinnbedingung auf "kürzeste" setzen, werde ich das Tag in [Code-Golf] ändern und einige neue beschreibende Tags hinzufügen. Wenn Sie sie nicht mögen, lassen Sie es mich wissen.
dmckee

Antworten:

16

7 Bytes

0000000: 6641 2521 2173 21                        fA%!!s!

Als 32 Bit

00000000  6641              inc cx
00000002  2521217321        and eax,0x21732121

Als 64 Bit

00000000  6641252121        and ax,0x2121
00000005  7321              jnc 0x28

andLöscht das Übertragsflag, sodass die 64-Bit-Version immer springt. Bei 64-Bit 6641ist das die Überschreibung der Operandengröße, gefolgt von rex.bder Operandengröße für das andergibt 16 Bit. Bei 32-Bit- 6641Befehlen handelt es sich um vollständige Befehle, daher andhat der Befehl kein Präfix und eine 32-Bit-Operandengröße. Dadurch wird die Anzahl der unmittelbaren Bytes geändert, die durch die andAngabe von zwei Bytes an Befehlen verbraucht werden , die nur im 64-Bit-Modus ausgeführt werden.

Geoff Reedy
quelle
1
Herzlichen Glückwunsch zum Erreichen von 1k.
DavidC
Dieses Verhalten ist CPU-spezifisch. Einige 64-Bit-Systeme ignorieren das Präfix 66 im 64-Bit-Modus.
Peter Ferrie
@peterferrie Hast du eine Referenz dafür? Ich lese, dass ein 66h-Präfix ignoriert wird, wenn REX.W eingestellt ist, aber dies hat nur REX.B
Geoff Reedy
Entschuldigung, ich liege falsch. Es ist nur die Übertragung der Kontrolle, die auf diese Weise beeinflusst wird (z. B. schaltet der 66 e8 bei Intel nicht auf 16-Bit-IP um).
Peter Ferrie
7

11 Bytes

ascii: j6Xj3AX,3t!
hex: 6a 36 58 6a 33 41 58 2c 33 74 21

Verwendet die Tatsache, dass in 32-Bit 0x41 gerade ist inc %ecx, während in 64-Bit das raxPräfix das Zielregister der folgenden popAnweisung ändert .

        .globl _check64
_check64:
        .byte   0x6a, 0x36      # push $0x36
        .byte   0x58            # pop %rax
        .byte   0x6a, 0x33      # push $0x33

        # this is either "inc %ecx; pop %eax" in 32-bit, or "pop %r8" in 64-bit.
        # so in 32-bit it sets eax to 0x33, in 64-bit it leaves rax unchanged at 0x36.
        .byte   0x41            # 32: "inc %ecx", 64: "rax prefix"
        .byte   0x58            # 32: "pop %eax", 64: "pop %r8"

        .byte   0x2c, 0x33      # sub $0x33,%al
        .byte   0x74, 0x21      # je (branches if 32 bit)

        mov     $1,%eax
        ret

        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
        mov     $0,%eax
        ret

Schrieb dies auf OSX, Ihr Assembler könnte anders sein.

Nennen Sie es mit diesem:

#include <stdio.h>
extern int check64(void);
int main(int argc, char *argv[]) {
  if (check64()) {
    printf("64-bit\n");
  } else {
    printf("32-bit\n");
  }
  return 0;
}
Keith Randall
quelle
2

7 Bytes

Ich verlasse mich nicht auf das Präfix 66.

$$@$Au!

32-Bit:

00000000 24 24 and al,24h
00000002 40    inc eax
00000003 24 41 and al,41h
00000005 75 21 jne 00000028h

Bei AL wird nach dem INC Bit 0 gesetzt, bei dem zweiten AND wird es beibehalten und die Verzweigung wird ausgeführt.

64-Bit:

00000000 24 24    and al,24h
00000002 40 24 41 and al,41h
00000005 75 21    jne 00000028h

Bei AL wird Bit 0 nach dem ersten UND gelöscht, die Verzweigung wird nicht genommen.

Peter Ferrie
quelle
0

Wenn nur C9h druckbar wäre ...

32-Bit:

00000000 33 C9 xor  ecx, ecx
00000002 63 C9 arpl ecx, ecx
00000004 74 21 je   00000027h

Die ARPL löscht das Z-Flag, wodurch die Verzweigung ausgeführt wird.

64-Bit:

00000000 33 C9 xor    ecx, ecx
00000002 63 C9 movsxd ecx, ecx
00000004 74 21 je     00000027h

Das XOR setzt das Z-Flag, das MOVSXD ändert es nicht, der Zweig wird nicht genommen.

Peter Ferrie
quelle