Windows PE File Structure
Last updated
Last updated
Shameless plug
This course is provided to you for free by the Malcore team: https://m4lc.io/course/winpe/register
Consider registering, and using Malcore, so we can continue to provide free content for the entire community. You can also join our Discord server here: https://m4lc.io/course/winpe/discord
We offer free threat intel in our Discord via our custom designed Discord bot. Join the Discord to discuss this course in further detail or to ask questions.
You can also support us by buying us a coffee
The Windows Portable Executable (PE) file format is a structure used by Windows binary files. It is derived from the Common Object File Format (COFF) used in Unix systems; it is fundamental for Windows systems.
In this course we will break down the Windows PE structure thoroughly.
NOTE: To help visualize some of this information you can see a full analysis of this file done by Malcore here: https://m4lc.io/course/winpe/full
The purpose of the DOS header is to maintain backwards compatibility with older systems. It is a remnant of the DOS era, and tells the system that this is an executable in DOS while providing a pointer to the PE header where modern executable information starts.
e_magic
: The signature that the system recognizes as a valid DOS executable
Offset: 0x0000
e_lfanew
: The pointer to the PE header location
Offset: 0x003C
Offset: 0x0040
Small program that runs in DOS mode, usually contains the message: This program cannot be run in DOS mode.
Offset is specified by e_lfanew
header.
Marks the start of the PE file
For example if e_lfanew
is 0x000080
the signature is at 0x80
The signature in bytes is always: \x50\x45\x00\x00
and displayed in ASCII it is always: PE\0\0
Offset is typically directly after the PE signature
Contains general metadata about the file
Machine
Offset: 0x00
Description: Indicates the target architecture of the file.
Possible Values:
0x014C
– Intel 386 (x86).
0x0200
– Intel Itanium.
0x8664
– x64 (AMD64).
0x01C0
– ARM.
0x01C4
– ARMv7.
0xAA64
– ARM64.
0x0100
– MIPS R3000.
NOTE: There may be more values, we just couldn't think of any other ones
Number of Sections
Offset: 0x02
Description: The number of section the PE file (counts the section header not the full section).
Timestamp
Offset: 0x04
Description: Contains the timestamp of the file creation or last modified time.
Usage: This can be used to analyze when a PE file was compiled or built.
You can see a timestamp after compilation being retrieved from the file here: https://m4lc.io/course/winpe/timestamp
PointerToSymbolTable
Offset: 0x08
Description: Points to the start of the symbol table in the file.
In executables and DLL files, this field is usually set to 0x00000000
.
Number of Symbols
Offset: 0x0C
Description: The number of entries in the symbol table. Is usually set to 0x00000000
.
SizeOfOptionalHeader
Offset: 0x10
Description: Specifies the size of the Optional Header
.
For 32-bit files, the size is usually 0x00E0
(224 bytes).
For 64-bit files, the size is usually 0x00F0
(240 bytes).
Characteristics
Offset: 0x12
Description: Contains flags that describe the attributes of the PE file (such as if it's an exe or a DLL).
Possible Values:
0x0002
– Executable image.
0x0020
– Application can handle addresses larger than 2GB (large address aware).
0x0100
– The file is a DLL.
0x2000
– The file is a system file.
0x4000
– File is a dynamically loadable driver.
0x8000
– File is removable media-aware.
Despite its name this is required for executable images and DLL files. It contains a bunch of information that is needed to properly load the PE file and execute the file in memory. You can see a lot of this information pulled out of a real file and visualize it here: https://m4lc.io/course/winpe/info
Magic
Offset: 0x00 (PE32 and PE32+)
Size: 2 bytes
Description:
Identifies the file format:
0x10B for 32 bit.
0x20B for 64 bit.
Used to determine between 32-bit and 64-bit executable formats.
Major Linker Version
Offset: 0x02
Size: 1 byte
Description:
The major version number of the linker that generated the file.
Indicates the compatibility of the file with the linker software.
Possible values:
2 (0x02
)
Linker Version: Microsoft Linker 2.x
Visual Studio Version: Early Microsoft C Compilers
3 (0x03
)
Linker Version: Microsoft Linker 3.x
Visual Studio Version: Early Microsoft C/C++ Compilers
5 (0x05
)
Linker Version: Microsoft Linker 5.x
Visual Studio Version: Visual Studio 97
6 (0x06
)
Linker Version: Microsoft Linker 6.x
Visual Studio Version: Visual Studio 6.0
7 (0x07
)
Linker Version: Microsoft Linker 7.0
Visual Studio Version: Visual Studio .NET 2002
7 (0x07
)
Linker Version: Microsoft Linker 7.10
Visual Studio Version: Visual Studio .NET 2003
8 (0x08
)
Linker Version: Microsoft Linker 8.0
Visual Studio Version: Visual Studio 2005
9 (0x09
)
Linker Version: Microsoft Linker 9.0
Visual Studio Version: Visual Studio 2008
10 (0x0A
)
Linker Version: Microsoft Linker 10.0
Visual Studio Version: Visual Studio 2010
11 (0x0B
)
Linker Version: Microsoft Linker 11.0
Visual Studio Version: Visual Studio 2012
12 (0x0C
)
Linker Version: Microsoft Linker 12.0
Visual Studio Version: Visual Studio 2013
14 (0x0E
)
Linker Version: Microsoft Linker 14.0
Visual Studio Version: Visual Studio 2015
14 (0x0E
)
Linker Version: Microsoft Linker 14.1
Visual Studio Version: Visual Studio 2017
14 (0x0E
)
Linker Version: Microsoft Linker 14.2
Visual Studio Version: Visual Studio 2019
14 (0x0E
)
Linker Version: Microsoft Linker 14.3
Visual Studio Version: Visual Studio 2022
NOTE: There are probably more that are not listed
Minor Linker Version
Offset: 0x03
Size: 1 byte
Description:
The minor version number of the linker that generated the file.
Possible values:
0:
Initial versions; often seen in older files.
10 (0x0A
):
Microsoft Linker version 5.10, corresponding to Visual Studio 97.
12 (0x0C
):
Microsoft Linker version 6.0, corresponding to Visual Studio 6.0.
16 (0x10
):
Microsoft Linker version 7.0, corresponding to Visual Studio .NET 2002.
20 (0x14
):
Microsoft Linker version 7.10, corresponding to Visual Studio .NET 2003.
30 (0x1E
):
Microsoft Linker version 8.0, corresponding to Visual Studio 2005.
36 (0x24
):
Microsoft Linker version 9.0, corresponding to Visual Studio 2008.
40 (0x28
):
Microsoft Linker version 10.0, corresponding to Visual Studio 2010.
46 (0x2E
):
Microsoft Linker version 11.0, corresponding to Visual Studio 2012.
48 (0x30
):
Microsoft Linker version 12.0, corresponding to Visual Studio 2013.
50 (0x32
):
Microsoft Linker version 14.0, corresponding to Visual Studio 2015.
52 (0x34
):
Microsoft Linker version 14.1, corresponding to Visual Studio 2017.
28 (0x1C
):
Microsoft Linker version 14.2, corresponding to Visual Studio 2019.
36 (0x24
):
Microsoft Linker version 14.3, corresponding to Visual Studio 2022.
NOTE: There are probably more that are not listed
SizeOfCode
Offset: 0x04
Size: 4 bytes
Description:
The total size of all sections that contain executable code.
SizeOfInitializedData
Offset: 0x08
Size: 4 bytes
Description:
The total size of all sections containing initialized data.
SizeOfUninitializedData
Offset: 0x0C
Size: 4 bytes
Description:
The total size of all sections containing uninitialized data.
AddressOfEntryPoint
Offset: 0x10
Size: 4 bytes
Description:
RVA of the entry point function.
This is where execution starts after the program is loaded.
BaseOfCode
Offset: 0x14
Size: 4 bytes
Description:
The RVA of the start of the code section.
Indicates where the code segment begins in memory.
BaseOfData (PE32 only)
Offset: 0x18
Size: 4 bytes
Description:
The RVA of the start of the data section.
This field is not present in PE32+.
Possible for BaseOfData
to be missing in PE32+ (which is why there is no image of it in this course)
ImageBase
Offset: 0x1C (PE32), 0x18 (PE32+)
Size: 4 bytes (PE32), 8 bytes (PE32+)
Description:
The preferred memory address at which the image should be loaded.
Defaults to 0x400000
for PE32 and 0x140000000
for PE32+.
SectionAlignment
Offset: 0x20
Size: 4 bytes
Description:
The alignment of sections in memory.
Typically 0x1000
(4KB) but must be greater than or equal to FileAlignment.
FileAlignment
Offset: 0x24
Size: 4 bytes
Description:
The alignment of sections in the file on disk.
Usually set to 0x200
(512 bytes) but can vary based on the file format.
MajorOperatingSystemVersion
Offset: 0x28
Size: 2 bytes
Description:
The major version of the minimum required operating system.
MinorOperatingSystemVersion
Offset: 0x2A
Size: 2 bytes
Description:
The minor version of the minimum required operating system.
MajorImageVersion
Offset: 0x2C
Size: 2 bytes
Description:
The major version number of the image, set by the developer.
MinorImageVersion
Offset: 0x2E
Size: 2 bytes
Description:
The minor version number of the image.
MajorSubsystemVersion
Offset: 0x30
Size: 2 bytes
Description:
The major version of the subsystem that the image requires.
MinorSubsystemVersion
Offset: 0x32
Size: 2 bytes
Description:
The minor version of the subsystem.
Win32VersionValue
Offset: 0x34
Size: 4 bytes
Description:
Reserved, usually set to 0.
SizeOfImage
Offset: 0x38
Size: 4 bytes
Description:
The total size of the image, aligned to SectionAlignment.
This includes headers and all sections.
SizeOfHeaders
Offset: 0x3C
Size: 4 bytes
Description:
The size of all headers combined.
Aligned to FileAlignment.
CheckSum
Offset: 0x40
Size: 4 bytes
Description:
The checksum of the image.
Required for kernel-mode drivers; optional for user-mode executables.
Subsystem
Offset: 0x44
Size: 2 bytes
Description:
Specifies the subsystem required to run the image.
Common values:
0x02: Windows GUI.
0x03: Windows Console.
DllCharacteristics
Offset: 0x46
Size: 2 bytes
Description:
Flags indicating specific characteristics of the DLL.
SizeOfStackReserve
Offset: 0x48 (PE32), 0x48 (PE32+)
Size: 4 bytes (PE32), 8 bytes (PE32+)
Description:
The size of memory reserved for the stack.
Defaults to 1 MB for PE32 and 4 MB for PE32+.
SizeOfStackCommit
Offset: 0x4C (PE32), 0x50 (PE32+)
Size: 4 bytes (PE32), 8 bytes (PE32+)
Description:
The size of memory initially committed for the stack.
SizeOfHeapReserve
Offset: 0x50 (PE32), 0x58 (PE32+)
Size: 4 bytes (PE32), 8 bytes (PE32+)
Description:
The size of memory reserved for the heap.
SizeOfHeapCommit
Offset: 0x54 (PE32), 0x60 (PE32+)
Size: 4 bytes (PE32), 8 bytes (PE32+)
Description:
The size of memory initially committed for the heap.
LoaderFlags
Offset: 0x58 (PE32), 0x68 (PE32+)
Size: 4 bytes
Description:
Reserved for system use, usually set to 0.
NumberOfRvaAndSizes
Offset: 0x5C (PE32), 0x6C (PE32+)
Size: 4 bytes
Description:
The number of data directories following this field.
Typically set to 16, covering standard PE data directories like Import Table, Export Table, etc.
The data directory is a set of pointers that are part of the OptionalHeader
. It directs the Windows loader to various tables and structures that manage the execution of the program.
Let’s break down each directory in detail:
Export Table
Offset (PE32): 0x60
, Offset (PE32+): 0x70
Purpose:
Contains addresses of functions, variables, and symbols that the module exports for use by other modules.
Used for linking and referencing functions provided by the module.
Fields:
RVA: Pointer to the start of the Export Table.
Size: Size of the Export Table data.
Import Table
Offset (PE32): 0x68
, Offset (PE32+): 0x78
Purpose:
Contains information about functions, symbols, and variables imported from other modules.
Used for dynamic linking, allowing the module to use functions provided by other DLLs.
Fields:
RVA: Pointer to the start of the Import Table.
Size: Size of the Import Table data.
You can see a visualization of the import table of this file here: https://m4lc.io/course/winpe/imports
Resource Table
Offset (PE32): 0x70
, Offset (PE32+): 0x80
Purpose:
Contains resources like icons, dialogs, menus, strings, bitmaps, and other user interface components.
Allows for localization and UI customization within the PE file.
Fields:
RVA: Pointer to the start of the Resource Table.
Size: Size of the Resource Table data.
Exception Table
Offset (PE32): 0x78
, Offset (PE32+): 0x88
Purpose:
Holds information for exception handling, particularly for x64 Structured Exception Handling (SEH).
Used to manage hardware exceptions and software-defined exceptions during execution.
Fields:
RVA: Pointer to the start of the Exception Table.
Size: Size of the Exception Table data.
Certificate Table
Offset (PE32): 0x80
, Offset (PE32+): 0x90
Purpose:
Contains digital certificates for the file.
Used for verifying the integrity and authenticity of the PE file, often part of Authenticode signature verification.
Fields:
RVA: Pointer to the start of the Certificate Table (points to a file offset, not an RVA).
Size: Size of the certificate data.
You can see what it looks like when certificates are pulled from a file here: https://m4lc.io/course/winpe/certs
Base Relocation Table
Offset (PE32): 0x88
, Offset (PE32+): 0x98
Purpose:
Contains base relocations that enable the executable to be loaded at a different base address than specified by ImageBase.
Adjusts memory addresses when the image cannot be loaded at its preferred address.
Fields:
RVA: Pointer to the start of the Base Relocation Table.
Size: Size of the Base Relocation Table data.
Debug Directory
Offset (PE32): 0x90
, Offset (PE32+): 0xA0
Purpose:
Contains information useful for debugging tools.
Includes information about symbols, source files, and debugging information needed for analysis.
Fields:
RVA: Pointer to the start of the Debug Directory.
Size: Size of the Debug Directory data.
Architecture
Offset (PE32): 0x98
, Offset (PE32+): 0xA8
Purpose:
Reserved for future use, typically set to 0.
Fields:
RVA: Usually set to 0.
Size: Usually set to 0.
GlobalPtr
Offset (PE32): 0xA0
, Offset (PE32+): 0xB0
Purpose:
Reserved for global pointer optimization; generally set to 0.
Fields:
RVA: Typically 0.
Size: Typically 0.
TLS Table
Offset (PE32): 0xA8
, Offset (PE32+): 0xB8
Purpose:
Contains initialization data for Thread Local Storage (TLS).
TLS provides a mechanism for data that is specific to individual threads.
Fields:
RVA: Pointer to the start of the TLS Table.
Size: Size of the TLS Table data.
Load Config Table
Offset (PE32): 0xB0
, Offset (PE32+): 0xC0
Purpose:
Contains security features like SafeSEH, CFG (Control Flow Guard), and other load-time configurations.
Used to enhance security during execution.
Fields:
RVA: Pointer to the start of the Load Config Table.
Size: Size of the Load Config Table data.
Bound Import
Offset (PE32): 0xB8
, Offset (PE32+): 0xC8
Purpose:
Contains information about functions that are bound to specific addresses.
Helps speed up the loading process by avoiding the need for dynamic import resolution.
Fields:
RVA: Pointer to the start of the Bound Import Table.
Size: Size of the Bound Import Table data.
Import Address Table (IAT)
Offset (PE32): 0xC0
, Offset (PE32+): 0xD0
Purpose:
Provides the actual addresses of imported functions used by the executable.
Used during runtime to resolve addresses for dynamically linked functions.
Fields:
RVA: Pointer to the start of the IAT.
Size: Size of the IAT data.
A section in a PE file represents different parts of the file the contain code, data, and other resources that make the file execute. Each section is defined by a header that describes its properties, size, and memory alignment.
You can see a visualization of the sections of the file used for this course here: https://m4lc.io/course/winpe/sections
Common sections in PE files include:
.text
Contains the executable code if the program, where the CPU executes its instructions
.data
Stories initialized global variables and static variables. These variables have a defined value before execution
.bss
Holds uninitialized data
.rdata
Contains read-only constants, strings, and data that should not be modified
.rsrc
Stores resources that the file will use
.edata
Contains the export table
.idata
Like .edata
but contains the import table
.reloc
Contains relocation information allowing the executable to be loaded at different addresses
.pdata
Contains exception data
.tls
Holds Thread Local Storage that initializes values to specific threads
Each section is 40 bytes long and contains multiple pieces to it. As you can see in the image and table below:
Name
VirtualSize
VirtualAddress
SizeOfRawData
PointerToRawData
PointerToRelocations
PointerToLinenumbers
NumberOfRelocations
NumberOfLinenumbers
Characteristics
That's really all there is to it. In this course we have broken down the PE file format and provided you with visualizations of how the format works. We appreciate you taking the time to read through this course and hope you got something out of it.
Once again:
A lot of this information can be visualized and seen done during analysis by following these links:
Also, please remember that this course is provided to you for free by the Malcore team: https://m4lc.io/course/winpe/register
Consider registering, and using Malcore, so we can continue to provide free content for the entire community. You can also join our Discord server here: https://m4lc.io/course/winpe/discord
We offer free threat intel in our Discord via our custom designed Discord bot. Join the Discord to discuss this course in further detail or to ask questions.
You can also support us by buying us a coffee
Tools used for this course:
Malcore: https://malcore.io
Malcat: https://malcat.fr/
Offset | Size (Bytes) | Field Name | Description |
---|---|---|---|
Offset (PE32) | Offset (PE32+) | Size | Field Name | Description |
---|---|---|---|---|
Offset (PE32) | Offset (PE32+) | Size | Field Name | Description |
---|---|---|---|---|
Offset | Size (Bytes) | Field Name | Description |
---|---|---|---|