Article by Ayman Alheraki on January 11 2026 10:37 AM
When a program is compiled into an executable, it carries a binary header at the beginning. This header is crucial — it tells the operating system how to load and run the program. These headers differ across platforms and architectures (32-bit vs 64-bit). This article presents a comprehensive overview of executable headers on Windows, Linux, and macOS, including real hexadecimal headers extracted from binaries.
Windows executables follow the PE format, built on top of an older DOS format for compatibility.
DOS Header (IMAGE_DOS_HEADER)
Starts with MZ (0x4D 0x5A) and contains a pointer to the PE header at offset 0x3C.
PE Signature: "PE\0\0" = 0x50 0x45 0x00 0x00
COFF File Header (IMAGE_FILE_HEADER) Contains machine type, section count, and flags.
Optional Header (IMAGE_OPTIONAL_HEADER) Contains entry point, memory layout, and more. It differs between 32-bit (PE32) and 64-bit (PE32+).
+----------------------------+| DOS Header (IMAGE_DOS_HEADER) [64 bytes]| Magic = "MZ"| Offset to PE header at offset 0x3C (e_lfanew)+----------------------------+| DOS Stub Program [Variable size]+----------------------------+| PE Signature ("PE\0\0") [4 bytes]+----------------------------+| COFF File Header (IMAGE_FILE_HEADER)| Machine (x86=0x014C, x64=0x8664)| NumberOfSections| TimeDateStamp| PointerToSymbolTable| NumberOfSymbols| SizeOfOptionalHeader| Characteristics+----------------------------+| Optional Header (IMAGE_OPTIONAL_HEADER)| (Differs for 32-bit and 64-bit)| Magic = 0x10B for PE32, 0x20B for PE32+ (x64)| EntryPoint, ImageBase, SectionAlignment, etc.+----------------------------+| Section Headers (.text, .data, .rdata, etc.)+----------------------------+PE32 is used for 32-bit executables.
PE32+ is used for 64-bit executables.
In PE32+, ImageBase and other fields are 64-bit wide.
Use dumpbin /headers yourfile.exe or objdump -f yourfile.exe to inspect.
4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00...80 00 00 00 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4CCD 21 50 45 00 00 4C 01 03 00 00 00 00 00 00 0000 00 00 00 E0 00 02 01 0B 01 08 00 00 10 00 0000 20 00 00 00 00 00 00 00 10 00 00 00 10 00 0000 30 00 00 00 40 00 00 ...4D 5A = "MZ"
50 45 00 00 = "PE\0\0"
0B 01 = PE32 (for PE32+ use 0B 02)
The ELF format is used on Linux and many Unix-like systems.
Magic Bytes: 0x7F 45 4C 46 = "\x7FELF"
Class: 0x01 = 32-bit, 0x02 = 64-bit
Data Encoding: 0x01 = Little Endian, 0x02 = Big Endian
File Type: 0x02 = Executable
Machine Type: e.g., 0x03 = x86, 0x3E = x86_64
+----------------------------+| ELF Header (EI_MAG = 0x7F 'E' 'L' 'F')| e_ident[16]| EI_CLASS (32/64-bit)| EI_DATA (Little/Big Endian)| EI_VERSION| e_type (Executable, Relocatable, etc.)| e_machine (e.g., x86=3, x86-64=62)| e_entry (Entry point)| e_phoff (Program header offset)| e_shoff (Section header offset)| e_flags| e_ehsize+----------------------------+| Program Headers| (Segment info: LOAD, DYNAMIC, etc.)+----------------------------+| Section Headers| (.text, .data, .bss, .symtab, etc.)+----------------------------+Use readelf -h yourfile or objdump -f yourfile to inspect.
7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 0002 00 3E 00 01 00 00 00 78 56 34 12 00 00 00 0040 00 00 00 00 00 00 00 80 00 00 00 00 00 00 0000 00 00 00 40 00 38 00 09 00 40 00 1B 00 1A 007F 45 4C 46 = ELF
02 = 64-bit
3E 00 = x86-64 architecture
For 32-bit ELF, use 01 in the class byte, and the rest of the layout is adjusted accordingly.
Mach-O (Mach Object) is the native binary format for macOS and iOS.
Magic: 0xCFFAEDFE = little-endian 64-bit Mach-O
CPU Type: e.g., 0x01000007 = x86_64
File Type: 0x02 = Executable
Load Commands: Follow immediately after the header
macOS (and iOS) uses the Mach-O (Mach Object) format, which can also be packaged in Fat/Universal binaries (for Intel and ARM).
+----------------------------+| Mach Header| magic (e.g., 0xFEEDFACE, 0xFEEDFACF for 64-bit)| cputype (e.g., x86, x86_64, arm64)| cpusubtype| filetype (e.g., MH_EXECUTE, MH_DYLIB)| ncmds (number of load commands)| sizeofcmds (size of all load commands)| flags| reserved (64-bit only)+----------------------------+| Load Commands| LC_SEGMENT / LC_SEGMENT_64| LC_SYMTAB| LC_MAIN (entry point)+----------------------------+| Sections (within segments)| __TEXT, __DATA, __LINKEDIT, etc.+----------------------------+Use otool -hv yourfile or mach-o-analyzer to view header info.
CF FA ED FE 07 00 00 01 03 00 00 00 02 00 00 000A 00 00 00 F0 00 00 00 85 00 00 00 00 00 00 00CF FA ED FE = Mach-O 64-bit (little endian)
07 00 00 01 = x86_64
02 00 00 00 = Executable
CA FE BA BE 00 00 00 02 00 00 00 07 00 00 00 0300 00 00 01 00 00 00 00 00 02 00 00 00 00 00 00Used when a binary includes multiple architectures (e.g., Intel + ARM).
| Platform | Format | Magic Bytes (Hex) | Description |
|---|---|---|---|
| Windows | PE | 4D 5A → 50 45 00 00 | DOS header, then PE signature |
| Linux | ELF | 7F 45 4C 46 | \x7FELF |
| macOS | Mach-O | CF FA ED FE | 64-bit Mach-O (little endian) |
| macOS | FAT | CA FE BA BE | Fat binary (multi-arch) |
Linux/macOS:
xxd your_binary | head
readelf -h your_binary
otool -hv your_macho_file
Windows:
Use CFF Explorer, PE-bear, or dumpbin /headers yourfile.exe
Understanding executable headers is essential for:
Writing loaders, debuggers, or virtual machines
Malware analysis and reverse engineering
Building cross-platform compilers and linkers
Developing custom packers or OS loaders
These headers reveal the true structure and intent of the binary before the code even runs.