相同的机器码是否能够确定它们是在32位还是64位模式下运行,并执行不同的操作?
即,编写多语言机器码。
通常情况下,您可以通过#ifdef
宏在构建时进行检测。或者在C语言中,您可以编写一个带有编译时常量作为条件的if()
,并让编译器优化掉其另一侧。
这只对奇怪的情况有用,比如代码注入,或者只是为了看看是否可能。
另请参见:多语言ARM / x86机器码,可根据解码字节的体系结构分支到不同的地址。
相同的机器码是否能够确定它们是在32位还是64位模式下运行,并执行不同的操作?
即,编写多语言机器码。
通常情况下,您可以通过#ifdef
宏在构建时进行检测。或者在C语言中,您可以编写一个带有编译时常量作为条件的if()
,并让编译器优化掉其另一侧。
这只对奇怪的情况有用,比如代码注入,或者只是为了看看是否可能。
另请参见:多语言ARM / x86机器码,可根据解码字节的体系结构分支到不同的地址。
inc
操作码,在64位模式下被重新用作REX前缀。 REX前缀对jcc
没有影响,因此您可以执行以下操作:xor eax,eax ; clear ZF
db 0x40 ; 32bit: inc eax. 64bit: useless REX prefix
jz .64bit_mode ; REX jcc works fine
提醒:通常你不想将此作为编译二进制文件的一部分。在构建时检测模式,以便基于此的任何决策都可以优化掉而不是在运行时执行。例如,使用 #ifdef __x86_64__
和/或 sizeof(void*)
(但不要忘记 ILP32 x32 ABI 在长模式下具有 32 位指针)。
syscall
退出(1)如果以64位运行,或者使用int 0x80
退出(0)如果以32位运行。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
从在64位系统上组装32位二进制文件(GNU工具链)中构建命令,链接自x86标签wiki中的FAQ部分。
syscall
在大多数 AMD CPU 的 32 位模式下是有效的。 - Jestersyscall
/sysenter
是在64位之前出现的。 - Jestersyscall
规范已经在1997年5月发布为_SYSCALL和SYSRET指令规范应用说明,订单号为21086_,比你链接的64位代码的邮件列表讨论早了三年,而且32位的AMD K6-2系列处理器自1998年以来就支持它。PS:是的,在32位模式下它的工作方式不同。 - Jester