PE/bash/x86/C/Ruby/Python2, 330 bytes
score ≈ 0.0445
xxd:
00000000: 4d5a 7544 3d27 5c27 273b 0a23 2f2a 0a27 MZuD='\'';.#/*.'
00000010: 3b65 6368 6f20 6261 7368 2023 273b 7072 ;echo bash #';pr
00000020: 696e 7420 2830 616e 6427 5275 6279 2729 int (0and'Ruby')
00000030: 6f72 2750 7974 686f 6e32 2723 4000 0000 or'Python2'#@...
00000040: 5045 0000 4c01 0100 b802 00cd 10b8 0013 PE..L...........
00000050: eb2e 0000 7000 0301 0b01 0000 7075 7473 ....p.......puts
00000060: 0000 0065 7869 7400 f000 0000 6d73 7663 ...exit.....msvc
00000070: 7274 0000 0000 4000 0400 0000 0400 0000 rt....@.........
00000080: bb0f 00bd 8c7c eb58 0400 0000 7838 3600 .....|.X....x86.
00000090: 0004 0000 0100 0000 0000 0000 0300 0000 ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000b0: 0000 0000 0200 0000 0000 0000 0000 0000 ................
000000c0: 0d01 0000 0000 0000 2e74 6578 7400 0000 .........text...
000000d0: 4500 0000 f000 0000 4500 0000 f000 0000 E.......E.......
000000e0: b903 00ba 0000 cd10 ebfe 0000 0000 0000 ................
000000f0: 6840 0040 00ff 1501 0140 00ff 1505 0140 h@.@.....@.....@
00000100: 005a 0000 0061 0000 0000 0000 0000 0000 .Z...a..........
00000110: 0000 0000 0000 0000 006c 0000 0001 0100 .........l......
00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000130: 0000 0000 002a 2f0d 6d61 696e 2829 7b70 .....*/.main(){p
00000140: 7574 7328 2243 2229 3b7d uts("C");}
source:
; nasm
base equ 0x400000
;
; DOS header
;
; The only two fields that matter are e_magic and e_lfanew
mzhdr:
dw "MZ" ; DOS e_magic
jnz a
db "='\'';",10,"#/*",10,"';echo bash #';print (0and'Ruby')or'Python2'#"
times 64 - 4 - ($-$$) db 0
dd 64 ; DOS e_lfanew
;
; NT headers
;
PE: dd "PE" ; PE signature
;
; NT file header
;
filehdr:
dw 0x014C ; Machine (Intel 386)
dw 1 ; NumberOfSections
; dd 0 ; TimeDateStamp UNUSED
; dd 0 ; PointerToSymbolTable UNUSED
; dd 0 ; NumberOfSymbols UNUSED
a:
mov ax,2 ; 3 bytes
int 0x10 ; 2 bytes ; clear screen
mov ax,0x1300 ; 3 bytes
jmp b ; 2 bytes
dw 0 ; 2 bytes
dw opthdrsize ; SizeOfOptionalHeader
dw 0x103 ; Characteristics
;
; NT optional header
;
opthdr:
dw 0x10B ; Magic (PE32)
; db 0 ; MajorLinkerVersion UNUSED
; db 0 ; MinorLinkerVersion UNUSED
; dd 0 ; SizeOfCode UNUSED
; dd 0 ; SizeOfInitializedData UNUSED
; dd 0 ; SizeOfUninitializedData UNUSED
puts_: db 0,0,"puts",0
exit_: db 0,0,"exit",0
dd start ; AddressOfEntryPoint
; dd 0 ; BaseOfCode UNUSED
; dd 0 ; BaseOfData UNUSED
crt: db "msvcrt",0,0
dd base ; ImageBase
dd 4 ; SectionAlignment
dd 4 ; FileAlignment
; dw 0 ; MajorOperatingSystemVersion UNUSED
; dw 0 ; MinorOperatingSystemVersion UNUSED
; dw 0 ; MajorImageVersion UNUSED
; dw 0 ; MinorImageVersion UNUSED
b:
mov bx,0xF ; 3 bytes ; video page, video attribute
mov bp,0x7C00+x86 ; 3 bytes
jmp c ; 2 bytes
dw 4 ; MajorSubsystemVersion
dw 0 ; MinorSubsystemVersion UNUSED
; dd 0 ; Win32VersionValue UNUSED
x86: db "x86",0
dd 1024 ; SizeOfImage
dd 1 ; SizeOfHeaders nonzero for Windows XP
dd 0 ; CheckSum UNUSED
dw 3 ; Subsystem (Console)
dw 0 ; DllCharacteristics UNUSED
dd 0 ; SizeOfStackReserve
dd 0 ; SizeOfStackCommit
dd 0 ; SizeOfHeapReserve
dd 0 ; SizeOfHeapCommit UNUSED
dd 0 ; LoaderFlags UNUSED
dd 2 ; NumberOfRvaAndSizes for Windows 10; UNUSED in Windows XP
;
; Data directories (part of optional header)
;
dd 0, 0 ; Export Table UNUSED
dd idata, 0 ; Import Table
opthdrsize equ $ - opthdr
;
; Code section header
;
db ".text", 0, 0, 0 ; Name
dd codesize ; VirtualSize
dd code ; VirtualAddress
dd codesize ; SizeOfRawData
dd code ; PointerToRawData
; dd 0 ; PointerToRelocations UNUSED
; dd 0 ; PointerToLinenumbers UNUSED
; dw 0 ; NumberOfRelocations UNUSED
c:
mov cx,3 ; 3 bytes ; text length
mov dx,0 ; 3 bytes ; text position
int 0x10 ; 2 bytes ; print text
jmp $ ; 2 bytes ; enter infinite loop
dw 0 ; NumberOfLinenumbers UNUSED
dd 0 ; Characteristics UNUSED
;
; Code section data
;
align 4, db 0
code:
;
; Entry point
;
bits 32
start:
push base + PE
call [base + puts]
call [base + exit]
;
; Import address table (array of IMAGE_THUNK_DATA structures)
;
iat:
puts: dd puts_ ; Import puts by name
exit: dd exit_ ; Import exit by name
dd 0 ; terminator
;
; Import table (array of IMAGE_IMPORT_DESCRIPTOR structures)
;
idata:
dd 0 ; OriginalFirstThunk UNUSED
dd 0 ; TimeDateStamp UNUSED
dd 0 ; ForwarderChain UNUSED
dd crt ; Name
dd iat ; FirstThunk
times 5 dd 0 ; terminator
codesize equ $ - code
db "*/",13,"main(){puts(",34,"C",34,");}"
Try it online
in bash/C/Ruby/Python2.
Portable Executable polyglot.exe
was tested on Windows 10 2004 64-bit and Windows XP SP3 32-bit.
Raw x86 was tested in QEMU 6.2.0: qemu-system-x86_64 -no-fd-bootchk -fda polyglot
.
Inspired by/based on αcτµαlly pδrταblε εxεcµταblε, which is a PE/bash/x86 polyglot.
Also based on this hello world PE.
Portable Executable
Main differences from hello world:
- DOS and NT headers don't overlap here, NT header immediately follows DOS header
puts
and exit
are imported by name
As mentioned in hello world PE answer, the minimal distance between the start of NT headers and end of file on Windows 10 is 264 bytes,
so minimal PE file size with non-overlapping DOS and NT headers on Windows 10 is 64+264=328 bytes (64 == sizeof(IMAGE_DOS_HEADER)).
It means that all but 2 bytes of C code at the end of the file are required for this file to work on Windows 10.
The program prints its own PE signature, which is kinda cute.
x86
αpε uses jno 0x4a
jo 0x4a
to jump to the main boot code. I ran out of space in the DOS header
and didn't want to rearrange the code, so I use the less robust single jnz a
instruction. This means I rely on BIOS to not set
bp
to 1
before calling boot sector (M
in MZ
is dec bp
). QEMU and Bochs set bp
to 0
. Unconditional jmp
is not used
because its code is 0xEB
, on the other hand almost all short conditional jumps have ASCII
codes, see here.
Main code:
mov ax,2
int 0x10 ; clear screen
mov ax,0x1300
mov bx,0xF ; video page, video attribute
mov bp,0x7C00+x86
mov cx,3 ; text length
mov dx,0 ; text position
int 0x10 ; print text
jmp $ ; enter infinite loop
x86: db "x86"
This code is split into 3 parts (a:
,b:
,c:
) to fit inside unused fields of NT headers.
It looks like both b
and c
would fit inside section header, but it breaks PE on Windows XP for some reason.
bash/C/Ruby/Python2
MZuD='\'';
#/*
';echo bash #';print (0and'Ruby')or'Python2'# ... */␍main(){puts("C");}
Pretty standard stuff:
- bash:
\
inside ''
does not escape
- C: allows single
#
on a line
- Ruby:
0
is truthy
Also, I used the fact that C treats \r
as newline, unlike bash/Ruby/Python2 (link).
This allows to use just #
in bash/Ruby/Python2 to comment out the rest of the polyglot.
print()
for Perl, Python, PHP, Processing and R be considered trivial or is it acceptable? \$\endgroup\$n * n / length
\$\endgroup\$