🔬 PE Analysis

PE File Analysis Quick Reference

Windows PE format — section meanings, suspicious imports and combinations, entropy, timestamp manipulation, and overlay data. Python + pecheck commands included.

📦 PE File Structure Overview

0x00
DOS Header (MZ) — e_magic = 0x5A4D, e_lfanew → PE offset
DOS Stub — "This program cannot be run in DOS mode"
e_lfanew
PE Signature — "PE\0\0" (0x50450000)
COFF File Header — machine type, section count, compile time
Optional Header — entry point, image base, subsystem, magic
Data Directories — Import table, Export table, Resources, TLS, Debug
.text — executable code
.data — initialised global variables
.rdata — read-only data (strings, imports)
.rsrc — embedded resources (icons, dialogs)
EOF
Overlay (optional) — data after last section → suspicious

Key header fields explained

FieldLocationWhat It Tells You
e_magicDOS Header offset 0Must be 0x5A4D ("MZ") — any other value means not a valid PE
e_lfanewDOS Header offset 0x3COffset to the PE signature — unusually large value is suspicious
MachineFile Header0x014C = x86, 0x8664 = x64 — mismatch with system = suspicious
TimeDateStampFile HeaderCompile timestamp — see Indicators tab for timestomping detection
CharacteristicsFile Header0x0002 = Executable, 0x2000 = DLL, 0x0020 = large address aware
AddressOfEntryPointOptional HeaderWhere execution begins — pointing outside .text section is suspicious
ImageBaseOptional HeaderPreferred load address — unusual values (not 0x400000/0x10000000) can indicate packing
SubsystemOptional Header2=GUI, 3=Console — GUI binary that opens console window is suspicious
MagicOptional Header0x10B = PE32 (x86), 0x20B = PE32+ (x64), 0x107 = ROM image

📂 PE Section Reference

Section NameNormal ContentMalicious Indicators
.textCompiled executable codeHigh entropy (>7.0) = packed. Entry point outside .text = suspicious
.dataInitialised global/static variablesHigh entropy = encrypted config or payload stored here
.rdataRead-only data: strings, import table, constantsVery few imports = suspicious (packed/shellcode)
.bssUninitialised data (zero-filled)Non-zero entropy = unusual, may contain hidden payload
.rsrcResources: icons, dialogs, version info, bitmapsHigh entropy resource = embedded encrypted payload or dropped file
.relocBase relocation table for ASLRMissing reloc in DLL = may be position-dependent shellcode
UPX0 / UPX1UPX packer sections — unpack with upx -d before analysis
.ndataNSIS installer dataMalware often repackaged as fake NSIS installers
Random names (.xxx, ABCDE)Custom packer — check entropy, likely packed/encrypted
Very few sections (1-2 total)Shellcode loaders, heavily packed samples often have minimal section count

Entropy quick reference

SectionNormal EntropyAlert if…
.text5.0 – 6.5>7.0 = packed
.data3.0 – 5.0>6.5 = unusual
.rdata4.0 – 5.5>7.0 = encrypted data
.rsrc3.0 – 7.0>7.2 = embedded encrypted file
.bss0.0Any non-zero = very suspicious

🔗 Suspicious Import Combinations

Windows API imports tell you what a binary can do — even before execution. These combinations are the most diagnostic for malware classification.

Import CombinationTechniqueMITRESeverity
VirtualAllocEx + WriteProcessMemory + CreateRemoteThreadClassic remote process injectionT1055.001Critical
VirtualAlloc + WriteProcessMemory + CreateThreadLocal process shellcode injectionT1055Critical
NtUnmapViewOfSection + VirtualAllocEx + WriteProcessMemoryProcess hollowingT1055.012Critical
SetWindowsHookEx + GetAsyncKeyStateKeyloggerT1056.001Critical
InternetOpen + InternetConnect + HttpSendRequestHTTP C2 communicationT1071.001High
CryptEncrypt + InternetOpenEncrypted C2 or data exfiltrationT1041High
RegSetValueEx (+ Run key path in strings)Registry persistenceT1547.001High
CreateService + StartServiceService-based persistenceT1543.003High
FindFirstFile + FindNextFile + EncryptFile / CryptEncryptFile enumeration + encryption (ransomware)T1486Critical
CreateToolhelp32Snapshot + Process32FirstProcess enumeration (recon / AV bypass)T1057Medium
IsDebuggerPresent + CheckRemoteDebuggerPresentDebugger detection / anti-analysisT1622Medium
GetSystemInfo + GlobalMemoryStatusEx + GetUserNameSystem fingerprinting (sandbox detection)T1082Medium
LoadLibrary + GetProcAddress (only imports)Dynamic API resolution — hides real capabilityT1027High
WNetOpenEnum + WNetEnumResourceNetwork share enumeration — lateral movementT1135High
sqlite3_open + sqlite3_execBrowser credential database theftT1555.003High

Minimal import table — high suspicion

Legitimate Windows executables import many functions. A PE with very few imports (or only LoadLibrary + GetProcAddress) is resolving all APIs dynamically at runtime — a strong indicator of packing or shellcode.

Import CountInterpretation
0 importsAlmost certainly shellcode or a loader stub
1-3 imports, only LoadLibrary/GetProcAddressDynamic API resolution — analysis requires unpacking or dynamic analysis
4-15 importsSuspicious — check if binary is packed
15+ imports across multiple DLLsMore typical of legitimate software (not conclusive)

⚠️ Malicious PE Indicators

Timestamp manipulation (timestomping)

