The 64-Bit Windows Executable Format

November 17, 2015

This page attempts to summarize what is required to create a working 64-bit PE file.

The Windows executable format has two variants: PE32 is the format for 32-bit programs, and PE32+ is the format for 64-bit programs. The formats are similar, but not identical. This page describes only the 64-bit variant, PE32+.

Many of these fields can be set to other values, but reasonable defaults are provided. For example, the file alignment can be set to any power of two between 512 and 64K, but there is little reason to use a larger alignment. Fields omitted from this description must be zero.

The DOS stub program

In nearly all PE executables, there is a DOS stub program after the DOS header and before the PE header. The table below shows where the DOS stub program goes, but the data for a DOS stub program is not listed. (A PE file will still run if the DOS stub is all zero.)


High-Level File Layout

Fixed-layout headers (DOS, PE, Data Directories)
Section headers (up to 96)
.text section data
.rdata section data
.data section data

Detailed File Layout

PositionValue Description
DOS header
00004D 5A "MZ", the DOS executable magic number
003C000000C8 File position of PE header
0040... The DOS stub program goes here (not required for 64-bit execution)
PE header
00C850 45 00 00 "PE\0\0", the PE magic number
00CC8664 Architecture (AMD64)
00CE???? Number of sections (between 1 and 96)
00DC00F0 Size of the "optional" header
00DE0022 Characteristics flags: executable, large address aware
"Optional" header(Not optional in executable PE files)
00E00B 02 Optional header magic number
00E2?? Linker major version (typically 0E or thereabouts)
00E3?? Linker minor version (typically 00)
00F0???????? RVA of code entry point (should point within .text section)
00F80000000140000000 Image base address
010000001000 Section alignment in memory (must be 4 kB)
010400000200 File alignment of sections (must be 512 B)
01080005 OS major version (typically 6)
010A0000 OS minor version (typically 0)
01100005 Subsystem major version (same as OS version)
01120000 Subsystem minor version (same as OS version)
0118???????? Size of image in memory, including headers, rounded up to Section Alignment
011C???????? Size of all headers, rounded up to file-alignment (typically 0200)
0124???? Subsystem (2 = Windows GUI, 3 = Windows console)
01268160 DLL characteristics flags: high entropy ASLR, relocatable, NX compatible, Terminal Server Aware
01280000000000100000 Stack memory reserved (recommend 1 MB)
01300000000000001000 Stack memory committed (recommend 4 kB)
01380000000000100000 Heap memory reserved (recommend 1 MB)
01400000000000001000 Heap memory committed (recommend 4 kB)
014C00000010 Number of Data Directory entries
Data Directory EntriesThis array describes the location and size of 16 Data Directories. The data pointed to by these Directories is typically part of a read-only section, later in the file.
0158DataDirectoryEntry[1] Import Directory: This data describes the symbols that this executable needs to import from DLLs.
(Corresponds to file position 0700.)
01B0DataDirectoryEntry[12]Import Address Table Directory: This is an array of pointers. When the executable is loaded, the addresses of imported functions will be placed in this array. The actual program in this executable then calls imported functions by looking up their address in this array.
(Corresponds to file position 0600.)
01C8DataDirectoryEntry[15]The final Data Directory entry

The rest of the file does not have a fixed layout. There are up to 96 section headers immediately following the Data Directory entries. After that comes the data for each of those sections, with each section's data aligned to the File Alignment.

The following data is an example of a small but valid three-section PE file.

PositionValue Description
SectionHeader[0].text section header
01D0".text\0\0\0" Section name (8 bytes, zero-padded)
01D800000034 Size in memory
01DC00001000 RVA (a multiple of the section alignment)
01E000000200 Size in file (a multiple of the file alignment)
01E400000400 File position (a multiple of the file alignment)
01F460000020 Flags: executable, readable, code
SectionHeader[1] .rdata section header
01F8".rdata\0\0" Section name
020000000154 Size in memory
020400002000 RVA
020800000200 Size in file
020C00000600 File position
021C40000040 Flags: readable, initialized data
SectionHeader[2] .data section header
0220".data\0\0\0" Section name
022800002400 Size in memory
022C00003000 RVA
023000000200 Size in file
023400000800 File position
0244C0000040 Flags: readable, writable, initialized data
.text section data
0400... x64 machine code (finally!)
.rdata section data
0600 Import information. (details below)
String literals and other readonly data.
.data section data
0800... Space for initialized and zero-initialized (.bss) variables.

The import information in the .rdata section is complex, so it is described separately here:

PositionValue Description
Import Address Table for kernel32.dllThese entries are 8 bytes, exactly the size of a 64-bit pointer. Each entry will be replaced with the address of a symbol that was imported from a DLL. Program instructions will reference these entries in order to find and call imported functions.
(This array must be identical to the Import Lookup Table, shown below.)
0600(u64)2138 RVA of the Hint/Name pair for this symbol. (Corresponds to file position 0738.)
0608(u64)zero Array terminator.
Import DirectoryImport info for kernel32.dll
070000002128 RVA of Import Lookup Table for this DLL. (Corresponds to file position 0728.)
070C&"kernel32.dll\0" RVA of the name of the DLL.
071000002000 RVA of the Import Address Table. (Corresponds to file position 0600.)
0714(ImportDirectoryEntry)zero Array terminator.
Import Lookup Table for kernel32.dllThere is one Import Lookup Table for each DLL referenced in the Import Directory.
0728(u64)2138 RVA of the Hint/Name pair for this symbol. (Corresponds to file position 0738.)
0730(u64)zero Array terminator.
Hint/Name PairsPointed to by Import Lookup Table entries.
0738"\0\0ExitProcess\0" A 16-bit value followed by a null-terminated string.
Null-terminated stringsDLL file names pointed to by Import Directory entries.


struct DataDirectoryEntry
	u32 DirectoryRVA;
	u32 DirectorySize;
// Repeated according to number-of-sections specified in the header.
struct SectionHeader
	u8[8] Name;		// Null-padded string.
	u32 VirtualSize;
	u32 VirtualAddress;
	u32 SizeInFile;
	u32 AddressInFile;
	u8[12] Unused;		// Must be zero.
	u32 Characteristics;
// Import Directory
// One entry per DLL imported from. (e.g. kernel32.dll)
// Array is terminated by an all-zero entry.
struct ImportDirectoryEntry
	u32 ImportLookupTable;	// RVA of an array in .rdata.
	u32 Timestamp;		// Can be zero.
	u32 ForwarderChain;	// Should be zero.
	u32 Name;		// RVA of name of DLL, a null-terminated string in .rdata.
	u32 ImportAddressTable;	// RVA of an array in .rdata.
// Import Lookup Table: Array of these entries.
// Array is terminated by an all-zero entry.
// One entry per function imported from a DLL.
// One array for each entry in the Import Address Table Directory.
// The Import Address Table is a copy of this array, stored elsewhere in .rdata section.
struct ImportLookupTableEntry
	u64 Entry;
	// The low 31 bits are the RVA of a Hint/Name table entry, in .rdata.
	// Bits 63 to 32 are zero.
	// (This describes only the name-based lookup format. Ordinal-based lookup is also possible.)
// The Import Lookup Table points at these.
// These structs are typically allocated in .rdata.
// Must begin on an even address.
struct HintNamePair
	u16 OrdinalHint;	// can be zero)
	u8[*] SymbolName;	// null-terminated string

Additional notes

The in-file and in-memory sizes of sections must be multiples of their respective "section alignment" values in the header. The in-memory size will usually be larger; whenever the section is larger in memory than on disk, the extra bytes will be zero. This is a convenient way to define the .bss section!