Lesson 4 of 6 · 35 min read

Memory Forensics for Hunters — Volatility 3, malfind, and MemProcFS

Memory is the one place where attackers cannot hide: whatever code is executing must be in RAM. A process can delete its binary from disk, overwrite its registry key, and clear event logs — but as long as it’s running, its code, network connections, and module list are in memory. Memory forensics is the hunter’s truth oracle.


Memory Analysis Workflow

Memory Image (raw dump or live memory)

         ▼ Step 1: Process enumeration
    pslist + psscan (find all processes, catch DKOM)

         ▼ Step 2: Network state
    netscan (active + recently closed connections)

         ▼ Step 3: Target suspicious PIDs
    cmdline → what was the process started with?
    dlllist → what DLLs are loaded?
    ldrmodules → any unlinked modules (reflective injection)?

         ▼ Step 4: Injection detection
    malfind → executable private memory (shellcode / injected code)

         ▼ Step 5: Extract artifacts
    procdump → dump executable from memory
    dumpfiles → dump open file handles
    hashdump → extract NTLM hashes (if credential dump suspected)

Core Volatility 3 Commands

Process Enumeration

# List processes via EPROCESS linked list
vol.py -f memory.raw windows.pslist.PsList

# Scan for EPROCESS pool tags (catches DKOM-hidden procs)
vol.py -f memory.raw windows.psscan.PsScan

# Process tree with parent-child visualization
vol.py -f memory.raw windows.pstree.PsTree

# Command lines of all processes
vol.py -f memory.raw windows.cmdline.CmdLine

What to look for:

pslist output:
PID   PPID  Name             Offset      Threads Handles SessionId
3872  644   svchost.exe      0xe0018340  12      423     0
8844  3444  svchost.exe      0xe0054a80  6       89      1  ← PPID 3444 = powershell.exe (not services.exe)

Anomaly: svchost.exe with PPID pointing to powershell.exe = injection target

Network State

# Active and recently terminated connections
vol.py -f memory.raw windows.netscan.NetScan

# Output includes: Proto, LocalAddr:Port, ForeignAddr:Port, State, PID, Owner
Proto  Local            Foreign          State    PID   Owner
TCPv4  10.1.2.33:50111  185.220.101.9:443 ESTABLISHED  8844  svchost.exe

A svchost.exe PID making outbound ESTABLISHED connections to external IPs is a finding — legitimate svchost instances don’t establish outbound TCP to external hosts.

Injection Detection

# Find executable private memory (shellcode / injected PE)
vol.py -f memory.raw windows.malfind.Malfind

# Limit to specific process for speed
vol.py -f memory.raw windows.malfind.Malfind --pid 8844
Process: svchost.exe  PID: 8844  Address: 0x1a0000
Vad Tag: VadS
Protection: PAGE_EXECUTE_READWRITE
Flags: CommitCharge: 24, PrivateMemory: 1, Protection: 6

4d 5a 90 00 03 00 00 00  MZ......  ← PE header in private memory
04 00 00 00 ff ff 00 00  ........

MZ header in a private (non-file-backed), executable region of svchost.exe = injected PE (reflective DLL or process hollowing). This is the highest-confidence injection indicator.

Module Analysis

# DLLs loaded by a process (via PEB)
vol.py -f memory.raw windows.dlllist.DllList --pid 8844

# Compare PEB lists vs VAD tree (catch reflective injection)
vol.py -f memory.raw windows.ldrmodules.LdrModules --pid 8844

ldrmodules output to look for:

Pid   Process      Base           InLoad InMem InInit MappedPath
8844  svchost.exe  0x7ffd1a000000 True   True  True   C:\Windows\System32\ntdll.dll
8844  svchost.exe  0x1a0000       False  False False   (no mapped file)  ← INJECTED

A memory region at 0x1a0000 with False/False/False in all three PEB lists but present in the VAD tree as executable = not loaded via the Windows loader = reflective injection.

Artifact Extraction

# Dump an executable from memory (use when binary deleted from disk)
vol.py -f memory.raw windows.procdump.ProcDump --pid 8844

# Extract from malfind region (dump the injected code)
vol.py -f memory.raw windows.dumpfiles.DumpFiles --pid 8844 --virtaddr 0x1a0000

# Credential hash extraction
vol.py -f memory.raw windows.hashdump.Hashdump

MemProcFS: Rapid Triage

When you don’t know which PID to target yet, MemProcFS provides instant browsable access to the full memory image:

# Mount the memory image as a filesystem
./memprocfs -device memory.raw -mount /mnt/mem

# Browse like a normal filesystem
ls /mnt/mem/proc/
# → [pid_list]

ls /mnt/mem/proc/8844_svchost.exe/
# → cmdline.txt  dlls/  files/  handles/  net/  pe/  threads/

cat /mnt/mem/proc/8844_svchost.exe/cmdline.txt
# → C:\Windows\system32\svchost.exe -k netsvcs

cat /mnt/mem/proc/8844_svchost.exe/net/netconn.txt
# → TCP 10.1.2.33:50111 → 185.220.101.9:443 ESTABLISHED

ls /mnt/mem/proc/8844_svchost.exe/dlls/
# → ntdll.dll  kernel32.dll  ... [injected.dll is NOT here — reflective injection]

MemProcFS forensic timeline (all processes combined):

cat /mnt/mem/forensic/timeline.csv | sort -t, -k1 | head -50
# TimeGenerated, Type, PID, Process, Info
# 2024-09-02 14:23:44, Net, 8844, svchost.exe, ESTABLISHED 185.220.101.9:443
# 2024-09-02 14:23:11, Proc, 3444, powershell.exe, Created
# 2024-09-02 14:21:07, Proc, 2188, cmd.exe, Created