Understanding the PE
Understanding PE
What does it mean ?
The Portable Executable (PE) format is the standard file format used for executables, object code, dynamic-link libraries (DLLs), and other binary files on both 32-bit and 64-bit versions of Windows, as well as in UEFI environments. It serves as the primary format for executable files on Windows NT-based systems, including file types such as .exe
, .dll
, .sys
(system drivers), and .mui
. Essentially, the PE format is a structured data container that provides the Windows loader with all the necessary information to correctly handle and execute the code. This includes references to dynamic libraries, import and export tables for APIs, resource data, and thread-local storage (TLS) details.
Basically PE format is a fundamental format for executable files, object code, DLLs and other types of native files on Windows. It consists of a number of headers, sections that tell the Windows Loader how to put the file into memory and prepare it to run.
Portable Executable ( PE ) Structure

DOS Header
The first part of the PE format is the IMAGE_DOS_HEADER ( DOS Header ) which is a 64 bytes long structure that looks like this:
The most important members that we need to know from a malware development and analysis perspective are:
- e_magic
- e_lfanew
All the other members contain information that is useful for the DOS loader to calculate offsets and tells us very little about the file.
1 | typedef struct _IMAGE_DOS_HEADER |
Size of Types (on typical Windows platforms):
WORD
= 2 bytesLONG
= 4 bytes
The IMAGE_DOS_HEADER
is exactly 64 bytes because:
- It contains only fixed-size data types (
WORD
andLONG
) - No padding is required (the layout is tightly packed)
- It was deliberately designed to be a compact and predictable header format at the beginning of executable files.
Add all of these up:
1 | 2 * 14 (first 14 WORD fields) = 28 |
The DOS Header is what makes the PE file an MS-DOS executable.

DOS Stub
While PE files maintain backward compatibility for historical reasons, modern Windows PE files are not designed to run in DOS. Instead, they include a DOS stub—a small piece of code that displays an error message if the file is executed in a DOS environment. By default, this message is: “This program cannot be run in DOS mode.”

Rich Header
Present in executables built with Microsoft development tools, the Rich Header contains metadata about the build environment. Both malware authors and analysts can leverage this information in various insightful ways.

NT Headers
The structure looks like the following:
1 | typedef struct _IMAGE_NT_HEADERS64 { |
There are 2 variants of NT Headers:
- For the x86 architecture it’s called: IMAGE_NT_HEADERS
- For the x64 architecture it’s called: IMAGE_NT_HEADERS64
The NT Headers structure contains 3 main parts:
- PE Signature: A 4-byte
DWORD
that marks the file as a PE image. It always holds the value0x00004550
, which corresponds to the ASCII string'PE\0\0'
. - File Header: A standard
COFF
File Header - A structure with seven fields that holds key information about the PE file, including the machine architecture, time-date stamp, size of the section table, size of the optional header, and various file characteristics. - Optional Header: Despite its name, the Optional Header is a critical part of the NT Headers for executable image files (such as
.exe
files). It’s called “optional” because it is omitted in certain file types like object files, but it is required for executables. This header supplies essential information to the operating system loader.- Data Directories: An array of 16 data directories, each containing important information used by the PE loader during program execution.
NT Headers - File Header
The structure looks like the following:
1 | typedef struct _IMAGE_FILE_HEADER { |
This structure is also referred to as the COFF File Header, where COFF means Common Object File Format Header
.
NT Headers - Optional Header
There are 2 variants of Optional Header:
- For the x86 architecture it’s called: _IMAGE_OPTIONAL_HEADER
- For the x64 architecture it’s called: _IMAGE_OPTIONAL_HEADER64
The structure looks like the following:
1 | typedef struct _IMAGE_OPTIONAL_HEADER64 { |
Optional Header - Data Directories
DataDirectory is one of the most important members of the Optional Header. It is an array with a data type of IMAGE_DATA_DIRECTORY
and contains up to 16 structures.

The array has a size of IMAGE_NUMBEROF_DIRECTORY_ENTRIES
:
1 | IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; |
Which is a set to a constant of 16: #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
Each IMAGE_DATA_DIRECTORY struct has two members: VirtualAddress and Size
1 | typedef struct _IMAGE_DATA_DIRECTORY { |
- VirtualAddress is a relative virtual address (RVA) that points to the start of the data directory.
- Size is the size of the data directory in bytes.
Data directories store information essential to the PE loader. Each entry is identified by an index and points to a specific offset within the file.
Among these, the most important are the Export Directory (IMAGE_DIRECTORY_ENTRY_EXPORT
, index 0) and the Import Address Table (IMAGE_DIRECTORY_ENTRY_IAT
).
The Export Directory is commonly found in DLLs that provide exported functions. It’s a data structure that holds the addresses of these exported functions and variables, allowing other executables to access and use them.
The Import Address Table (IAT) holds the addresses of functions and data imported from other executables, allowing the program to access external code and resources at runtime.
Section Headers
Following the Optional Header and preceding the actual sections are the Section Headers. These headers provide metadata about each section in the PE file.
Each Section Header is represented by the IMAGE_SECTION_HEADER
structure, which is defined in winnt.h
as follows:
1 | typedef struct _IMAGE_SECTION_HEADER { |
Sections
Sections hold the actual data of the executable and make up the remainder of the PE file following the headers—specifically, after the section headers.
Some sections have special names that reflect their purpose. We’ll cover a few of these, but a complete list can be found in Microsoft’s official documentation under the “Special Sections” section.
- .text – Holds the program’s executable code.
- .data – Stores initialized global and static variables.
- .bss – Contains uninitialized data that is zeroed at runtime.
- .rdata – Contains read-only initialized data, such as constants.
- .edata – Holds the export table, which lists functions and data the file exports.
- .idata – Stores the import table, detailing external functions and libraries the file uses.
- .reloc – Contains relocation information used when the image is loaded at a different base address.
- .rsrc – Includes application resources such as icons, images, dialogs, and embedded binaries.

Resources
A very good video that explains all of this is https://www.youtube.com/watch?v=OkX2lIf9YEM from Guided Hacking.
Some good articles and overviews: https://0xrick.github.io/win-internals/pe1/