Malware triage starts before any tool runs — a 30-second visual inspection of the file’s static properties often tells you which tools to run and what questions to ask.
Static Triage Checklist
File received: update.exe (SHA256: 4a3f7e2c8b1d5e9f3c7a2e6b4d8f1a5c)
Step 1: File identification Time: 30 seconds
└── file update.exe (OS file type detection)
└── xxd update.exe | head -2 (check MZ header)
└── sha256sum update.exe (hash for VT lookup)
Step 2: VirusTotal hash lookup Time: 2 minutes
└── curl -s https://www.virustotal.com/... (or browser)
└── If VT match → family identified, skip steps 3-5
└── If no VT match → new or modified sample, continue
Step 3: DIE / PEStudio metadata Time: 3 minutes
└── Compilation timestamp (forged?)
└── Architecture (x86/x64)
└── Entropy per section
└── Packer detection
└── Compiler/language identification
Step 4: Import analysis Time: 5 minutes
└── Which DLLs imported?
└── Which functions — network? injection? anti-debug?
└── Few imports → dynamic resolution → packed
Step 5: Strings analysis Time: 5 minutes
└── strings -a update.exe | grep -E "http|https|[0-9]{1,3}\.[0-9]+"
└── Registry keys, file paths, mutex names
└── User-Agent strings, encoded data
Total: ~15 minutes before sandbox detonation
PE Header Analysis
# Basic file identification
file update.exe
# PE32+ executable (DLL) (console) x86-64, for MS Windows
# PE header examination with pecheck (Python)
# Install: pip install pefile
python3 << 'EOF'
import pefile
pe = pefile.PE("update.exe")
# Compilation timestamp (may be forged)
import datetime
ts = pe.FILE_HEADER.TimeDateStamp
print(f"Compile time: {datetime.datetime.utcfromtimestamp(ts)} UTC")
# Architecture
print(f"Machine: {hex(pe.FILE_HEADER.Machine)}")
# 0x8664 = x64, 0x14c = x86
# Entry point
print(f"Entry point: {hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint)}")
# Section entropy
for section in pe.sections:
name = section.Name.decode().rstrip('\x00')
entropy = section.get_entropy()
size = section.SizeOfRawData
print(f"Section: {name:<10} Size: {size:>8} Entropy: {entropy:.2f}")
EOF
Import Table Analysis
# Extract all imported functions using pefile
python3 << 'EOF'
import pefile
pe = pefile.PE("update.exe")
suspicious_imports = {
# Process injection
"OpenProcess", "VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread",
"NtWriteVirtualMemory", "RtlCreateUserThread",
# Privilege escalation
"OpenProcessToken", "AdjustTokenPrivileges", "LookupPrivilegeValue",
# Anti-analysis
"IsDebuggerPresent", "CheckRemoteDebuggerPresent", "NtQueryInformationProcess",
# Network
"WSAStartup", "connect", "InternetOpen", "HttpSendRequest",
# Persistence
"RegSetValueEx", "RegCreateKeyEx", "CreateService",
}
print("=== IMPORT ANALYSIS ===")
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
for lib in pe.DIRECTORY_ENTRY_IMPORT:
dll_name = lib.dll.decode()
print(f"\n[DLL] {dll_name}")
for imp in lib.imports:
if imp.name:
func_name = imp.name.decode()
flag = " ⚠ SUSPICIOUS" if func_name in suspicious_imports else ""
print(f" {func_name}{flag}")
else:
print("No import table found — binary may be packed or shellcode")
EOF
Strings Analysis
# Extract strings (ASCII + Unicode)
strings -a -n 8 update.exe > strings_output.txt # ASCII, min 8 chars
strings -a -n 8 -e l update.exe >> strings_output.txt # Unicode (LE 16-bit)
# IOC extraction from strings
echo "=== NETWORK IOCs ==="
grep -E "https?://|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" strings_output.txt
echo "=== REGISTRY KEYS ==="
grep -i "HKCU\|HKLM\|SOFTWARE\\\\Microsoft" strings_output.txt | head -10
echo "=== STAGING PATHS ==="
grep -iE "\\\\temp\\\\|\\\\tmp\\\\|appdata|programdata|\\\\users\\\\public" strings_output.txt
echo "=== BASE64 BLOBS (length > 40 chars) ==="
grep -E "^[A-Za-z0-9+/]{40,}={0,2}$" strings_output.txt | head -10
echo "=== MUTEX/EVENT NAMES ==="
grep -iE "Global\\\\|mutex|event" strings_output.txt | head -10
echo "=== COMMON C2 INDICATORS ==="
grep -E "User-Agent|Mozilla|Content-Type|Authorization" strings_output.txt
Identifying Common Malware Families from Static Analysis
Family | Key Static Indicators
────────────────────────────────────────────────────────────────────
Cobalt Strike | Very few imports (.text high entropy), no readable strings
beacon | → Shellcode-like structure, requires dynamic analysis
|
Meterpreter | reverse_tcp/reverse_https strings, PAYLOAD_UUID
(staged) | Few imports if staged shellcode
|
Go-based | No Windows API imports (Go uses syscall directly)
implants | Strings: golang runtime, main.main, net/http
(Sliver, etc.) | File signature: Go binary detected by DIE
|
Njrat/AsyncRAT | .NET binary (detected by DIE), Confuser obfuscator
(RATs) | Strings: mutex name, registry key for persistence
|
Emotet/Qakbot | Packed (.text high entropy), minimal imports
(loaders) | Forged compilation timestamp, drop path strings
|
Mimikatz | Strings: sekurlsa, lsadump, privilege::debug
| Imports: LsaEnumerateLogonSessions, NtQueryVirtualMemory