Asi performing-static-malware-analysis-with-pe-studio
git clone https://github.com/plurigrid/asi
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/asi/skills/performing-static-malware-analysis-with-pe-studio" ~/.claude/skills/plurigrid-asi-performing-static-malware-analysis-with-pe-studio && rm -rf "$T"
plugins/asi/skills/performing-static-malware-analysis-with-pe-studio/SKILL.mdPerforming Static Malware Analysis with PEStudio
When to Use
- A suspicious Windows executable has been collected and needs initial triage before sandbox execution
- You need to identify imports, strings, and resources that reveal malware functionality without running the sample
- Determining whether a PE file is packed, obfuscated, or contains anti-analysis techniques
- Extracting indicators of compromise (hashes, URLs, IPs, registry keys) embedded in a binary
- Classifying a sample's capabilities based on its import table and section characteristics
Do not use for dynamic behavioral analysis requiring execution; use a sandbox (Cuckoo, ANY.RUN) for runtime behavior observation.
Prerequisites
- PEStudio (free edition from https://www.winitor.com/) installed on an isolated analysis workstation
- Python 3.8+ with
library for scripted PE analysis (pefile
)pip install pefile - CFF Explorer or PE-bear as supplementary PE analysis tools
- Access to VirusTotal API for hash lookups and community intelligence
- Isolated analysis VM with no network connectivity to production systems
- FLOSS (FireEye Labs Obfuscated String Solver) for extracting obfuscated strings
Workflow
Step 1: Compute File Hashes and Verify Sample Integrity
Generate cryptographic hashes for identification and intelligence lookup:
# Generate MD5, SHA-1, and SHA-256 hashes md5sum suspect.exe sha1sum suspect.exe sha256sum suspect.exe # Check hash against VirusTotal curl -s -X GET "https://www.virustotal.com/api/v3/files/$(sha256sum suspect.exe | cut -d' ' -f1)" \ -H "x-apikey: $VT_API_KEY" | jq '.data.attributes.last_analysis_stats' # Get file type with magic bytes verification file suspect.exe
Step 2: Examine PE Headers and Section Table
Open the sample in PEStudio and inspect structural properties:
PEStudio Analysis Points: ━━━━━━━━━━━━━━━━━━━━━━━━━ File Header: Compilation timestamp, target architecture (x86/x64) Optional Header: Entry point address, image base, subsystem (GUI/console) Section Table: Section names, virtual/raw sizes, entropy values High entropy (>7.0) in .text/.rsrc suggests packing Signatures: Authenticode signature presence and validity
Scripted PE Header Analysis with pefile:
import pefile import hashlib import math pe = pefile.PE("suspect.exe") # Compilation timestamp import datetime timestamp = pe.FILE_HEADER.TimeDateStamp compile_time = datetime.datetime.utcfromtimestamp(timestamp) print(f"Compile Time: {compile_time} UTC") # Section analysis with entropy calculation for 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 > 0 else 0 print(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 file if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'): for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries: if hasattr(resource_type, 'directory'): for resource_id in resource_type.directory.entries: if hasattr(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") with open(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 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)
Output Format
STATIC MALWARE ANALYSIS REPORT ================================= Sample: suspect.exe MD5: d41d8cd98f00b204e9800998ecf8427e SHA-256: e3b0c44298fc1c149afbf4c8996fb924... File Size: 245,760 bytes File Type: PE32 executable (GUI) Intel 80386 Compile Time: 2025-09-14 08:23:15 UTC PACKING STATUS Packer Detected: None (native binary) Section Entropy: .text=6.42 .rdata=4.89 .data=3.21 .rsrc=7.81 Note: .rsrc section entropy elevated - check resources SUSPICIOUS IMPORTS [INJECTION] kernel32.dll -> VirtualAllocEx [INJECTION] kernel32.dll -> WriteProcessMemory [INJECTION] kernel32.dll -> CreateRemoteThread [EVASION] kernel32.dll -> IsDebuggerPresent [NETWORK] wininet.dll -> InternetOpenA [NETWORK] wininet.dll -> HttpSendRequestA [PERSISTENCE] advapi32.dll -> RegSetValueExA EXTRACTED INDICATORS URLs: hxxps://update.malicious[.]com/gate.php IPs: 185.220.101[.]42, 91.215.85[.]17 Registry Keys: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\svchost File Paths: C:\Users\Public\svchost.exe EMBEDDED RESOURCES Resource 101: Size=98304 Entropy=7.89 [!] Embedded PE detected Resource 102: Size=4096 Entropy=2.14 (configuration XML) ASSESSMENT Threat Level: HIGH Classification: Dropper with process injection capabilities Recommended: Execute in sandbox, extract embedded PE for separate analysis