PE File Analysis

Overview

The Portable Executable (PE) format is the standard binary format for Windows executables (EXE), dynamic-link libraries (DLL), drivers (SYS), and other executable types. Analyzing PE headers, sections, imports, exports, and resources reveals a malware sample's capabilities, compiler information, and anomalies that indicate malicious intent.

PE Structure Overview

┌──────────────────────┐
│     DOS Header       │  (MZ signature, e_lfanew pointer)
├──────────────────────┤
│     DOS Stub         │  ("This program cannot be run...")
├──────────────────────┤
│     PE Signature     │  ("PE\0\0")
├──────────────────────┤
│   COFF File Header   │  (machine, sections, timestamp)
├──────────────────────┤
│   Optional Header    │  (entry point, image base, subsystem)
├──────────────────────┤
│   Section Headers    │  (.text, .data, .rdata, .rsrc, ...)
├──────────────────────┤
│   Section Data       │  (actual code and data)
└──────────────────────┘

Analysis with pefile (Python)

# pefile
# https://github.com/erocarrera/pefile
import pefile

pe = pefile.PE('sample.exe')

# Basic info
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(f"Machine:     {hex(pe.FILE_HEADER.Machine)}")
print(f"Timestamp:   {pe.FILE_HEADER.TimeDateStamp}")

# Compilation timestamp (can be faked)
import datetime
timestamp = pe.FILE_HEADER.TimeDateStamp
compile_time = datetime.datetime.utcfromtimestamp(timestamp)
print(f"Compiled:    {compile_time}")

# Import hash
print(f"Imphash:     {pe.get_imphash()}")

Analyzing Sections

# pefile
# https://github.com/erocarrera/pefile
import pefile

pe = pefile.PE('sample.exe')

for section in pe.sections:
    print(f"Name:           {section.Name.decode().rstrip(chr(0))}")
    print(f"  Virtual Addr: {hex(section.VirtualAddress)}")
    print(f"  Virtual Size: {hex(section.Misc_VirtualSize)}")
    print(f"  Raw Size:     {hex(section.SizeOfRawData)}")
    print(f"  Entropy:      {section.get_entropy():.2f}")
    print(f"  Flags:        {hex(section.Characteristics)}")
    print()

Section Anomalies to Look For

Anomaly Indicator
High entropy (> 7.0) Encrypted or compressed data (packed?)
Virtual size >> raw size Section will be unpacked at runtime
Raw size = 0 but virtual size > 0 Data generated at runtime
.text section is writable Self-modifying code
Unusual section names UPX0, .enigma, .themida, etc. (packer names)
Entry point not in .text Entry point in a packer section

Analyzing Imports

# pefile
# https://github.com/erocarrera/pefile
import pefile

pe = pefile.PE('sample.exe')

if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
    for entry in pe.DIRECTORY_ENTRY_IMPORT:
        print(f"\n{entry.dll.decode()}")
        for imp in entry.imports:
            name = imp.name.decode() if imp.name else f"ordinal_{imp.ordinal}"
            print(f"  {name}")

Analyzing Exports

# pefile
# https://github.com/erocarrera/pefile
import pefile

pe = pefile.PE('sample.dll')

if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
    for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
        name = exp.name.decode() if exp.name else "N/A"
        print(f"  {name} @ {hex(pe.OPTIONAL_HEADER.ImageBase + exp.address)}")

Extracting Resources

# pefile
# https://github.com/erocarrera/pefile
import pefile

pe = pefile.PE('sample.exe')

if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
    for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
        name = pefile.RESOURCE_TYPE.get(resource_type.id, str(resource_type.id))
        print(f"Resource type: {name}")
        if hasattr(resource_type, 'directory'):
            for entry in resource_type.directory.entries:
                if hasattr(entry, 'directory'):
                    for res in entry.directory.entries:
                        offset = res.data.struct.OffsetToData
                        size = res.data.struct.Size
                        print(f"  Offset: {hex(offset)}, Size: {size}")

Embedded resources often contain additional payloads, configuration data, or secondary executables.

Analysis with rabin2

# radare2
# https://github.com/radareorg/radare2

# Show binary info (headers, protections)
rabin2 -I sample.exe

# List imports
rabin2 -i sample.exe

# List exports
rabin2 -E sample.exe

# List sections
rabin2 -S sample.exe

# List strings
rabin2 -z sample.exe

# Show entry point
rabin2 -e sample.exe

# Show libraries (DLL dependencies)
rabin2 -l sample.exe

# Show header fields
rabin2 -H sample.exe

# Calculate hashes
rahash2 -a md5,sha256 sample.exe

# Show all info (comprehensive)
rabin2 -g sample.exe

Analysis with objdump

# Display PE file headers
objdump -x sample.exe | head -60

# Display all section headers
objdump -h sample.exe

# Disassemble the .text section
objdump -d sample.exe | head -100

# Show private headers (PE-specific)
objdump -p sample.exe | head -40

Suspicious PE Characteristics

Compilation Timestamp

# pefile
# https://github.com/erocarrera/pefile
import pefile, datetime

pe = pefile.PE('sample.exe')
ts = pe.FILE_HEADER.TimeDateStamp
compile_time = datetime.datetime.utcfromtimestamp(ts)
print(f"Compile time: {compile_time}")

# Red flags:
# - Timestamp in the future
# - Timestamp of 0 (Jan 1, 1970) — stripped/zeroed
# - Very old timestamp for a modern binary
# - Timestamp set to a known Delphi/Borland epoch

DLL Characteristics (ASLR/DEP/SEH)

# pefile
# https://github.com/erocarrera/pefile
import pefile

pe = pefile.PE('sample.exe')

dll_chars = pe.OPTIONAL_HEADER.DllCharacteristics
print(f"ASLR:            {bool(dll_chars & 0x0040)}")
print(f"DEP/NX:          {bool(dll_chars & 0x0100)}")
print(f"No SEH:          {bool(dll_chars & 0x0400)}")
print(f"High entropy VA: {bool(dll_chars & 0x0020)}")

Malware may disable ASLR or DEP for easier exploitation.

Common Suspicious Imports

DLL Function Capability
kernel32.dll CreateRemoteThread Process injection
kernel32.dll VirtualAllocEx Remote memory allocation
kernel32.dll WriteProcessMemory Remote code writing
kernel32.dll CreateFileA/W File operations
kernel32.dll WinExec, CreateProcessA Process execution
advapi32.dll RegSetValueExA Registry modification
advapi32.dll OpenProcessToken Token manipulation
ws2_32.dll connect, send, recv Network communication
wininet.dll InternetOpenA, HttpSendRequestA HTTP communication
urlmon.dll URLDownloadToFileA File download
ntdll.dll NtUnmapViewOfSection Process hollowing
advapi32.dll CryptEncrypt, CryptDecrypt Encryption

References

Tools

Official Documentation