ByteScope

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

0x000000004D5AMZDOS '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

OffsetLengthFieldMeaning
02e_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.
258Rest of the MS-DOS headerPage 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.
604e_lfanewLittle-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.
64variableMS-DOS stub programReal 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 + 04PE signature50 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 + 42MachineTarget 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 + 62NumberOfSectionsHow 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 + 84TimeDateStampSeconds 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 + 128PointerToSymbolTable + NumberOfSymbolsTwo 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 + 202SizeOfOptionalHeaderByte 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 + 222CharacteristicsAttribute 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 + 242Optional header Magic0x010B 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 + SizeOfOptionalHeader40 per sectionSection tableOne 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 withHow to tell
MS-DOS executableA 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 executableNothing 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 / CFBBoth 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 assemblyA 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.

FormatFirst bytesExtension
ELF executable7F 45 4C 46.elf
Mach-O executableCF FA ED FE.dylib
OLE2 / CFBD0 CF 11 E0 A1 B1.doc

Frequently asked questions

What is a magic number in a file?

The first few bytes of a file, which identify its format regardless of what the name says. A PNG always starts with 89 50 4E 47; a ZIP with 50 4B 03 04. Operating systems, browsers and the file command all read these bytes rather than trusting the extension, which is why renaming a file does not change what it actually is.

How do I check a file's magic number myself?

On macOS or Linux, xxd -l 16 yourfile prints the first 16 bytes; on Windows, Format-Hex -Path yourfile -Count 16 in PowerShell. Or drop the file into the File Type Identifier on this site, which reads the bytes locally and names the format for you — nothing is uploaded.

Why do two formats share the same magic number?

Because one is built on the other. Every docx, xlsx, pptx, apk, jar and epub begins with the ZIP signature, since all of them are ZIP archives underneath; telling them apart means looking at the entries inside. Java class files and macOS universal binaries both start with CA FE BA BE and are distinguished by the field that follows.

Can a file have no magic number at all?

Yes. Text-based formats — XML, HTML, JSON, SVG, CSV — have no fixed byte signature, so they are recognized structurally instead: a leading <?xml declaration, a <svg element, or a { followed by a quoted key. That detection is a good guess rather than a certainty, which is why this reference marks those formats as probe-detected.

Does a matching magic number mean the file is safe?

No. It tells you what the format is, not whether the contents are harmless — a genuine PDF or docx can still carry a malicious payload. Identifying the true type is a useful first check, especially when the extension disagrees with the bytes, but it is not a malware scan.

Have a file that might be in the PE (Windows executable) format? Identify it with the File Type Identifier — it reads the magic number locally, nothing is uploaded. Or browse all formats.