PE (Windows executable) file format
Portable Executable — the Windows .exe/.dll binary format.
- MIME type
- application/vnd.microsoft.portable-executable
- Extensions
- .exe, .dll, .sys
- Category
- Executables & bytecode
Magic-number signature
| 0x00000000 | 4D5A | MZ | DOS 'MZ' stub |
A 4-byte offset at 0x3C points to the 'PE\0\0' (50 45 00 00) signature.
About the PE (Windows executable) format
PE is the Windows format for executables, DLLs and drivers, and it is still wearing a costume from 1985: every PE file begins with a working MS-DOS program whose only job is to print a message and exit, so that running a Windows binary on DOS produced a sentence instead of a crash. The real header is reached through a pointer stored at offset 0x3C, which leads to the four bytes 'PE\0\0', a COFF file header naming the machine and the section count, and an optional header — not optional at all for an image — that carries the entry point, the preferred load address and the data directories.
Header layout, byte by byte
| Offset | Length | Field | Meaning |
|---|---|---|---|
| 0 | 2 | e_magic ('MZ') | 4D 5A, the initials of Mark Zbikowski, one of the original MS-DOS architects. Every PE image, every DOS executable and every .NET assembly starts with these two bytes, so they identify the family and nothing more specific. |
| 2 | 58 | Rest of the MS-DOS header | Page counts, relocation table offset, initial stack and instruction pointers for real-mode DOS. Windows ignores all of it; a linker writes a template. Only its length matters, because it fixes where the pointer to the real header lives. |
| 60 | 4 | e_lfanew | Little-endian file offset of the PE signature — the one field in the DOS header Windows reads. 0x80 (128) is the classic value for MSVC and GCC output, but it is genuinely variable, so never assume it. Everything below is quoted relative to this value. |
| 64 | variable | MS-DOS stub program | Real 16-bit code plus the string 'This program cannot be run in DOS mode'. It is dead weight that every Windows binary still carries, and it ends where e_lfanew points. |
| e_lfanew + 0 | 4 | PE signature | 50 45 00 00, the letters PE and two NUL bytes. If these four bytes are not at the offset stored at 60, the file is a plain DOS executable or a damaged PE, and the loader refuses it. |
| e_lfanew + 4 | 2 | Machine | Target CPU: 0x8664 for x64, 0x014C for 32-bit x86, 0xAA64 for ARM64. This is the field behind the 'not a valid Win32 application' message when the architecture does not match. |
| e_lfanew + 6 | 2 | NumberOfSections | How many section headers follow the optional header. Typically five to twenty; a packed or protected binary often has two or three with names that are not .text or .rdata. |
| e_lfanew + 8 | 4 | TimeDateStamp | Seconds since 1970 at link time — a real timestamp on older toolchains, and a reproducible-build hash on newer ones, so a date in 2107 or 1970 is a build-system artefact rather than damage. |
| e_lfanew + 12 | 8 | PointerToSymbolTable + NumberOfSymbols | Two four-byte fields for the COFF symbol table. Deprecated for images and normally zero on anything Microsoft's linker produced; MinGW binaries still fill them in, which is why a GCC-built .exe carries symbols a Visual Studio one does not. |
| e_lfanew + 20 | 2 | SizeOfOptionalHeader | Byte length of the optional header that follows: 224 for a 32-bit image, 240 for 64-bit, 0 for an object file. Adding it to the optional header's start is how you find the section table without parsing anything in between. |
| e_lfanew + 22 | 2 | Characteristics | Attribute flags. 0x0002 marks it a runnable image and 0x2000 marks it a DLL, which is the actual difference between a .dll and a .exe — the extension is only a convention, and one bit here flips it. |
| e_lfanew + 24 | 2 | Optional header Magic | 0x010B for PE32, 0x020B for PE32+, the 64-bit variant. It is not the same thing as the Machine field, and both need to agree: PE32+ widens the image base and several size fields, so reading a PE32+ header as PE32 yields plausible-looking nonsense. |
| e_lfanew + 24 + SizeOfOptionalHeader | 40 per section | Section table | One 40-byte entry per section: an eight-byte name padded with NULs, the virtual size and address, then the raw size and file offset. This is the table that maps what you see in a hex editor to what the process sees in memory. |
Telling it apart from its neighbours
| Easily confused with | How to tell |
|---|---|
| MS-DOS executable | A genuine 16-bit DOS program also starts with 4D 5A and has no PE header at all — the bytes at offset 0x3C are just part of its header rather than a pointer. Follow the pointer: if the target is past the end of the file, or the four bytes there are not 50 45 00 00, what you have is DOS-only or damaged, which is exactly why an identifier can only call a bare MZ file 'likely a PE'. |
| ELF executable | Nothing subtle at byte level — 4D 5A against 7F 45 4C 46 — but it is the single most common mix-up in a cross-compiled build tree, where .so and .dll land side by side and both may be named lib-something. Read offset 0 before believing an extension. |
| OLE2 / CFB | Both are Microsoft container formats from the same era and both hold 'a program' in the loose sense, so a .doc with macros and a .exe get confused in triage. OLE2 compound files begin D0 CF 11 E0 A1 B1 1A E1, never MZ, and are structured as a FAT-like directory of streams rather than as sections mapped into memory. |
| .NET assembly | A managed .exe or .dll is a completely normal PE file — same MZ, same PE signature, same section table — that carries almost no native code. The tell is data directory entry 14 (the CLR header) being non-zero, and a .text section whose contents are CIL metadata; that is why a .NET binary decompiles cleanly while a C++ one does not. |
How these files break
- e_lfanew pointing nowhere. Prepending anything to a PE file — an HTML error page, a UTF-8 BOM, a mis-merged patch — leaves the MZ magic intact while the offset at 0x3C now measures from the wrong base, so the loader looks for 'PE\0\0' and finds section data. The file identifies as an executable and refuses to run.
- Truncation. Section data sits after all the headers, so a partial download parses perfectly: machine, section count and entry point all read correctly while the last section's raw data is short. Windows reports a generic image error, and the diagnosis is comparing each section's PointerToRawData plus SizeOfRawData against the actual file size.
- An invalidated Authenticode signature. Any byte changed outside the excluded ranges breaks the signed hash, so a file that has been patched, appended to, or had an installer stub stapled on still runs but reports as unsigned or tampered — and on a machine with signature enforcement, does not run at all.
- Text-mode transfer. The same CRLF translation that ruins any binary shifts everything after the first 0D 0A. Because the PE headers are reached by absolute offsets rather than by scanning, the failure surfaces immediately as an unreadable header rather than as a subtle bug.
- Appended payload treated as damage. Installers, self-extracting archives and some licence wrappers legitimately append data past the end of the last section, so a file being larger than its headers describe is normal for PE and is not, on its own, evidence of corruption.
Where the format stands today
PE is what every Windows program, DLL, kernel driver and .NET assembly is, and it also carries UEFI firmware applications, so a modern boot loader on any platform is usually a PE file. Nothing has replaced it on Windows and nothing is trying to: the format has absorbed 64-bit addressing, managed code, ARM64 and ARM64EC hybrid images without a break in compatibility, so a 1993 binary still has a recognisable header. Its reach outside Windows is limited to Wine and UEFI, which is why cross-platform projects ship a PE, an ELF and a Mach-O of the same program rather than one artefact. For anyone reading bytes, the practical consequence is that PE is the only one of the three that hides its real header behind a pointer — everything interesting starts at whatever offset 0x3C says.
Open PE (Windows executable) files on this site
Every one of these runs in your browser — the file never leaves your machine.
Related formats
Formats that are easy to confuse with PE (Windows executable) — compare their leading bytes.
| Format | First bytes | Extension |
|---|---|---|
| ELF executable | 7F 45 4C 46 | .elf |
| Mach-O executable | CF FA ED FE | .dylib |
| OLE2 / CFB | D0 CF 11 E0 A1 B1 | .doc |