Performs static analysis of Windows PE malware samples using PEStudio to examine file headers, imports, strings, and resources without executing the binary, identifying packing, anti-analysis tricks, and malicious imports. Use for pre-execution triage of a suspicious Windows executable before sandbox detonation.
Performs static analysis of Windows PE malware samples using PEStudio to examine file headers, imports, strings, and resources without executing the binary, identifying packing, anti-analysis tricks, and malicious imports. Use for pre-execution triage of a suspicious Windows executable before sandbox detonation.
import pefile
import hashlib
import math
pe = pefile.PE("suspect.exe")
# Compilation timestampimport datetime
timestamp = pe.FILE_HEADER.TimeDateStamp
compile_time = datetime.datetime.utcfromtimestamp(timestamp)
print(f"Compile Time: {compile_time} UTC")
# Section analysis with entropy calculationfor section in pe.sections:
name = section.Name.decode().rstrip('\x00')
entropy = section.get_entropy()
raw_size = section.SizeOfRawData
virtual_size = section.Misc_VirtualSize
ratio = virtual_size / raw_size if raw_size > 0else0print(f"Section: {name:8s} Entropy: {entropy:.2f} Raw: {raw_size:>10} Virtual: {virtual_size:>10} Ratio: {ratio:.2f}")
if entropy > 7.0:
print(f" [!] HIGH ENTROPY - likely packed or encrypted")
if ratio > 10:
print(f" [!] HIGH V/R RATIO - unpacking stub likely present")
Step 3: Analyze Import Address Table (IAT)
Identify suspicious API imports that indicate malware capabilities:
# Extract and categorize imports
suspicious_imports = {
"Process Injection": ["VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread", "NtCreateThreadEx"],
"Keylogging": ["GetAsyncKeyState", "SetWindowsHookExA", "GetKeyState"],
"Persistence": ["RegSetValueExA", "CreateServiceA", "SchTasksCreate"],
"Evasion": ["IsDebuggerPresent", "CheckRemoteDebuggerPresent", "NtQueryInformationProcess"],
"Network": ["InternetOpenA", "HttpSendRequestA", "URLDownloadToFileA", "WSAStartup"],
"File Operations": ["CreateFileA", "WriteFile", "DeleteFileA", "MoveFileA"],
"Crypto": ["CryptEncrypt", "CryptDecrypt", "CryptAcquireContextA"],
}
for entry in pe.DIRECTORY_ENTRY_IMPORT:
dll_name = entry.dll.decode()
for imp in entry.imports:
if imp.name:
func_name = imp.name.decode()
for category, funcs in suspicious_imports.items():
if func_name in funcs:
print(f"[!] {category}: {dll_name} -> {func_name}")
Step 4: Extract and Analyze Strings
Use FLOSS for obfuscated strings and standard strings extraction:
# Standard strings extraction (ASCII and Unicode)
strings -a suspect.exe > strings_ascii.txt
strings -el suspect.exe > strings_unicode.txt
# FLOSS for decoded/deobfuscated strings
floss suspect.exe --output-json floss_output.json
# Search for network indicators in strings
grep -iE "(http|https|ftp)://" strings_ascii.txt
grep -iE "([0-9]{1,3}\.){3}[0-9]{1,3}" strings_ascii.txt
grep -iE "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" strings_ascii.txt
# Search for registry keys
grep -i "HKLM\\|HKCU\\|SOFTWARE\\|CurrentVersion\\Run" strings_ascii.txt
# Search for file paths and extensions
grep -iE "\.(exe|dll|bat|ps1|vbs|tmp)" strings_ascii.txt
Step 5: Inspect Resources and Embedded Data
Examine the PE resource section for embedded payloads or configuration:
# Extract resources from PE fileifhasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
ifhasattr(resource_type, 'directory'):
for resource_id in resource_type.directory.entries:
ifhasattr(resource_id, 'directory'):
for resource_lang in resource_id.directory.entries:
data = pe.get_data(resource_lang.data.struct.OffsetToData,
resource_lang.data.struct.Size)
entropy = calculate_entropy(data)
print(f"Resource Type: {resource_type.id} Size: {len(data)} Entropy: {entropy:.2f}")
if entropy > 7.0:
print(f" [!] High entropy resource - possible embedded payload")
# Check for PE signature in resource (embedded executable)if data[:2] == b'MZ':
print(f" [!] Embedded PE detected in resource")
withopen(f"extracted_resource_{resource_type.id}.bin", "wb") as f:
f.write(data)
Step 6: Check for Packing and Protection
Determine if the binary is packed or protected:
# Detect packer with Detect It Easy (DIE)
diec suspect.exe
# Check with PEiD signatures (command-line version)
python3 -c "
import pefile
pe = pefile.PE('suspect.exe')
# Check for common packer section names
packer_sections = {'.upx0': 'UPX', '.aspack': 'ASPack', '.adata': 'ASPack',
'.nsp0': 'NsPack', '.vmprotect': 'VMProtect', '.themida': 'Themida'}
for section in pe.sections:
name = section.Name.decode().rstrip('\x00').lower()
if name in packer_sections:
print(f'[!] Packer detected: {packer_sections[name]} (section: {name})')
# Check import table size (very few imports suggest packing)
import_count = sum(len(entry.imports) for entry in pe.DIRECTORY_ENTRY_IMPORT)
if import_count < 10:
print(f'[!] Only {import_count} imports - likely packed')
"
Step 7: Generate Static Analysis Report
Compile all findings into a structured triage report:
Document the following for each analyzed sample:
- File identification (hashes, file type, size, compile timestamp)
- Packing/protection status and identified packer
- Suspicious imports categorized by capability
- Network indicators extracted from strings (IPs, domains, URLs)
- Embedded resources and their characteristics
- Overall threat assessment and recommended next steps (sandbox execution, YARA rule creation)
Key Concepts
Term
Definition
PE (Portable Executable)
The file format for Windows executables (.exe, .dll, .sys) containing headers, sections, imports, and resources that define how the OS loads the binary
Import Address Table (IAT)
PE structure listing external DLL functions the executable calls at runtime; reveals program capabilities and intent
Section Entropy
Statistical measure of randomness in a PE section; values above 7.0 (out of 8.0) indicate compression, encryption, or packing
FLOSS
FireEye Labs Obfuscated String Solver; automatically extracts and decodes obfuscated strings that standard strings misses
Packing
Compression or encryption of a PE file's code section to hinder static analysis; requires runtime unpacking stub to execute
PE Resources
Data section within a PE file that can contain icons, dialogs, version info, or attacker-embedded payloads and configuration data
Compilation Timestamp
Timestamp in the PE header indicating when the binary was compiled; can be forged but often reveals development timeline
Tools & Systems
PEStudio: Free Windows tool for static analysis of PE files providing indicators, imports, strings, and resource inspection in a single interface
pefile (Python): Python library for parsing and analyzing PE file structures programmatically for automated analysis pipelines
FLOSS: FireEye tool that extracts obfuscated strings from malware using static analysis techniques including stack string decoding
Detect It Easy (DIE): Packer and compiler detection tool that identifies protectors, compilers, and linkers used to build PE files
CFF Explorer: Advanced PE editor and viewer for detailed inspection of PE headers, sections, imports, and resource directories
Common Scenarios
Scenario: Triaging a Suspicious Email Attachment
Context: SOC receives an alert on a suspicious executable attached to a phishing email. The file needs rapid triage to determine if it is malicious before committing sandbox resources.
Approach:
Compute SHA-256 hash and query VirusTotal for existing detections and community comments
Open in PEStudio and check the indicators tab for red/yellow flagged items
Verify compile timestamp (future dates or dates from 1970 indicate timestamp manipulation)
Check imports for VirtualAllocEx, CreateRemoteThread (injection), URLDownloadToFileA (downloader)
Extract strings and search for C2 URLs, IP addresses, and file paths
Check resources for embedded PE files or high-entropy data blobs
Assess packing status; if packed, note the packer and plan for unpacking before deeper analysis
Pitfalls:
Trusting the PE compile timestamp without corroborating evidence (timestamps are trivially forged)
Concluding a file is benign because it has few suspicious imports (packed malware hides real imports)
Missing Unicode strings by only running ASCII string extraction
Not checking overlay data appended after the last PE section (common hiding spot for configuration data)