2016-06-27 12 views
9

Có thể cho cùng một byte mã máy để tìm hiểu xem chúng đang chạy ở chế độ 32 hay 64 bit, và sau đó thực hiện những việc khác nhau?x86-32/x86-64 mảnh mã máy-đa-bit phát hiện chế độ 64 bit tại thời gian chạy?

tức là viết polyglot mã máy.

Thông thường bạn có thể phát hiện tại thời gian xây dựng với các macro #ifdef. Hoặc trong C, bạn có thể viết một if() với hằng số biên dịch theo thời gian và có trình biên dịch tối ưu hóa phía bên kia của nó.

Điều này chỉ hữu ích cho các trường hợp lạ, như có thể tiêm mã, hoặc chỉ để xem liệu có thể.


Xem chi tiết: a polyglot ARM/x86 machine code để phân nhánh tới các địa chỉ khác nhau tùy thuộc vào kiến ​​trúc nào giải mã byte.

Trả lời

9

Cách dễ nhất là sử dụng các mã mở một byte inc được đặt lại là tiền tố REX ở chế độ 64 bit. Một tiền tố REX không ảnh hưởng đến jcc, vì vậy bạn có thể làm:

xor eax,eax  ; clear ZF 
db 0x40    ; 32bit: inc eax. 64bit: useless REX prefix 
jz .64bit_mode  ; REX jcc works fine 

Dưới đây là một chương trình đầy đủ Linux/NASM sử dụng syscall-exit(1) nếu chạy như 64bit, hoặc int 0x80 để exit(0) nếu chạy như 32bit.

Việc sử dụng BITS 32 và BITS 64 đảm bảo rằng nó cũng lắp ráp cùng một mã máy. (Và có, tôi đã kiểm tra với objdump -d để hiển thị byte mã máy thô)

Mặc dù vậy, tôi đã sử dụng db 0x40 thay vì inc eax, để làm rõ hơn những gì đặc biệt.

BITS 32 
global _start 
_start: 
     xor eax,eax   ; clear ZF 
     db 0x40     ; 32bit: inc eax. 64bit: useless REX prefix 
     jz  .64bit_mode  ; REX jcc still works 

     ;jmp .64bit_mode ; uncomment to test that the 64bit code does fault in a 32bit binary 

.32bit_mode: 
     xor  ebx,ebx 
     mov  eax, 1   ; exit(0) 
     int  0x80 


BITS 64 
.64bit_mode: 
     lea rdx, [rel _start]  ; An instruction that won't assemble in 32-bit mode. 
     ;; arbitrary 64bit code here 

     mov edi, 1 
     mov eax, 231 ; exit_group(1). 
     syscall   ; This does SIGILL if this is run in 32bit mode on Intel CPUs 

;;;;; Or as a callable function: 
BITS 32 
am_i_32bit: ;; returns false only in 64bit mode 
     xor  eax,eax 

     db 0x40     ; 32bit: inc eax 
           ; 64bit: REX.W=0 
     ;nop      ; REX nop is REX xchg eax,eax 
     ret      ; REX ret works normally, too 

Tested và làm việc. Tôi xây dựng nó hai lần để có được siêu dữ liệu ELF khác nhau xung quanh cùng một mã máy.

$ yasm -felf64 -Worphan-labels -gdwarf2 x86-polyglot-32-64.asm && ld -o x86-polyglot.64bit x86-polyglot-32-64.o 
$ yasm -felf32 -Worphan-labels -gdwarf2 x86-polyglot-32-64.asm && ld -melf_i386 -o x86-polyglot.32bit x86-polyglot-32-64.o 
$ ./x86-polyglot.32bit && echo 32bit || echo 64bit 
32bit 
$ ./x86-polyglot.64bit && echo 32bit || echo 64bit 
64bit 

(xây dựng các lệnh từ Assembling 32-bit binaries on a 64-bit system (GNU toolchain), liên kết từ các phần FAQ trong thẻ wiki).

+0

Chỉnh sửa nhỏ: 'syscall' hợp lệ trên hầu hết CPU của AMD ở chế độ 32 bit. – Jester

+0

@Jester: Cảm ơn, tôi đã tự hỏi tại sao nó tháo rời mà không có khiếu nại ở chế độ 32 bit, và được lắp ráp trong một phiên bản trước của mã. Nhưng nó đã làm việc cho tôi (trên một Intel Merom) để xác nhận rằng tôi có một SIGILL từ chạy chi nhánh sai trong chế độ 32bit. ('lea' chỉ giải mã thành một' dec' và một lá với chế độ địa chỉ khác nhưng vẫn hợp lệ.) Dù sao, sửa chú thích :) –

+0

AMD đã phát minh ra 'syscall' và Intel đã phát minh ra' sysenter'. Tất nhiên AMD giữ 'syscall' khi tạo ra chế độ 64 bit để khi Intel chấp nhận điều đó, họ cũng có' syscall'. – Jester

Các vấn đề liên quan