This site uses cookies to serve ads via Google AdSense and to analyse site usage. By continuing to use this site, you consent to our use of cookies. Privacy Policy
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
Field
Location
What It Tells You
e_magic
DOS Header offset 0
Must be 0x5A4D ("MZ") — any other value means not a valid PE
e_lfanew
DOS Header offset 0x3C
Offset to the PE signature — unusually large value is suspicious
Machine
File Header
0x014C = x86, 0x8664 = x64 — mismatch with system = suspicious
TimeDateStamp
File Header
Compile timestamp — see Indicators tab for timestomping detection
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 Count
Interpretation
0 imports
Almost certainly shellcode or a loader stub
1-3 imports, only LoadLibrary/GetProcAddress
Dynamic API resolution — analysis requires unpacking or dynamic analysis
4-15 imports
Suspicious — check if binary is packed
15+ imports across multiple DLLs
More typical of legitimate software (not conclusive)
⚠️ Malicious PE Indicators
Timestamp manipulation (timestomping)
Observation
Verdict
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 hours
Suspicious — may have been copied/deployed after compilation
Compile time matches known malware family release date
Corroborating evidence of attribution
Debug directory timestamp differs from compile timestamp
File 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 check
Red flag
CompanyName
Claims Microsoft/Google/Adobe but file is not signed by that company
OriginalFilename
Claims to be svchost.exe, explorer.exe — but path or hash doesn't match
FileDescription
Generic or empty — legitimate Microsoft tools always have descriptions
ProductVersion vs FileVersion
Mismatch or all zeros
Authenticode signature
No 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
We use cookies to remember your preferences. No advertising or tracking cookies.
Privacy Policy