the cutting room mirror

Format:PE

PE, or Portable Executable, is the executable file format employed by Microsoft on Windows ever since NT in 1993, and 95 for home users.
Canonically valid executables meant as programs that can be run will always come with the extension .exe, which became known as its name for most people due to being its one unique identifier for them.
Dynamic Link Libraries, also known as DLLs are also simply PEs with different characteristics and the canonical extension .dll instead, having the same format besides that.

DOS Stub#

Since in 1993 the MZ format was still being widely used for DOS executables in Microsoft systems, PE employs a DOS executable at the start so that it can also run on them.
All variables listed here are 2 bytes long.

Location

Specification name

Type

Meaning

0x00

e_magic

String

MZ stub signature. Used to identify the MZ stub and a PE as a whole, still.

0x08

e_cparhdr (executable_countparagraphsheader)

Word/integer

The count of paragraphs (16 bytes long) in the header. Practically, only used to point to where does the MZ executable's code begin.

0x3C

e_lfanew (*longfileaddress)

Word/integer

Pointer to the own header of the current extension to the MZ format, in this case the PE header from PE.

Earlier PEs will have a second mode just for DOS, but by the time of Windows 95's popularity, this executable was turned into a stub with only the following message:

This program must be run under Win32

A second, alternative message was also used in the following years and became the default DOS stub message by the time of Windows XP, replacing the first message.

This program cannot be run on DOS mode.

Machine Code#

What comes before it will always be 16-bit x86 instructions to show the message, since Microsoft only ran its products on x86 PCs at the time. They are noted down here in Assembly.

push cs       ; Move Code Segment to stack
pop ds        ; Move stack to Data Segment
; Without these two instructions above, code cannot run on DOS
mov dx, $0E   ; Move 0x0E (offset to the message) to whole data register
mov ah, $09   ; Move 9 ("Print String" function on INT 21h) to high accumulator
int $21       ; INT 21h
mov ax, $4C01 ; Move 0x4C01 to accumulator, effectively requesting functions 0x4C and 1 ("Terminate a Process" and "Read Keyboard Input", giving control back to DOS)
int $21       ; INT 21h (Executes the functions)