Chi nhánh khác nhau trong x86 / x86-64 chỉ sử dụng các ký tự ASCII có thể nhìn thấy có thể in được trong mã máy


14

Nhiệm vụ rất đơn giản: viết chương trình phân nhánh khác nhau theo x86 (32 bit) và x86-64 (64 bit) chỉ sử dụng các ký tự ASCII hiển thị có thể in 0x21 ... 0x7e (không được phép dấu cách và del) trong mã máy .

  • Lắp ráp có điều kiện không được phép.
  • Sử dụng các lệnh gọi API không được phép.
  • Không được phép sử dụng mã chế độ kernel (ring 0).
  • Mã phải chạy mà không gây ra ngoại lệ trong cả IA-32 và x86-64 trong Linux hoặc trong một số HĐH chế độ được bảo vệ khác.
  • Các chức năng không được phụ thuộc vào các tham số dòng lệnh.
  • Tất cả các hướng dẫn phải được mã hóa bằng mã máy chỉ sử dụng các ký tự ASCII trong phạm vi 0x21 ... 0x7e (33 ... 126 thập phân). Vì vậy, ví dụ. cpuidvượt quá giới hạn ( 0f a2trừ khi), trừ khi bạn sử dụng mã tự sửa đổi.
  • Cùng một mã nhị phân phải chạy trong x86 và x86-64, nhưng vì các tiêu đề tệp (ELF / ELF64 / v.v.) có thể khác nhau, bạn có thể cần phải lắp ráp và liên kết lại. Tuy nhiên, mã nhị phân không được thay đổi.
  • Các giải pháp nên hoạt động trên tất cả các bộ xử lý giữa i386 ... Core i7, nhưng tôi cũng quan tâm đến các giải pháp hạn chế hơn.
  • Mã phải phân nhánh trong 32-bit x86 nhưng không phải trong x86-64 hoặc ngược lại, nhưng sử dụng các bước nhảy có điều kiện không phải là một yêu cầu (nhảy hoặc gọi gián tiếp cũng được chấp nhận). Địa chỉ đích của nhánh phải sao cho có không gian cho một số mã, ít nhất là 2 byte không gian trong đó một bước nhảy ngắn ( jmp rel8) phù hợp.

Câu trả lời chiến thắng là câu trả lời sử dụng ít byte nhất trong mã máy. Các byte trong tiêu đề tệp (chẳng hạn ELF / ELF64) không được tính và bất kỳ byte mã nào sau nhánh (cho mục đích thử nghiệm, v.v.) cũng không được tính.

Vui lòng trình bày câu trả lời của bạn dưới dạng ASCII, dưới dạng byte thập lục phân và dưới dạng mã nhận xét.

Giải pháp của tôi, 39 byte:

ASCII: fhotfhatfhitfhutfhotfhatfhitfhut_H3<$t!

thập lục phân : 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.

Mã số:

; 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
Bạn thực sự trúng mũ nóng trong túp lều :)
aditsu

Đẹp. Lạ, nhưng hay. Vì bạn đang đặt điều kiện chiến thắng là "ngắn nhất" nên tôi sẽ thay đổi thẻ thành [code-golf] và thêm một số thẻ mô tả mới. Nếu bạn không thích chúng, hãy cho tôi biết.
dmckee --- ex-moderator mèo con

Câu trả lời:


16

7 byte

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

Như 32 bit

00000000  6641              inc cx
00000002  2521217321        and eax,0x21732121

Như 64 bit

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

andxóa cờ mang theo để phiên bản 64 bit luôn nhảy. Đối với 64 bit, 6641ghi đè kích thước toán hạng theo sau là rex.bkích thước toán hạng cho andra là 16 bit. Trên 32 bit 6641là một lệnh hoàn chỉnh nên andkhông có tiền tố và có kích thước toán hạng 32 bit. Điều này thay đổi số lượng byte ngay lập tức được tiêu thụ bằng cách andđưa ra hai byte hướng dẫn chỉ được thực hiện trong chế độ 64 bit.


1
Chúc mừng bạn đã đạt 1k.
DavidC

hành vi này là cụ thể CPU. Một số hệ thống 64 bit sẽ bỏ qua tiền tố 66 ở chế độ 64 bit.
perr ferrie

@peterferrie Bạn có tài liệu tham khảo cho điều đó không? Tôi đọc được rằng một tiền tố 66 giờ bị bỏ qua khi REX.W được đặt nhưng điều này chỉ có REX.B
Geoff Reedy

xin lỗi, tôi sai rồi Chỉ có quyền kiểm soát chuyển nhượng bị ảnh hưởng theo cách đó (ví dụ: 66 e8 không chuyển sang IP 16 bit trên Intel).
perr ferrie

7

11 byte

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

Sử dụng thực tế là trong 32 bit, 0x41 chỉ là inc %ecx, trong khi ở 64 bit, nó là raxtiền tố sửa đổi thanh ghi đích của lệnh sau 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

Đã viết điều này trên OSX, trình biên dịch của bạn có thể khác.

Gọi nó bằng cái này:

#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 byte

Không dựa vào tiền tố 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

AL sẽ có bit 0 được đặt sau INC, AND thứ hai sẽ bảo toàn nó, nhánh sẽ được lấy.

64-bit:

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

AL sẽ có bit 0 rõ ràng sau AND đầu tiên, nhánh sẽ không được lấy.


0

Nếu chỉ C9h có thể in được ...

32-bit:

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

ARPL sẽ xóa cờ Z, khiến chi nhánh bị lấy.

64-bit:

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

XOR sẽ đặt cờ Z, MOVSXD sẽ không thay đổi nó, nhánh sẽ không được lấy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.