Introduction
The Portable Executable (PE) format is an essential structure for Windows binaries such as executables (EXE) and dynamic link libraries (DLLs). It consists of a set of PE headers, each playing a crucial role in how the file loads, executes, and interacts with the operating system. This guide explores the key PE headers, their specific purposes, internal structures, and sizes, offering a comprehensive understanding for developers, reverse engineers, and cybersecurity professionals. By mastering the PE file format, you can gain deeper insights into PE file analysis and enhance your understanding of system programming.
What is the Portable Executable (PE) Format?
The Portable Executable (PE) format is a structure used by Windows binaries to ensure efficient execution on the system. From PE headers to section headers, each component is designed for a specific purpose, whether it’s managing metadata, memory mapping, or optimizing runtime behavior. Understanding the PE format structure helps in analyzing how Windows executables and DLLs operate within the system.
PE File Structure Overview
A PE file comprises several headers and sections:
- DOS Header
- PE Signature
- File Header
- Optional Header
- Data Directories
- Section Headers
- Sections
Headers and Sections in Detail
1. DOS Header
- Purpose: A legacy header from MS-DOS that contains the "MZ" magic number and a pointer to the PE header.
- Structure Name:
IMAGE_DOS_HEADER
- Size: 64 bytes
Key Fields:
e_magic
: Identifies the file as an executable with the "MZ" signature.e_lfanew
: Points to the NT headers.
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; /* 00: MZ Header signature */
WORD e_cblp; /* 02: Bytes on last page of file */
WORD e_cp; /* 04: Pages in file */
WORD e_crlc; /* 06: Relocations */
WORD e_cparhdr; /* 08: Size of header in paragraphs */
WORD e_minalloc; /* 0a: Minimum extra paragraphs needed */
WORD e_maxalloc; /* 0c: Maximum extra paragraphs needed */
WORD e_ss; /* 0e: Initial (relative) SS value */
WORD e_sp; /* 10: Initial SP value */
WORD e_csum; /* 12: Checksum */
WORD e_ip; /* 14: Initial IP value */
WORD e_cs; /* 16: Initial (relative) CS value */
WORD e_lfarlc; /* 18: File address of relocation table */
WORD e_ovno; /* 1a: Overlay number */
WORD e_res[4]; /* 1c: Reserved words */
WORD e_oemid; /* 24: OEM identifier (for e_oeminfo) */
WORD e_oeminfo; /* 26: OEM information; e_oemid specific */
WORD e_res2[10]; /* 28: Reserved words */
DWORD e_lfanew; /* 3c: Offset to extended header */
}
1.1 DOS Stub
- Purpose: Displays a message such as "This program cannot be run in DOS mode" when executed in a DOS environment.
- Size: Variable, typically ~128 bytes.
2. PE Signature
- Purpose: Marks the file as a Portable Executable file.
- Structure Name: Not a structure but a constant value (
"PE\0\0"
). - Size: 4 bytes
Here’s your content formatted for a Blogger post. Simply copy and paste it into your blog editor:
3. NT Header (IMAGE_NT_HEADERS)
The NT Header plays a crucial role in PE (Portable Executable) files, encapsulating the FileHeader
and OptionalHeader
, which provide essential details about the PE file. Like the DOS Header, the NT Header has a signature to validate it. This signature, often "PE"
, is represented as 0x50
and 0x45
bytes. Since the signature is a DWORD
, it is represented as 0x50450000
, padded with two null bytes. You can access the NT Header using the e_lfanew
member in the DOS Header.
NT Header Structure: 32-bit and 64-bit Variants
32-bit Version:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
64-bit Version:
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
The primary difference lies in the OptionalHeader
structure, which varies between IMAGE_OPTIONAL_HEADER32
and IMAGE_OPTIONAL_HEADER64
.
3.1. PE Signature
- Purpose: Identifies the file as a PE file.
- Structure Name: Not a structure; it’s a constant value (
"PE\0\0"
). - Size: 4 bytes
- Key Fields:
"PE\0\0"
3.2. File Header / PE Header
- Purpose: Contains metadata about the file, such as the target machine and the number of sections.
- Structure Name:
IMAGE_FILE_HEADER
- Size: 20 bytes
- Key Fields:
Machine
: Target architecture (e.g., x86, x64).NumberOfSections
: Number of sections in the file.Characteristics
: Flags describing file attributes.
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
3.3. Optional Header
- Purpose: Provides details about the executable, such as entry points and memory layout.
- Structure Name:
IMAGE_OPTIONAL_HEADER
- Varies for 32-bit and 64-bit versions.
- Size:
- 32-bit: 96 bytes
- 64-bit: 112 bytes
- Key Fields:
AddressOfEntryPoint
: Entry point of the executable.ImageBase
: Preferred memory address for loading.Subsystem
: Indicates the runtime environment (e.g., GUI, console).
Optional Header Structure
64-bit Version:
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic; /* 0x20b */
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
}
32-bit Version:
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
4. Data Directories
The Data Directories are pointers in the Optional Header, linking to important tables like imports and exports.
- Structure Name:
IMAGE_DATA_DIRECTORY
- Size: 8 bytes per entry (16 entries = 128 bytes)
- Key Fields:
VirtualAddress
: Address of the table.Size
: Size of the table.
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
The Data Directory array is of size `IMAGE_NUMBEROF_DIRECTORY_ENTRIES` which is a constant value of `16`. Each element in the array represents a specific data directory which includes some data about a PE section or a Data Table (the place where specific information about the PE is saved).
A specific data directory can be accessed using its index in the array.
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
Key Data Directories
Export Directory
Contains addresses of exported functions, found mainly in DLLs (e.g., kernel32.dll
exporting CreateFileA
).
Import Address Table
Holds addresses of functions imported from other files (e.g., Application.exe
importing CreateFileA
from kernel32.dll
).
5. Section Headers (Section Table)
- Purpose: Describe individual sections like
.text
(code),.data
(initialized data), and.rdata
(read-only data). - Structure Name:
IMAGE_SECTION_HEADER
- Size: 40 bytes per section
Key Fields:
Name
: Name of the section (e.g.,.text
,.data
).VirtualSize
: Size of the section in memory.SizeOfRawData
: Size of the section on disk.
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
}
6. Sections
Sections store the actual content of the executable (code, data, imports, exports, etc.). The specific sections and their contents include:
Section | Purpose | Contents | Characteristics |
---|---|---|---|
.text | Code section | Executable instructions (machine code). | Execute |
.rdata | Read-only data | Import Table, Export Table, strings, constants. | Read |
.data | Initialized data | Global/static variables with initial values. | Read |
.bss | Uninitialized data | Global/static variables without initial values. | Read |
.idata | Import-related data | Import Address Table (IAT), descriptors. | Read |
.edata | Export-related data | Export Directory Table, exported symbols. | Read |
.reloc | Relocation information | Base Relocation Table. | Read |
.rsrc | Resources | Icons, dialogs, menus, etc. | Read |
6.1. .text (Code Section)
- Purpose: Contains the executable instructions (machine code) of the program.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
- Compiled machine instructions.
- Entry point code for the program.
- Characteristics:
IMAGE_SCN_CNT_CODE
: Indicates this is a code section.IMAGE_SCN_MEM_EXECUTE
: The section is executable.IMAGE_SCN_MEM_READ
: The section is readable.
- Key Fields in Section Header:
Name
: Typically ".text".VirtualSize
: Size of the section in memory.PointerToRawData
: Offset of the section's raw data in the file.Characteristics
: Attributes for the section.
6.2. .rdata
(Read-Only Data Section)
- Purpose: Contains read-only data, such as constants, strings, and sometimes import/export-related data.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
- Strings and constants.
- Import Table (Import Address Table, Import Lookup Table).
- Export Table (if not in
.edata
).
- Characteristics:
IMAGE_SCN_CNT_INITIALIZED_DATA
: Contains initialized data.IMAGE_SCN_MEM_READ
: Read-only data.
- Key Fields in Section Header:
Name
: Typically ".rdata".VirtualSize
: Size of the section in memory.PointerToRawData
: Offset of the section's raw data in the file.Characteristics
: Attributes for the section.
6.3. .data
(Initialized Data Section)
- Purpose: Stores global and static variables with initial values.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
- Initialized global/static variables.
- Writable data used by the program.
- Characteristics:
IMAGE_SCN_CNT_INITIALIZED_DATA
: Contains initialized data.IMAGE_SCN_MEM_READ
: The section is readable.IMAGE_SCN_MEM_WRITE
: The section is writable.
- Key Fields in Section Header:
Name
: Typically ".data".VirtualSize
: Size of the section in memory.PointerToRawData
: Offset of the section's raw data in the file.Characteristics
: Attributes for the section.
6.4. .bss
(Uninitialized Data Section)
- Purpose: Stores uninitialized global and static variables.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
- Uninitialized global/static variables.
- Memory is zero-initialized at runtime.
- Size:
- File size: Typically 0 (uninitialized data is not stored in the file).
- Virtual size: Rounded to
SectionAlignment
.
- Characteristics:
IMAGE_SCN_CNT_UNINITIALIZED_DATA
: Contains uninitialized data.IMAGE_SCN_MEM_READ
: The section is readable.IMAGE_SCN_MEM_WRITE
: The section is writable.
- Key Fields in Section Header:
Name
: Typically ".bss".VirtualSize
: Size of the section in memory.PointerToRawData
: Usually 0 (since no raw data is stored in the file).Characteristics
: Attributes for the section.
6.5. .idata
(Import Section)
- Purpose: Contains information about imported functions and libraries.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
- Import Address Table (IAT).
- Import Lookup Table (ILT).
IMAGE_IMPORT_DESCRIPTOR
structures.
- Characteristics:
IMAGE_SCN_CNT_INITIALIZED_DATA
: Contains initialized data.IMAGE_SCN_MEM_READ
: Readable.
- Key Fields in Section Header:
Name
: Typically ".idata".VirtualSize
: Size of the section in memory.PointerToRawData
: Offset of the section's raw data in the file.Characteristics
: Attributes for the section.
6.6. .edata
(Export Section)
- Purpose: Contains information about exported functions and symbols.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
IMAGE_EXPORT_DIRECTORY
structure.- Addresses of exported symbols.
- Symbol names and ordinals.
- Characteristics:
IMAGE_SCN_CNT_INITIALIZED_DATA
: Contains initialized data.IMAGE_SCN_MEM_READ
: Readable.
- Key Fields in Section Header:
Name
: Typically ".edata".VirtualSize
: Size of the section in memory.PointerToRawData
: Offset of the section's raw data in the file.Characteristics
: Attributes for the section.
6.7. .reloc
(Relocation Section)
- Purpose: Contains relocation information for fixing addresses when the PE file is loaded at a non-preferred base address.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
IMAGE_BASE_RELOCATION
structures.- Relocation entries for specific addresses.
- Characteristics:
IMAGE_SCN_CNT_INITIALIZED_DATA
: Contains initialized data.IMAGE_SCN_MEM_READ
: Readable.
- Key Fields in Section Header:
Name
: Typically ".reloc".VirtualSize
: Size of the section in memory.PointerToRawData
: Offset of the section's raw data in the file.Characteristics
: Attributes for the section.
6.8. .rsrc
(Resource Section)
- Purpose: Stores application resources, such as icons, menus, dialogs, and strings.
- Structure Name:
IMAGE_SECTION_HEADER
- Contents:
- Resource data in a hierarchical format.
- Characteristics:
IMAGE_SCN_CNT_INITIALIZED_DATA
: Contains initialized data.IMAGE_SCN_MEM_READ
: Readable.
- Key Fields in Section Header:
Name
: Typically ".rsrc".VirtualSize
: Size of the section in memory.PointerToRawData
: Offset of the section's raw data in the file.Characteristics
: Attributes for the section.
Summary Table
Header/Section | Structure Name | Size (Bytes) | Purpose |
---|---|---|---|
DOS Header | `IMAGE_DOS_HEADER` | 64 | Legacy; points to PE header. |
PE Signature | Constant (`"PE\0\0"`) | 4 | Identifies the file as PE. |
File Header | `IMAGE_FILE_HEADER` | 20 | File metadata. |
Optional Header | `IMAGE_OPTIONAL_HEADER32`/ `IMAGE_OPTIONAL_HEADER64` |
96 / 112 | Detailed executable info. |
Section Headers | `IMAGE_SECTION_HEADER` | 40 per section | Describes file sections. |
Data Directories | `IMAGE_DATA_DIRECTORY` | 128 | Points to tables like import/export. |
Import Descriptor | `IMAGE_IMPORT_DESCRIPTOR` | 20 per entry | Details about imported DLLs/functions. |
Export Directory | `IMAGE_EXPORT_DIRECTORY` | 40 | Details about exported symbols. |
Base Relocation Table | `IMAGE_BASE_RELOCATION` | Varies | Relocation info for memory mapping. |
Resource Directory | `IMAGE_RESOURCE_DIRECTORY` | Varies | Resource data (icons, strings, etc.). |
Debug Directory | `IMAGE_DEBUG_DIRECTORY` | 28 per entry | Debugging info pointers. |
Summary
The Portable Executable (PE) format is the backbone of Windows executable files, facilitating efficient interaction between applications and the Windows operating system. Understanding its components—such as PE headers, section headers, and data directories—is essential for anyone involved in PE file analysis. From the DOS Header to the Section, each section defines the structure and behavior of the file during runtime.
Whether you're a developer optimizing your code, a reverse engineer dissecting Windows binaries, or a cybersecurity professional investigating potential threats, mastering the PE format is key to navigating and manipulating Windows executables effectively. This guide provides a foundational understanding to help you advance your skills in binary analysis, PE file analysis, and system programming.
No comments:
Post a Comment