สาขาแตกต่างกันใน x86 / x86-64 โดยใช้เฉพาะอักขระ ASCII ที่มองเห็นได้ซึ่งพิมพ์ได้ในรหัสเครื่อง


14

ภารกิจนั้นง่าย: เขียนโปรแกรมที่แตกต่างใน x86 (32- บิต) และ x86-64 (64- บิต) โดยใช้เฉพาะอักขระ ASCII ที่มองเห็นได้ที่พิมพ์ได้ 0x21 ... 0x7e (ไม่อนุญาตให้เว้นวรรคและเดล) ในรหัสเครื่อง .

  • ไม่อนุญาตให้ประกอบแบบมีเงื่อนไข
  • ไม่อนุญาตให้ใช้การเรียก API
  • ไม่อนุญาตให้ใช้รหัสโหมดเคอร์เนล (แหวน 0)
  • รหัสจะต้องทำงานโดยไม่ทำให้เกิดข้อยกเว้นทั้ง IA-32 และ x86-64 ใน Linux หรือใน OS ที่ได้รับการป้องกันอื่น ๆ
  • การทำงานจะต้องไม่ขึ้นอยู่กับพารามิเตอร์บรรทัดคำสั่ง
  • คำแนะนำทั้งหมดจะต้องเข้ารหัสในรหัสเครื่องโดยใช้เฉพาะอักขระ ASCII ในช่วง 0x21 ... 0x7e (33 ... 126 ทศนิยม) เช่น cpuidไม่มีข้อ จำกัด (เป็น0f a2) เว้นแต่คุณจะใช้รหัสแก้ไขด้วยตนเอง
  • รหัสไบนารีเดียวกันต้องทำงานใน x86 และ x86-64 แต่เนื่องจากส่วนหัวของไฟล์ (ELF / ELF64 / ฯลฯ ) อาจแตกต่างกันคุณอาจต้องรวบรวมและเชื่อมโยงอีกครั้ง อย่างไรก็ตามรหัสไบนารีจะต้องไม่เปลี่ยนแปลง
  • โซลูชันควรทำงานกับโปรเซสเซอร์ทั้งหมดระหว่าง i386 ... Core i7 แต่ฉันสนใจในโซลูชันที่ จำกัด ยิ่งกว่า
  • รหัสจะต้องสาขาใน 32-bit x86 แต่ไม่ได้อยู่ใน x86-64 หรือในทางกลับกัน แต่การใช้การกระโดดแบบมีเงื่อนไขนั้นไม่ได้เป็นข้อกำหนด ที่อยู่เป้าหมายสาขาจะต้องมีที่ว่างสำหรับรหัสบางส่วนอย่างน้อย 2 ไบต์ของพื้นที่ที่มีการกระโดดสั้น ( jmp rel8) พอดี

คำตอบที่ชนะคือคำตอบที่ใช้ไบต์น้อยที่สุดในรหัสเครื่อง ไบต์ในส่วนหัวของไฟล์ (ตัวอย่างเช่น 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

1
คุณโดนหมวกร้อนแรงในกระท่อมจริงๆ :)
aditsu ออกเพราะ SE นั้นชั่วร้าย

ดี แปลก แต่ก็ดี เนื่องจากคุณกำลังตั้งเงื่อนไขการชนะว่า "สั้นที่สุด" ฉันจะเปลี่ยนแท็กเป็น [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

andล้างแฟล็กการพกพาดังนั้นเวอร์ชัน 64 บิตจะข้ามไปเสมอ สำหรับ 64- บิต6641ขนาดตัวถูกดำเนินการแทนที่แล้วตามด้วยrex.bขนาดตัวถูกดำเนินการสำหรับandออกมาเป็น 16 บิต ใน 32- บิต6641เป็นคำสั่งที่สมบูรณ์ดังนั้นจึงandไม่มีคำนำหน้าและมีขนาดตัวถูกดำเนินการ 32 บิต สิ่งนี้จะเปลี่ยนจำนวนของไบต์ทันทีที่ใช้โดยการandให้คำแนะนำสองไบต์ที่ดำเนินการในโหมด 64 บิตเท่านั้น


1
ขอแสดงความยินดีกับการเข้าถึง 1k
DavidC

ลักษณะการทำงานนี้เป็นเฉพาะของ CPU ระบบ 64 บิตบางระบบจะเพิกเฉยต่อคำนำหน้า 66 ในโหมด 64 บิต
เตอร์ปีเตอร์

@peterferrie คุณมีการอ้างอิงสำหรับสิ่งนั้นหรือ การอ่านของฉันคือว่าคำนำหน้า 66h จะถูกละเว้นเมื่อ REX.W ถูกตั้งค่า แต่นี้มีเพียง REX.B
Geoff Reedy

ขอโทษนะฉันผิด มันเป็นเพียงการควบคุมการถ่ายโอนที่ได้รับผลกระทบด้วยวิธีนั้น (เช่น 66 e8 ไม่ได้เปลี่ยนเป็น IP แบบ 16 บิตบน Intel)
เตอร์ปีเตอร์

7

11 ไบต์

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

ใช้ข้อเท็จจริงที่ว่าใน 32- บิต 0x41 เป็นเพียงinc %ecxในขณะที่ใน 64- บิตมันเป็น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 จะมีชุดบิต 0 หลังจาก INC ที่สองและจะเก็บไว้สาขาจะถูกยึด

64 บิต:

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

AL จะมีบิต 0 ที่ชัดเจนหลังจาก AND แรกและสาขาจะไม่ถูกนำมาใช้


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.