Article by Ayman Alheraki on January 11 2026 10:37 AM
Professional developers
Security researchers
Low-level engineers
OS developers
Reverse engineers
The EXE file format on Windows uses the Portable Executable (PE) format, which is based on the COFF structure. The file consists of the following sections:
+--------------------------+| MS-DOS Header | ← 64 bytes starting with "MZ"+--------------------------+| DOS Stub | ← Displays a message in DOS mode+--------------------------+| PE Signature ("PE\0\0") | ← Always starts at e_lfanew offset+--------------------------+| COFF File Header |+--------------------------+| Optional Header | ← Contains the entry point address+--------------------------+| Section Headers | ← For sections like .text, .data, etc.+--------------------------+| Section Data |+--------------------------+
Let's analyze the first 1024 bytes of a sample executable using a Hex editor:
4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00...50 45 00 00 4C 01 03 00 00 00 00 00 00 00 00 00E0 00 02 01 0B 01 06 00 00 10 00 00 00 20 00 00...
IMAGE_DOS_HEADER)typedef struct _IMAGE_DOS_HEADER { WORD e_magic; // 0x00: "MZ" = 0x5A4D WORD e_cblp; // 0x02: Bytes on last page of file WORD e_cp; // 0x04: Pages in file WORD e_crlc; // 0x06: Relocations ... LONG e_lfanew; // 0x3C: Offset to PE Header (critical)};4D 5A = e_magic = 0x5A4D ("MZ")90 00 = e_cblp = 0x009003 00 = e_cp = 0x000300 00 = e_crlc = 0x0000...80 00 00 00 = e_lfanew = 0x00000080 → PE Header starts hereExplanation:
e_lfanew is the offset in the file where the PE header starts, typically at offset 0x80 or 0xF8 depending on the linker.
IMAGE_NT_HEADERS)50 45 00 00 = "PE\0\0" = 0x00004550
IMAGE_FILE_HEADER)typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics;};4C 01 = Machine = 0x014C (Intel 386)03 00 = NumberOfSections = 378 56 34 12 = TimeDateStamp = Build timestamp00 00 00 00 = PointerToSymbols = Usually 000 00 00 00 = NumberOfSymbols = Usually 0E0 00 = SizeOfOptionalHeader= 0x00E002 01 = Characteristics = 0x0102 → Executable | 32-bit
IMAGE_OPTIONAL_HEADER)Despite its name, this section is mandatory and contains essential runtime information:
typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; // PE32 (0x10B) or PE32+ (0x20B) BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; ...};0B 01 = Magic = 0x010B → PE3206 = MajorLinkerVersion = v600 = MinorLinkerVersion = v000 10 00 00 = SizeOfCode = 0x0000100000 20 00 00 = SizeOfInitializedData = 0x2000...10 00 00 00 = AddressOfEntryPoint = 0x00001000When executed, the Windows loader begins execution at:
xxxxxxxxxxImageBase + AddressOfEntryPointIf ImageBase = 0x400000 (default), the entry point is 0x401000.
IMAGE_SECTION_HEADER)Each section like .text or .data is defined by:
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[8]; // Example: ".text" DWORD VirtualSize; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; ...};2E 74 65 78 74 00 00 00 = ".text"00 10 00 00 = VirtualSize = 0x100000 10 00 00 = VirtualAddress = 0x100000 10 00 00 = SizeOfRawData = 0x100000 04 00 00 = PointerToRawData = 0x400
Running a command like:
cl main.cpp /link /ENTRY:main /SUBSYSTEM:CONSOLEThe linker performs:
Creates section headers
Inserts compiled code into .text section
Sets the entry point address
Builds the PE header and updates e_lfanew
Calculates padding and alignment
Merges objects and resolves symbols
dumpbin /headers your.exe
sigcheck (from Sysinternals)
PEview, CFF Explorer
IDA Pro, Ghidra (for reverse engineering)
int main() { HANDLE hFile = CreateFile("yourfile.exe", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); BYTE* base = (BYTE*)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)base; IMAGE_NT_HEADERS* pe = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew);
std::cout << "PE Signature: " << std::hex << pe->Signature << "\n"; std::cout << "Entry Point: " << std::hex << pe->OptionalHeader.AddressOfEntryPoint << "\n"; std::cout << "Image Base : " << std::hex << pe->OptionalHeader.ImageBase << "\n";
UnmapViewOfFile(base); CloseHandle(hMapping); CloseHandle(hFile);}
The PE header is the core structure that enables Windows to load and execute an executable file.
It contains crucial information such as the entry point, section layout, memory configuration, and dependencies.
Mastery of this format is critical for:
Building custom linkers or loaders
Designing reverse engineering tools
Implementing security or anti-tampering techniques
Developing embedded or custom operating systems