Python PE parsing module for analyzing Portable Executable (PE) files with comprehensive header, section, and directory entry support
—
Utilities for detecting packed executables and identifying packers/compilers using signature databases. The peutils module provides sophisticated packer detection capabilities.
Manage and query PEiD signature databases for packer identification.
class SignatureDatabase:
def __init__(self, filename=None, data=None):
"""
Initialize signature database.
Args:
filename (str, optional): Path to PEiD signature file or URL
data (str, optional): Raw signature data
"""
def load(self, filename=None, data=None):
"""
Load additional signature file.
Args:
filename (str, optional): Path to signature file or URL
data (str, optional): Raw signature data
Note:
Can be called multiple times to combine signatures from
different sources. Supports loading from URLs.
"""
def match(self, pe, ep_only=True, section_start_only=False):
"""
Find exact signature matches for PE file.
Args:
pe: PE file object
ep_only (bool): Only check entry point signatures
section_start_only (bool): Only check section start signatures
Returns:
str or list: Packer name (ep_only=True) or list of (offset, name) tuples
"""
def match_all(self, pe, ep_only=True, section_start_only=False):
"""
Find all likely signature matches.
Args:
pe: PE file object
ep_only (bool): Only check entry point signatures
section_start_only (bool): Only check section start signatures
Returns:
list: All matching signatures instead of just most precise
"""
def match_data(self, code_data, ep_only=True, section_start_only=False):
"""
Match raw code data against signatures.
Args:
code_data (bytes): Raw code data to analyze
ep_only (bool): Use entry point signatures
section_start_only (bool): Use section start signatures
Returns:
str or list: Match results similar to match()
"""
def generate_ep_signature(self, pe, name, sig_length=512):
"""
Generate entry point signature for PE file.
Args:
pe: PE file object
name (str): Name for the signature
sig_length (int): Length of signature to generate
Returns:
str: Generated signature in PEiD format
"""
def generate_section_signatures(self, pe, name, sig_length=512):
"""
Generate signatures for all sections in PE file.
Args:
pe: PE file object
name (str): Base name for signatures
sig_length (int): Length of signatures to generate
Returns:
str: Generated signatures for all sections
"""High-level functions for packer and malware detection.
def is_probably_packed(pe):
"""
Determine if PE file is likely packed using entropy analysis.
Args:
pe: PE file object
Returns:
bool: True if file appears to be packed
Note:
Uses entropy analysis with empirical thresholds:
- Section entropy > 7.4
- Compressed data ratio > 20%
"""
def is_suspicious(pe):
"""
Analyze PE file for suspicious characteristics.
Args:
pe: PE file object
Returns:
bool: True if file has suspicious characteristics
Note:
Checks for unusual import locations, unrecognized sections,
long ASCII strings, and relocations at entry point.
"""
def is_valid(pe):
"""
Validate PE file structure.
Args:
pe: PE file object
Returns:
bool: True if PE structure is valid
Note:
Currently a placeholder function.
"""import pefile
import peutils
# Load PE file
with pefile.PE('suspected_packed.exe') as pe:
# Quick entropy-based packer detection
if peutils.is_probably_packed(pe):
print("File is probably packed")
else:
print("File does not appear to be packed")
# Check for suspicious characteristics
if peutils.is_suspicious(pe):
print("File has suspicious characteristics")import pefile
import peutils
# Load signature database
sig_db = peutils.SignatureDatabase('userdb.txt')
# Can also load from URL
# sig_db = peutils.SignatureDatabase('https://example.com/signatures.txt')
# Load additional signatures
sig_db.load('additional_sigs.txt')
with pefile.PE('executable.exe') as pe:
# Check for packer signatures at entry point
matches = sig_db.match(pe, ep_only=True)
if matches:
print(f"Detected packer: {matches}")
else:
print("No packer signature found at entry point")
# Check section starts for packer signatures
section_matches = sig_db.match(pe, ep_only=False, section_start_only=True)
if section_matches:
print("Section signature matches:")
for offset, packer in section_matches:
print(f" Offset 0x{offset:08x}: {packer}")
# Get all possible matches
all_matches = sig_db.match_all(pe, ep_only=True)
if all_matches:
print("All possible matches:")
for match in all_matches:
print(f" {match}")import pefile
import peutils
def detailed_entropy_analysis(pe):
"""Perform detailed entropy analysis for packer detection."""
print("Entropy Analysis:")
print("-" * 40)
high_entropy_sections = 0
total_sections = len(pe.sections)
for section in pe.sections:
name = section.Name.decode('utf-8').strip('\x00')
entropy = section.get_entropy()
size = section.SizeOfRawData
print(f"{name:<10}: Entropy={entropy:.3f}, Size={size}")
if entropy > 7.4: # High entropy threshold
high_entropy_sections += 1
print(f" ^ High entropy section (possible packing/encryption)")
# Overall assessment
packed_ratio = high_entropy_sections / total_sections if total_sections > 0 else 0
print(f"\nSummary:")
print(f"High entropy sections: {high_entropy_sections}/{total_sections}")
print(f"Packed ratio: {packed_ratio:.2%}")
if packed_ratio > 0.2: # 20% threshold
print("Assessment: Likely packed")
else:
print("Assessment: Probably not packed")
return packed_ratio > 0.2
# Usage
with pefile.PE('executable.exe') as pe:
is_packed = detailed_entropy_analysis(pe)
# Compare with peutils assessment
peutils_assessment = peutils.is_probably_packed(pe)
print(f"\npeutils assessment: {peutils_assessment}")import pefile
import peutils
def generate_custom_signatures(pe_file, output_file):
"""Generate custom signatures for a PE file."""
sig_db = peutils.SignatureDatabase()
with pefile.PE(pe_file) as pe:
# Generate entry point signature
ep_sig = sig_db.generate_ep_signature(pe, "Custom_Sample", 256)
# Generate section signatures
section_sigs = sig_db.generate_section_signatures(pe, "Custom_Sample", 256)
# Save signatures
with open(output_file, 'w') as f:
f.write("// Entry Point Signature\n")
f.write(ep_sig)
f.write("\n\n// Section Signatures\n")
f.write(section_sigs)
print(f"Generated signatures saved to {output_file}")
# Usage
generate_custom_signatures('unique_packer.exe', 'custom_signatures.txt')import pefile
import peutils
import os
def batch_packer_analysis(directory, sig_db_path=None):
"""Analyze multiple PE files for packer detection."""
# Load signature database if provided
sig_db = None
if sig_db_path and os.path.exists(sig_db_path):
sig_db = peutils.SignatureDatabase(sig_db_path)
results = {}
# Analyze all PE files in directory
for filename in os.listdir(directory):
if filename.lower().endswith(('.exe', '.dll')):
filepath = os.path.join(directory, filename)
try:
with pefile.PE(filepath) as pe:
# Entropy-based detection
entropy_packed = peutils.is_probably_packed(pe)
suspicious = peutils.is_suspicious(pe)
# Signature-based detection
sig_match = None
if sig_db:
sig_match = sig_db.match(pe, ep_only=True)
results[filename] = {
'entropy_packed': entropy_packed,
'suspicious': suspicious,
'signature_match': sig_match
}
except Exception as e:
results[filename] = {'error': str(e)}
# Display results
print("Batch Packer Analysis Results:")
print("=" * 60)
print(f"{'Filename':<20} {'Entropy':<8} {'Suspicious':<10} {'Signature':<20}")
print("-" * 60)
for filename, result in results.items():
if 'error' in result:
print(f"{filename:<20} ERROR: {result['error']}")
else:
entropy = "PACKED" if result['entropy_packed'] else "clean"
suspicious = "YES" if result['suspicious'] else "no"
signature = result['signature_match'] or "none"
print(f"{filename:<20} {entropy:<8} {suspicious:<10} {signature:<20}")
# Usage
batch_packer_analysis('./samples', 'userdb.txt')import pefile
import peutils
def advanced_packer_detection(pe_file):
"""Comprehensive packer detection combining multiple techniques."""
print(f"Advanced Packer Detection: {pe_file}")
print("=" * 50)
with pefile.PE(pe_file) as pe:
# 1. Entropy analysis
print("1. Entropy Analysis:")
entropy_packed = peutils.is_probably_packed(pe)
print(f" Result: {'PACKED' if entropy_packed else 'NOT PACKED'}")
# 2. Suspicious characteristics
print("2. Suspicious Characteristics:")
suspicious = peutils.is_suspicious(pe)
print(f" Result: {'SUSPICIOUS' if suspicious else 'NORMAL'}")
# 3. Section analysis
print("3. Section Analysis:")
suspicious_sections = 0
for section in pe.sections:
name = section.Name.decode('utf-8').strip('\x00')
entropy = section.get_entropy()
# Check for suspicious section names
if name in ['.upx0', '.upx1', '.aspack', '.petite', '.packed']:
print(f" Known packer section: {name}")
suspicious_sections += 1
# Check for high entropy with executable characteristics
if entropy > 7.0 and section.Characteristics & 0x20000000: # Executable
print(f" High entropy executable section: {name} (entropy: {entropy:.2f})")
suspicious_sections += 1
# 4. Import table analysis
print("4. Import Analysis:")
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
import_count = sum(len(entry.imports) for entry in pe.DIRECTORY_ENTRY_IMPORT)
dll_count = len(pe.DIRECTORY_ENTRY_IMPORT)
print(f" DLLs: {dll_count}, Functions: {import_count}")
# Low import count might indicate packing
if import_count < 10:
print(" Low import count - possible packing")
suspicious_sections += 1
else:
print(" No imports found - highly suspicious")
suspicious_sections += 1
# 5. Entry point analysis
print("5. Entry Point Analysis:")
ep_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint
ep_section = pe.get_section_by_rva(ep_rva)
if ep_section:
section_name = ep_section.Name.decode('utf-8').strip('\x00')
print(f" Entry point in section: {section_name}")
# Entry point not in .text section is suspicious
if section_name not in ['.text', 'CODE']:
print(f" Entry point not in standard code section - suspicious")
suspicious_sections += 1
# Final assessment
print("\n6. Final Assessment:")
print("-" * 20)
confidence_score = 0
if entropy_packed:
confidence_score += 3
if suspicious:
confidence_score += 2
if suspicious_sections > 0:
confidence_score += suspicious_sections
if confidence_score >= 5:
assessment = "HIGHLY LIKELY PACKED"
elif confidence_score >= 3:
assessment = "POSSIBLY PACKED"
else:
assessment = "PROBABLY NOT PACKED"
print(f"Confidence Score: {confidence_score}")
print(f"Assessment: {assessment}")
# Usage
advanced_packer_detection('suspicious_file.exe')Install with Tessl CLI
npx tessl i tessl/pypi-pefile