기계 코드에서 인쇄 가능한 가시 ASCII 문자 만 사용하여 x86 / x86-64에서 다르게 분기


14

작업은 간단합니다. 머신 코드에서 인쇄 가능한 보이는 ASCII 문자 0x21 ... 0x7e (공백 및 델타는 허용되지 않음) 만 사용하여 x86 (32 비트) 및 x86-64 (64 비트)로 다르게 분기되는 프로그램을 작성하십시오. .

  • 조건부 어셈블리는 허용되지 않습니다.
  • API 호출을 사용할 수 없습니다.
  • 커널 모드 (링 0) 코드 사용은 허용되지 않습니다.
  • Linux 또는 다른 보호 모드 OS에서는 IA-32 및 x86-64 모두에서 예외를 발생시키지 않고 코드를 실행해야합니다.
  • 기능은 명령 행 매개 변수에 의존해서는 안됩니다.
  • 모든 명령어는 0x21 ... 0x7e (십진수 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
당신은 정말로 오두막집에서 뜨거운 모자를 쳤습니다 :)
SE는 EVIL이기 때문에 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 비트 버전이 항상 점프하도록 carry 플래그를 지 웁니다. 64 비트의 6641경우 피연산자 크기 재정의 다음에 rex.b피연산자 크기가 and16 비트로 나타납니다. 32 비트 6641에서이 명령은 완전한 명령어이므로 and접두사가없고 32 비트 피연산자 크기가 있습니다. and64 비트 모드에서만 실행되는 2 바이트의 명령을 제공 하여 소비되는 즉시 바이트 수를 변경 합니다.


1
1k에 도달 한 것을 축하합니다.
DavidC

이 동작은 CPU에 따라 다릅니다. 일부 64 비트 시스템은 64 비트 모드에서 66 접두사를 무시합니다.
피터 페리 2013

@peterferrie 당신은 그것에 대한 참조가 있습니까? 내 독서는 REX.W를 설정하면 66h 접두사가 무시되지만 REX.B 만 있습니다.
Geoff Reedy

실례합니다 이러한 방식으로 영향을받는 것은 제어 이전에만 해당됩니다 (예 : 66 e8은 인텔에서 16 비트 IP로 전환되지 않음).
피터 페리

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은 INC 다음에 비트 0을 설정하고 두 번째 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.