PE Header Analysis

28 minIn Progress

PE Header Analysis

The Portable Executable (PE) format is the native executable format for Windows. Every .exe, .dll, .sys, and .scr file follows this specification. Understanding PE headers reveals architecture, capabilities, compilation details, and anomalies -- all without executing the binary.


1. PE Format Structure

+----------------------------------+
|     DOS Header (64 bytes)        |  "MZ" magic, pointer to PE header
+----------------------------------+
|     DOS Stub                     |  "This program cannot be run..."
+----------------------------------+
|     PE Signature (4 bytes)       |  "PE\0\0" (bytes 50 45 00 00)
+----------------------------------+
|   COFF/File Header (20 bytes)    |  Machine type, section count, timestamp
+----------------------------------+
|   Optional Header (variable)     |  Entry point, image base, subsystem
|     - Standard fields            |
|     - Data Directories           |  Import/export tables, resources
+----------------------------------+
|   Section Headers Table          |  .text, .data, .rdata, .rsrc, .reloc
+----------------------------------+
|   Section Bodies                 |  Actual code and data
+----------------------------------+
Hex editor showing MZ signature at the start of a PE file
Hex editor showing MZ signature at the start of a PE file

2. DOS Header

FieldOffsetSizePurpose
e_magic0x002 bytesMust be 0x5A4D ("MZ")
e_lfanew0x3C4 bytesOffset to PE signature

3. COFF File Header

FieldSizeValues of Interest
Machine2 bytes0x014C = x86, 0x8664 = x64, 0x01C4 = ARM
NumberOfSections2 bytesNormal: 3-7. Very high count suggests packing
TimeDateStamp4 bytesUnix timestamp. Often faked by malware authors
Characteristics2 bytesIMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_DLL

Compilation Timestamps

import pefile
from datetime import datetime

pe = pefile.PE("suspicious.exe")
timestamp = pe.FILE_HEADER.TimeDateStamp
dt = datetime.utcfromtimestamp(timestamp)
print(f"Compilation: {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC")

# Red flags:
# - Date in the far future (2030+)
# - Date of 0 (Jan 1, 1970) -- deliberately zeroed
# - Delphi binaries commonly show June 19, 1992 (a linker artifact, not a rule)

4. Optional Header Key Fields

FieldPurposeAnalyst Notes
AddressOfEntryPointRVA where execution beginsPoints outside .text? Suspect packing
ImageBasePreferred load address0x00400000 (EXE), 0x10000000 (DLL)
SubsystemGUI (2), Console (3), Native (1)How Windows launches the program
DllCharacteristicsSecurity featuresCheck ASLR, DEP/NX, CFG flags
MajorLinkerVersionLinker versionIdentifies compiler toolchain

Understanding RVA vs. VA vs. File Offset

TermDefinitionExample
File OffsetPosition on disk0x00000400
RVAOffset from ImageBase in memory0x00001000
VARVA + ImageBase0x00401000

Data Directories

IndexDirectoryWhy It Matters
1Import TableDLLs and APIs used -- critical for capability analysis
2Resource TableEmbedded icons, configs, additional PE files
5Base RelocationNeeded for ASLR
6Debug DirectoryPDB path (developer info)
14CLR Runtime HeaderIndicates .NET executable

5. Section Table

SectionTypical PurposeSuspicious When...
.textExecutable codeEntropy > 7.0 (packed)
.dataInitialized dataHas EXECUTE flag
.rdataRead-only data (imports, strings)Contains URLs/IPs
.rsrcResources (icons, dialogs)Unusually large
.relocBase relocationsMissing in a DLL
UPX0/UPX1UPX packer sectionsPacked binary
Random namesNon-standard sectionsCustom packer
PEStudio showing PE sections with entropy values and flags
PEStudio showing PE sections with entropy values and flags

Section Characteristics Flags

0x00000020  CNT_CODE               Contains executable code
0x00000040  CNT_INITIALIZED_DATA   Contains initialized data
0x20000000  MEM_EXECUTE            Executable memory
0x40000000  MEM_READ               Readable memory
0x80000000  MEM_WRITE              Writable memory

Red flag: READ + WRITE + EXECUTE (0xE0000000) -> Self-modifying/unpacking code

6. Tools for PE Header Analysis

PEStudio (Windows)

Comprehensive view of PE headers with automatic anomaly flagging. Calculates hashes, displays imports, shows section entropy.

Detect It Easy / Exeinfo PE

$ diec suspicious.exe
PE64
Compiler: Microsoft Visual C/C++(2010)[-]
Linker: Microsoft Linker(10.0)[GUI64]

pefile (Python)

import pefile
pe = pefile.PE("suspicious.exe")

print(f"Entry Point: {hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint)}")
print(f"Image Base:  {hex(pe.OPTIONAL_HEADER.ImageBase)}")
print(f"Subsystem:   {pe.OPTIONAL_HEADER.Subsystem}")

print("\nSections:")
for section in pe.sections:
    name = section.Name.decode().strip('\x00')
    entropy = section.get_entropy()
    flags = hex(section.Characteristics)
    status = "HIGH ENTROPY" if entropy > 7.0 else ""
    print(f"  {name:10s} entropy={entropy:.2f} chars={flags} {status}")

CAPA (Mandiant)

$ capa suspicious.exe
# Maps PE structure to ATT&CK techniques automatically

7. PE Analysis Workflow

  1. Open in PEStudio/DiE -- overview of anomalies, compiler, packer
  2. Check timestamp -- plausible? Matches campaign timeline?
  3. Inspect sections -- high entropy, unusual names, suspicious flags?
  4. Examine entry point -- points to .text or unusual section?
  5. Review data directories -- import table present? .NET CLR header?
  6. Check security flags -- ASLR, DEP/NX enabled or disabled?
  7. Run CAPA -- automated capability detection
  8. Document findings -- section hashes, anomalies, compiler info

Common Pitfall: A "clean" PE header does not mean benign. Sophisticated malware uses legitimate compilers and standard PE structures.

MITRE ATT&CK: PE manipulation relates to T1027.002 - Software Packing and T1036.001 - Invalid Code Signature.

Try it in the shell
Practise this lesson's tooling on its sample in an emulated analyst shell. Output is pre-recorded — nothing executes.

Suggested triage steps

  1. 1

    Confirm the format

    Establish this is a PE before opening the header up.

  2. 2

    Walk the section table

    Entry point and sections, the way PEStudio would show them.

  3. 3

    See the raw header bytes

    The DOS and COFF headers you just read about start right here.

  4. 4

    Assess section entropy

    Compare each measured section with the stated packing threshold.

analyst@lab:~emulated · nothing executes

MAA analyst shell — emulated. Nothing executes.

Type 'help', or click a step on the left.

$