Branch differently in x86 / x86-64 using only printable visible ASCII characters in the machine code
7 bytes
0000000: 6641 2521 2173 21 fA%!!s!
As 32 bit
00000000 6641 inc cx
00000002 2521217321 and eax,0x21732121
As 64 bit
00000000 6641252121 and ax,0x2121
00000005 7321 jnc 0x28
and
clears the carry flag so the 64 bit version always jumps. For 64-bit the 6641
is the operand size override followed by rex.b
so the operand size for the and
comes out as 16 bit. On 32-bit the 6641
is a complete instruction so the and
has no prefix and has a 32-bit operand size. This changes the number of immediate bytes consumed by the and
giving two bytes of instructions that are only executed in 64-bit mode.
11 bytes
ascii: j6Xj3AX,3t!
hex: 6a 36 58 6a 33 41 58 2c 33 74 21
Uses the fact that in 32-bit, 0x41 is just inc %ecx
, whereas in 64-bit it is the rax
prefix that modifies the target register of the following pop
instruction.
.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
Wrote this on OSX, your assembler might be different.
Call it with this:
#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;
}
7 bytes
Not relying on 66 prefix.
$$@$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 will have bit 0 set after the INC, the second AND will preserve it, the branch will be taken.
64-bit:
00000000 24 24 and al,24h
00000002 40 24 41 and al,41h
00000005 75 21 jne 00000028h
AL will have bit 0 clear after the first AND, the branch will not be taken.