ObservationVerdict
Compile time before 1995 (PE format didn't widely exist)Timestomped
Compile time in the future (past today's date)Timestomped
Compile time exactly 0 (1970-01-01)Timestomped or stripped
Compile time doesn't match file creation time by >24 hoursSuspicious — may have been copied/deployed after compilation
Compile time matches known malware family release dateCorroborating evidence of attribution
Debug directory timestamp differs from compile timestampFile header timestamp was manually changed

Overlay data

Overlay is data appended after the last PE section. Legitimate uses: self-extracting archives (SFX), installers, digital signatures. Malicious uses: embedded payload, encoded config, dropper contents.

python3 << 'EOF' import pefile pe = pefile.PE('suspicious.exe') # Find end of last section last_section = max(pe.sections, key=lambda s: s.PointerToRawData + s.SizeOfRawData) last_byte = last_section.PointerToRawData + last_section.SizeOfRawData # Get file size import os file_size = os.path.getsize('suspicious.exe') overlay_size = file_size - last_byte if overlay_size > 0: print(f"[!] OVERLAY DETECTED: {overlay_size} bytes at offset {hex(last_byte)}") with open('suspicious.exe','rb') as f: f.seek(last_byte) overlay = f.read(64) print(f"First bytes: {overlay.hex()}") # Check if overlay is another PE if overlay[:2] == b'MZ': print("[!!] Overlay appears to be an embedded PE file!") # Check if ZIP/archive elif overlay[:2] == b'PK': print("[!] Overlay appears to be a ZIP/archive!") else: print(f"[+] No overlay detected. File ends at section boundary.") EOF

Version info impersonation

What to checkRed flag
CompanyNameClaims Microsoft/Google/Adobe but file is not signed by that company
OriginalFilenameClaims to be svchost.exe, explorer.exe — but path or hash doesn't match
FileDescriptionGeneric or empty — legitimate Microsoft tools always have descriptions
ProductVersion vs FileVersionMismatch or all zeros
Authenticode signatureNo signature, revoked certificate, or certificate not matching company

⌨️ Command Reference

Quick full analysis — one script

python3 << 'EOF' import pefile, datetime, math, os, re, sys fname = sys.argv[1] if len(sys.argv) > 1 else 'sample.exe' pe = pefile.PE(fname) fh = pe.FILE_HEADER; oh = pe.OPTIONAL_HEADER print("=" * 60) print(f"PE ANALYSIS: {fname}") print("=" * 60) # Basic info print(f"\n[FILE]") print(f" Size: {os.path.getsize(fname):,} bytes") print(f" Architecture: {'x64' if fh.Machine==0x8664 else 'x86'} ({hex(fh.Machine)})") print(f" Compile time: {datetime.datetime.fromtimestamp(fh.TimeDateStamp)} UTC") print(f" Subsystem: {['','Native','GUI','Console'][oh.Subsystem] if oh.Subsystem<=3 else oh.Subsystem}") print(f" Entry point: {hex(oh.AddressOfEntryPoint)}") print(f" Image base: {hex(oh.ImageBase)}") # Sections print(f"\n[SECTIONS]") for s in pe.sections: n = s.Name.decode().rstrip('\x00') d = s.get_data() counts = [0]*256 for b in d: counts[b]+=1 ent = -sum((c/len(d))*math.log2(c/len(d)) for c in counts if c) if d else 0 flag = " *** PACKED ***" if ent > 7.0 else " * high" if ent > 6.5 else "" print(f" {n:<10} size:{len(d):<8} entropy:{ent:.2f}{flag}") # Import count imp_count = 0 if hasattr(pe,'DIRECTORY_ENTRY_IMPORT'): for lib in pe.DIRECTORY_ENTRY_IMPORT: imp_count += len([i for i in lib.imports if i.name]) print(f"\n[IMPORTS] {imp_count} functions") if imp_count < 5: print(" *** VERY FEW IMPORTS — likely packed or shellcode loader ***") # Suspicious imports SUSPICIOUS = { 'VirtualAllocEx':'Process injection', 'WriteProcessMemory':'Process injection', 'CreateRemoteThread':'Process injection', 'SetWindowsHookEx':'Keylogger', 'GetAsyncKeyState':'Keylogger', 'CryptEncrypt':'Encryption capability', 'InternetOpenUrl':'HTTP requests', 'IsDebuggerPresent':'Anti-debug', } found = [] if hasattr(pe,'DIRECTORY_ENTRY_IMPORT'): for lib in pe.DIRECTORY_ENTRY_IMPORT: for imp in lib.imports: if imp.name and imp.name.decode() in SUSPICIOUS: found.append((imp.name.decode(), SUSPICIOUS[imp.name.decode()])) if found: print("\n[SUSPICIOUS IMPORTS]") for f,r in found: print(f" {f:<35} → {r}") # Overlay last = max(pe.sections, key=lambda s: s.PointerToRawData+s.SizeOfRawData) ov = os.path.getsize(fname) - (last.PointerToRawData+last.SizeOfRawData) print(f"\n[OVERLAY] {ov} bytes {'*** DETECTED ***' if ov > 0 else '(none)'}") print("\n" + "="*60) EOF

Individual commands

# Install pefile pip install pefile # Quick check with pecheck (if installed) pecheck suspicious.exe # PE-bear: GUI tool — best for visual section analysis # Download: github.com/hasherezade/pe-bear # Verify Authenticode signature (Windows) Get-AuthenticodeSignature suspicious.exe | Select * # Check imports with dumpbin (Windows, part of Visual Studio) dumpbin /imports suspicious.exe dumpbin /exports suspicious.exe # objdump (Linux, MinGW) objdump -x suspicious.exe | grep -A5 "Import Tables" # Detect packer die suspicious.exe # Detect-It-Easy peid suspicious.exe # PEiD (older but useful) # Section entropy with radare2 r2 -c 'iS~entropy' suspicious.exe