相同字节的机器代码是否可以确定它们是在32位还是64位模式下运行,然后做不同的事情?
即。写polyglot机器码。
通常,您可以使用#ifdef
宏在构建时检测到。或者在C中,你可以编写一个带有编译时常量的if()
作为条件,并让编译器优化它的另一面。
这仅适用于奇怪的情况,例如代码注入,或只是为了查看它是否可行。
另请参阅:a polyglot ARM / x86 machine code分支到不同的地址,具体取决于解码字节的架构。
答案 0 :(得分:10)
最简单的方法是使用在64位模式下重新用作REX前缀的单字节inc
操作码。 REX前缀对jcc
没有影响,因此您可以执行以下操作:
xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc works fine
另请参阅根据其在codegolf.SE上的Determine your language's version执行的模式返回16,32或64的3向多语言。
提醒:通常,您不希望将此作为已编译二进制文件的一部分。在构建时检测模式,因此基于此的任何决策都可以优化而不是在运行时完成。例如使用#ifdef __x86_64__
和/或sizeof(void*)
(但不要忘记ILP32 x32 ABI在长模式下有32位指针)。
这是一个完整的Linux / NASM程序,如果以64位运行,则使用syscall
到exit(1)
;如果以32位运行,则使用int 0x80
到exit(0)
。
使用BITS 32和BITS 64可确保它以任何方式组装到相同的机器代码。 (是的,我检查了objdump -d
以显示原始机器代码字节)
即便如此,我使用db 0x40
代替inc eax
,以使其更加清晰。
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
经过测试并正常工作。我构建它两次以获得围绕相同机器代码的不同ELF元数据。
$ 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
(来自Assembling 32-bit binaries on a 64-bit system (GNU toolchain)的构建命令,链接自x86标记wiki中的FAQ部分。)