The Portable Executable, commonly abbreviated as PE, is the main executable file format used by Windows. Files such as .exe, .dll, .sys, .scr, .ocx, and .cpl are usually PE files.
A PE file is not just a block of executable code. It is a structured file that tells Windows how the image should be loaded into memory, where execution should begin, which DLLs are required, which functions are imported or exported, which sections should be readable, writable, or executable, and which security-related metadata is present.
For malware development, malware analysis, reverse engineering, and detection engineering, understanding PE files is important because many techniques require inspecting or modifying PE headers, parsing imports and exports, resolving API addresses, locating sections, reading shellcode from resources, identifying packed binaries, or understanding how the Windows loader prepares an image for execution.
The official Microsoft PE format documentation can be found here.
It is worth noting that this module and future modules will often refer to executables, DLLs, and drivers as images. In Windows terminology, an image is an executable file that can be mapped into a process address space.
Before going into the PE headers, it is important to understand that a PE file has two different views:
These two views are related, but they are not identical. Windows does not simply copy the PE file byte-for-byte into memory and execute it from the same layout that exists on disk. Instead, the Windows loader (Windows component that loads PE files into memory) reads the PE headers, reserves memory for the image, maps each section into memory, applies the required permissions, resolves API imports, performs relocations if needed, and then transfers execution to the image entry point. This means that a byte located at file offset 0x400 on disk may appear at RVA 0x1000 after the image is mapped into memory.
All the aforementioned steps implemented by the Windows Loader will be discussed in further detail in later modules.
A PE file contains several headers followed by one or more sections. The headers describe the file, and the sections contain the actual code, data, imports, exports, resources, relocation information, and other runtime metadata. A simplified PE layout looks like this:

Both the Data Directories and PE sections contain multiple entries. The exact section layout can differ from one binary to another. For example, a binary compiled with Visual Studio will not always have the same section layout as a packed binary, a manually modified binary, a driver, a .NET executable, or a binary produced by another compiler. The same idea applies to Data Directories; some images may have an import directory but no export directory. Others may have a resource directory, relocation directory, TLS directory, exception directory, or CLR header depending on how the binary was built and what features it uses.
This module will introduce the important Data Directory entries and common PE sections, explain what they represent, and show why they matter when analyzing or parsing PE files. Before covering them, however, we need to examine the PE header structures themselves, as shown in the following sections.
The first structure in a PE file is the DOS header, represented by the IMAGE_DOS_HEADER structure. Unfortunately, at the time of writing this module, this structure does not appear to have a dedicated Microsoft documentation page, but it is defined in the winnt.h header file.
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // Offset to the NT header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;