In-memory and fileless malware detection — AMSI bypass analysis, reflective DLL injection, CLR-hosted payloads, process hollowing, PowerShell cradle chains, WMI/COM persistence, and registry-resident payloads. Covers detection, memory forensics, and payload extraction.
82%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Medium
Suggest reviewing before use
Detect, extract, and analyze malware that lives entirely in memory — no files on disk. Covers AMSI bypass techniques, reflective loading, process injection variants, and LOLBin abuse chains.
# Scan for injected code in running processes
pe-sieve.exe /pid <PID> /dir ./dump/ /shellc /iat 3
# Scan ALL processes at once
hollows_hunter.exe /dir ./hunt_results/
# Dump suspicious process memory
procdump.exe -ma <PID> process.dmp
# Search memory dump for PE headers
python3 -c "
d=open('process.dmp','rb').read()
import re
for m in re.finditer(b'MZ',d):
off=m.start()
if d[off+0x3c:off+0x40] != b'\x00'*4:
print(f'PE header at offset 0x{off:x}')
"
# Extract PowerShell from event logs
wevtutil qe Microsoft-Windows-PowerShell/Operational /f:text /c:50| Technique | ID | How It Appears |
|---|---|---|
| PowerShell | T1059.001 | Encoded cradles, AMSI bypass, download-execute chains |
| Reflective Code Loading | T1620 | Assembly.Load(byte[]), ReflectiveLoader in shellcode, Donut payloads |
| Process Injection | T1055 | Classic injection, process hollowing (T1055.012), thread hijacking |
| Impair Defenses: AMSI | T1562.001 | Patching AmsiScanBuffer, CLR hooking, registry disable |
| WMI Event Subscription | T1546.003 | Fileless persistence via WMI consumers |
| Signed Binary Proxy | T1218 | mshta, rundll32, regsvr32, msbuild LOLBin execution |
Identify processes with injected or hollowed code regions.
# pe-sieve — scan single process for implants
pe-sieve.exe /pid <PID> /shellc /threads /iat 3 /dir ./pe_sieve_out/
# Output: report.json with findings per memory region
# hollows_hunter — system-wide sweep
hollows_hunter.exe /dir ./hh_out/ /shellc /loop
# Scans every process; reports: hollowed, implanted, unreachable
# Process Hacker — manual inspection (GUI)
# 1. Open Process Hacker → Properties on suspect process
# 2. Memory tab → look for:
# - RWX regions (should be rare in legit processes)
# - Private regions with PE headers (MZ signature)
# - Mapped regions not backed by a file
# 3. Threads tab → look for:
# - Threads with start address in unbacked memory
# - Threads in ntdll!RtlUserThreadStart pointing to RWX region
# Volatility 3 — analyze memory image
vol3 -f memory.raw windows.malfind
vol3 -f memory.raw windows.vadinfo --pid <PID>
vol3 -f memory.raw windows.hollowfindUnderstand and detect AMSI patching techniques.
# Common AMSI bypass: patching AmsiScanBuffer in amsi.dll
# The bypass writes 0xC3 (RET) or specific bytes to the function prolog
# Detection: compare amsi.dll in-memory vs on-disk
# Check if AMSI is patched in a process
python3 << 'EOF'
import ctypes, ctypes.wintypes
# Load amsi.dll and get AmsiScanBuffer address
amsi = ctypes.windll.LoadLibrary("amsi.dll")
addr = ctypes.windll.kernel32.GetProcAddress(amsi._handle, b"AmsiScanBuffer")
# Read first bytes — should NOT be 0xC3 (ret) or 0x80 (cmp byte patched)
buf = (ctypes.c_byte * 8)()
ctypes.memmove(buf, addr, 8)
first_bytes = bytes(buf)
print(f"AmsiScanBuffer prolog: {first_bytes.hex()}")
if first_bytes[0] == 0xC3 or first_bytes[:3] == bytes([0x48, 0x31, 0xC0]):
print("[!] AMSI IS PATCHED — bypass active")
else:
print("[+] AMSI appears intact")
EOF
# ETW-based AMSI logging analysis
# Check for disabled ETW providers (another bypass vector)
logman query providers | findstr /i "amsi\|antimalware"
# Scan PowerShell scriptblock logs for bypass patterns
# Event Log: Microsoft-Windows-PowerShell/Operational, Event ID 4104
wevtutil qe "Microsoft-Windows-PowerShell/Operational" /q:"*[System[EventID=4104]]" /f:text /c:100 > scriptblocks.txt
grep -iE "AmsiScanBuffer|amsiInitFailed|AmsiUtils|Reflection\.Assembly|VirtualProtect|Marshal\.Copy" scriptblocks.txt
# Known AMSI bypass signatures to hunt
# 1. Matt Graeber's reflection: [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
# 2. Direct patch: VirtualProtect + Marshal.Copy on AmsiScanBuffer
# 3. amsiInitFailed field set to $true
# 4. CLR hooking via profiler DLL
# 5. Registry disable: HKCU\Software\Microsoft\Windows Script\Settings\AmsiEnable=0Detect and extract reflectively loaded DLLs.
# Reflective loaders don't use LoadLibrary — they parse PE headers manually
# Key indicators:
# - RWX memory region with PE header
# - No corresponding file on disk
# - Thread start address in unbacked memory
# pe-sieve dump of reflective DLL
pe-sieve.exe /pid <PID> /dir ./dump/ /imp 3 /shellc
# Reconstructs import table from the dump
# Manual extraction from process memory
python3 << 'EOF'
import ctypes
from ctypes import wintypes
PROCESS_VM_READ = 0x0010
PROCESS_QUERY_INFORMATION = 0x0400
pid = <PID>
kernel32 = ctypes.windll.kernel32
handle = kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, pid)
# Walk memory regions looking for MZ headers in RWX pages
addr = 0
while addr < 0x7FFFFFFFFFFF:
mbi = ctypes.create_string_buffer(48)
if kernel32.VirtualQueryEx(handle, addr, mbi, 48) == 0:
break
# Parse MEMORY_BASIC_INFORMATION
base = int.from_bytes(mbi[0:8], 'little')
size = int.from_bytes(mbi[24:32], 'little')
protect = int.from_bytes(mbi[32:36], 'little')
state = int.from_bytes(mbi[16:20], 'little')
# PAGE_EXECUTE_READWRITE = 0x40, MEM_COMMIT = 0x1000
if state == 0x1000 and protect == 0x40 and size > 0x1000:
buf = (ctypes.c_char * min(size, 0x200))()
read = ctypes.c_size_t()
kernel32.ReadProcessMemory(handle, base, buf, len(buf), ctypes.byref(read))
if buf.raw[:2] == b'MZ':
print(f"[!] PE in RWX at 0x{base:x} size=0x{size:x}")
addr = base + size
kernel32.CloseHandle(handle)
EOF
# Volatility malfind — finds injected code regions
vol3 -f memory.raw windows.malfind --pid <PID> --dump --dump-dir ./malfind_dumps/# Detect hollowed processes: legitimate image path but injected code
# Key sign: PEB ImageBaseAddress differs from on-disk PE base
# Volatility hollow process detection
vol3 -f memory.raw windows.hollowfind
# Manual check with pe-sieve
pe-sieve.exe /pid <PID> /hooks /iat 3 /dir ./hollow_check/
# Look for "replaced" status in report.json
# Sysmon-based detection (Event ID 25 — process tampering)
wevtutil qe "Microsoft-Windows-Sysmon/Operational" /q:"*[System[EventID=25]]" /f:text /c:20
# Common hollowing targets (watch these processes):
# svchost.exe, explorer.exe, notepad.exe, dllhost.exe, RuntimeBroker.exe
# If their in-memory image doesn't match disk → hollowed# Extract full PowerShell execution chain from logs
# ScriptBlock Logging (Event ID 4104) captures decoded content
# Export all scriptblock events
powershell -c "Get-WinEvent -LogName 'Microsoft-Windows-PowerShell/Operational' | Where-Object {$_.Id -eq 4104} | Select-Object -Property TimeCreated,Message | Export-Csv scriptblocks.csv"
# Common fileless chain pattern:
# Stage 0: mshta/wscript/shortcut → powershell -enc <base64>
# Stage 1: IEX(New-Object Net.WebClient).DownloadString('http://...')
# Stage 2: Reflective load .NET assembly or shellcode via [System.Reflection.Assembly]::Load()
# Stage 3: In-memory C2 beacon (Cobalt Strike, Sliver, etc.)
# Decode base64 PowerShell
python3 << 'EOF'
import base64
encoded = "<BASE64_FROM_CMDLINE>"
decoded = base64.b64decode(encoded).decode('utf-16-le')
print(decoded)
# Extract URLs
import re
urls = re.findall(r'https?://[^\s\'"]+', decoded)
for u in urls:
print(f" URL: {u}")
EOF
# Reconstruct WMI persistence (fileless persistence mechanism)
wmic /namespace:\\root\subscription path __EventFilter list full
wmic /namespace:\\root\subscription path CommandLineEventConsumer list full
wmic /namespace:\\root\subscription path __FilterToConsumerBinding list full# .NET assemblies loaded via Assembly.Load(byte[]) leave CLR artifacts
# Detect: ETW CLR events or inspect AppDomain loaded assemblies
# List loaded assemblies in a process using ClrMD
python3 << 'EOF'
# Use clrmd via pythonnet or dump with SOS
# Alternative: use procdump + dotnet-dump
import subprocess
subprocess.run(["procdump", "-ma", str(<PID>), "clr_dump.dmp"])
# Then analyze with dotnet-dump
subprocess.run(["dotnet-dump", "analyze", "clr_dump.dmp"],
input=b"clrmodules\ndumpheap -stat -type System.Reflection.Assembly\nexit\n")
EOF
# WinDbg + SOS approach for CLR inspection
# .loadby sos clr
# !DumpDomain — lists all AppDomains and loaded assemblies
# !DumpAssembly <addr> — details of a specific assembly
# !SaveModule <addr> recovered.dll — save module to disk
# ETW provider for CLR loading events
logman create trace clr_trace -p {E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4} 0x8 -o clr_events.etl
# Keywords 0x8 = LoaderKeyword — captures Assembly.Load events| Tool | Purpose | Install |
|---|---|---|
| pe-sieve | Detect implants/hollowing in running processes | github.com/hasherezade/pe-sieve |
| hollows_hunter | System-wide pe-sieve scan | github.com/hasherezade/hollows_hunter |
| Process Hacker | Advanced process inspector (GUI) | processhacker.sourceforge.io |
| Volatility 3 | Memory image forensics | github.com/volatilityfoundation/volatility3 |
| Moneta | Detect in-memory malware artifacts | github.com/forrest-orr/moneta |
| Sysmon | Windows system activity logging | docs.microsoft.com/sysinternals/sysmon |
| procdump | Process memory dumper | docs.microsoft.com/sysinternals/procdump |
| dotnet-dump | .NET managed dump analyzer | dotnet tool install -g dotnet-dump |
| ETWExplorer | ETW provider browser | github.com/zodiacon/EtwExplorer |
| Indicator | Description | Detection |
|---|---|---|
| RWX memory region with MZ header | Reflectively loaded PE | pe-sieve /shellc; Volatility malfind |
AmsiScanBuffer patched prolog | AMSI bypass active | Compare in-memory vs on-disk amsi.dll |
PowerShell -enc with long base64 | Encoded PowerShell payload | Sysmon Event ID 1, CommandLine regex |
| WMI event subscription | Fileless persistence | WMI query __EventFilter / CommandLineEventConsumer |
| Thread start in unbacked memory | Injected thread | Process Hacker threads tab; Moneta scan |
csc.exe / msbuild.exe with no project | LOLBin code compilation | Sysmon: parent process analysis |
| High scriptblock Event ID 4104 volume | PowerShell cradle chain | SIEM correlation on 4104 burst |
| Issue | Resolution |
|---|---|
| pe-sieve misses injected .NET | Use /dotnet flag; .NET assemblies need CLR-aware scanning |
| Process terminates before dump | Use Sysmon + scriptblock logging for post-mortem; configure proactive ETW tracing |
| AMSI patched before logging starts | Use kernel-mode ETW or driver-based monitoring (e.g., Microsoft Defender ATP kernel sensor) |
| PowerShell constrained language mode | Attacker may bypass via Add-Type with inline C# or CMSTP/MSBuild LOLBins |
| Memory image too large for Volatility | Use --pid to scope analysis; increase system swap |
| Anti-forensics: timestomped/cleared logs | Cross-reference with NTFS $MFT, Sysmon, and network logs |
| Kernel-mode injection (driver-level) | Out of scope for user-mode tools — see reverser/rootkit-analysis |
IF suspicious process has RWX regions or pe-sieve/hollows_hunter hits:
→ Dump process memory (procdump -ma)
→ Extract implants with pe-sieve /imp 3
→ Identify payload type (.NET → dnSpy; native → Ghidra)
→ Trace injection chain (parent process, command line, scriptblocks)
ELSE IF PowerShell scriptblock logs show encoded/obfuscated content:
→ Decode all stages
→ Identify download URLs and in-memory payloads
→ Check for AMSI bypass indicators
ELSE IF WMI persistence found:
→ Extract consumer script/command
→ Trace event filter trigger conditions
→ Remove binding + consumer + filter
ELSE:
→ Enable Sysmon + scriptblock logging + ETW
→ Re-run detection after activity windowPurpleAILAB/Decepticon
49888ee
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.