マシンコードで印刷可能な表示ASCII文字のみを使用して、x86 / x86-64で異なる分岐を行います。


14

タスクは簡単です:マシンコードで印刷可能な可視ASCII文字0x21 ... 0x7e(スペースとdelは使用できません)のみを使用して、x86(32ビット)とx86-64(64ビット)で異なる分岐を行うプログラムを記述します。

  • 条件付きアセンブリは許可されていません。
  • API呼び出しの使用は許可されていません。
  • カーネルモード(リング0)コードの使用は許可されていません。
  • LinuxのIA-32とx86-64の両方、または他の保護モードOSで例外を発生させずにコードを実行する必要があります。
  • 機能はコマンドラインパラメータに依存してはいけません。
  • すべての命令は、0x21 ... 0x7e(10進数で33 ... 126)の範囲のASCII文字のみを使用してマシンコードでエンコードする必要があります。例えば 自己修正コードを使用しない限りcpuid、制限外です(0f a2)。
  • 同じバイナリコードをx86とx86-64で実行する必要がありますが、ファイルヘッダー(ELF / ELF64 / etc。)が異なる場合があるため、再度アセンブルしてリンクする必要があります。ただし、バイナリコードは変更しないでください。
  • ソリューションはi386 ... Core i7の間のすべてのプロセッサで動作するはずですが、より限定的なソリューションにも興味があります。
  • コードは32ビットx86で分岐する必要がありますが、x86-64で分岐することはできませんが、条件付きジャンプの使用は必須ではありません(間接ジャンプまたは呼び出しも受け入れられます)。分岐ターゲットアドレスは、コード用のスペース、つまりショートジャンプ(jmp rel8)が収まる少なくとも2バイトのスペースがあるようにする必要があります。

勝利の答えは、マシンコードの中で最も少ないバイトを使用するものです。ファイルヘッダーのバイト(ELF / ELF64など)はカウントされず、分岐後のコードのバイト(テスト目的など)もカウントされません。

回答をASCII、16進バイト、コメントコードとして提示してください。

私のソリューション、39バイト:

ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!

16進数: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

1
あなたは本当に小屋で暑い帽子にぶつかりました:)
aditsu

いいね 奇妙だが、いい。勝利条件を「最短」に設定しているので、タグを[code-golf]に変更し、新しい説明的なタグを追加します。気に入らない場合はお知らせください。
dmckee ---元モデレーター子猫

回答:


16

7バイト

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

32ビットとして

00000000  6641              inc cx
00000002  2521217321        and eax,0x21732121

64ビットとして

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

and64ビットバージョンが常にジャンプするように、キャリーフラグをクリアします。64ビットの6641場合、オペランドサイズのオーバーライドに続いrex.bてオペランドのサイズがand16ビットになります。32ビットで6641は、これは完全な命令であるため、andプレフィックスはなく、32ビットのオペランドサイズを持ちます。これは、and64ビットモードでのみ実行される2バイトの命令を与えることによって消費される即値バイト数を変更します。


1
1kに達しました。
DavidC

この動作はCPU固有です。一部の64ビットシステムは、64ビットモードで66プレフィックスを無視します。
ピーターフェリー

@peterferrieそのためのリファレンスはありますか?私の読書はREX.Wが設定されている場合66Hプレフィックスは無視されていることですが、これが唯一のREX.Bあり
ジェフ・リーディ

すみません、私は間違っています。そのように影響するのは制御の移転のみです(たとえば、66 e8はIntelで16ビットIPに切り替えません)。
ピーターフェリー

7

11バイト

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

32ビットでは0x41がちょうどという事実を使用しますが、inc %ecx64ビットではrax次のpop命令のターゲットレジスタを変更するのはプレフィックスです。

        .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

OSXでこれを書いた場合、アセンブラーが異なる場合があります。

これで呼び出します:

#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;
}

2

7バイト

66プレフィックスに依存しません。

$$@$Au!

32ビット:

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

ALは、INCの後にビット0が設定され、2番目のANDはそれを保持し、分岐が行われます。

64ビット:

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

ALは、最初のANDの後にビット0がクリアされ、分岐は行われません。


0

C9hのみが印刷可能である場合...

32ビット:

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

ARPLはZフラグをクリアし、ブランチを取得します。

64ビット:

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

XORはZフラグを設定し、MOVSXDはそれを変更せず、分岐は行われません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.