This gets really fun when you add in more architectures. If I recall correctly Midnight Sun CTF had a challenge that asked for shellcode that worked on x86/x86-64/ARM/ARM64/MIPSLE. The technique is the same, if more difficult: a header that quickly peels off and branches to an architecture-specific block, but also doesn’t encode something invalid or likely to crash. I’ll have to see if I have that header somewhere…
Edit: found it, it's \xeb\x16\x00\x32\x10\x00\x00\x3a\x0f\x00\x00\x2a\x19\x00\x00\x14\x31\x00\x01\x04\x00\x00\x00\x00. Here's how it looks on each architecture:
For x86/x86-64 you'll notice the code is identical; we had a separate polyglot specifically for it to detect 64-bit and branch away. (0x40 is inc eax on x86, and a REX prefix on x86-64.)
Edit: found it, it's \xeb\x16\x00\x32\x10\x00\x00\x3a\x0f\x00\x00\x2a\x19\x00\x00\x14\x31\x00\x01\x04\x00\x00\x00\x00. Here's how it looks on each architecture:
x86/x86-64:
ARM: ARM64: MIPSLE: For x86/x86-64 you'll notice the code is identical; we had a separate polyglot specifically for it to detect 64-bit and branch away. (0x40 is inc eax on x86, and a REX prefix on x86-64.)