Chapter 20

Volatility 3 Fundamentals

Volatility 3 is the primary open-source memory analysis framework. This chapter covers installation, symbol setup, the core plugin workflow, and the most important plugins for incident response — from process listing to network connections to registry extraction from memory.

Scenario

You have a 32 GB RAM image from a Windows 2022 server confirmed compromised with a Cobalt Strike beacon. Volatility 3 will let you answer the key questions: What processes are running and which have suspicious parent relationships? Are there any injected code sections in legitimate processes? What network connections are active? What credentials are cached in LSASS? What registry keys are loaded? This chapter walks through the analysis workflow from first command to finished findings.

Installation and Setup

Bashvolatility3-setup.sh
# Volatility 3 installation
pip3 install volatility3
# Or from source for latest:
git clone https://github.com/volatilityfoundation/volatility3
cd volatility3 && pip3 install -e .

# Verify installation
vol -h | head -5

# Symbol tables are required for Windows analysis
# Download pre-built symbol packs from Volatility Foundation:
# https://downloads.volatilityfoundation.org/volatility3/symbols/

# Place symbols in volatility3/symbols/ or configure path
# Windows symbols automatically downloaded on first use (internet needed)
# For airgapped lab: manually download and place:
#   windows.zip → volatility3/symbols/windows/
#   mac.zip     → volatility3/symbols/mac/

# Test against your image
vol -f /cases/CASE001/RAM.raw windows.info

Core Plugin Reference

PluginWhat it doesCommand
windows.pslistList all processes from the EPROCESS listvol -f image.raw windows.pslist
windows.pstreeProcess list with parent-child hierarchyvol -f image.raw windows.pstree
windows.psscanScan memory for EPROCESS structures (finds hidden processes)vol -f image.raw windows.psscan
windows.cmdlineCommand line arguments for each processvol -f image.raw windows.cmdline
windows.dlllistDLLs loaded by each processvol -f image.raw windows.dlllist --pid 1234
windows.malfindFind memory regions with executable code not backed by a file on diskvol -f image.raw windows.malfind
windows.netscanNetwork connections (active + recently closed)vol -f image.raw windows.netscan
windows.netstatActive TCP connections (Windows 10+)vol -f image.raw windows.netstat
windows.hashdumpExtract NTLM hashes from SAM (requires SYSTEM key)vol -f image.raw windows.hashdump
windows.lsadumpExtract LSA secretsvol -f image.raw windows.lsadump
windows.registry.hivelistList loaded registry hivesvol -f image.raw windows.registry.hivelist
windows.registry.printkeyPrint a registry key from memoryvol -f image.raw windows.registry.printkey --key "Software\Microsoft\Windows\CurrentVersion\Run"
windows.handlesOpen handles for a processvol -f image.raw windows.handles --pid 1234
windows.filescanFind FILE_OBJECT structures — files open or recently accessedvol -f image.raw windows.filescan
windows.dumpfilesExtract file content from memory cachevol -f image.raw windows.dumpfiles --physaddr 0x...

Process Analysis Workflow

Bashvol3-process-analysis.sh
RAM="/cases/CASE001/RAM.raw"
OUT="/cases/CASE001/memory"
mkdir -p $OUT

# Step 1: Process tree — visual parent-child relationships
vol -f $RAM windows.pstree --output csv > $OUT/pstree.csv

# Step 2: Process scan (catches DKOM-hidden processes)
# Compare pslist vs psscan — processes in psscan but not pslist = hidden
vol -f $RAM windows.psscan --output csv > $OUT/psscan.csv
vol -f $RAM windows.pslist --output csv > $OUT/pslist.csv

# Step 3: Command lines — what arguments did each process receive?
vol -f $RAM windows.cmdline --output csv > $OUT/cmdlines.csv

# Step 4: Network connections
vol -f $RAM windows.netscan --output csv > $OUT/netscan.csv

# Step 5: Run malfind to locate injected code
vol -f $RAM windows.malfind --output csv > $OUT/malfind.csv

echo "Core analysis complete. Check $OUT/"
Pythonanalyze-pstree.py
"""
Analyze Volatility pstree output for suspicious parent-child relationships.
Flags processes with unexpected parents.
"""
import csv

# Known-normal parent-child relationships
EXPECTED_PARENTS = {
    "lsass.exe":       ["wininit.exe"],
    "services.exe":    ["wininit.exe"],
    "svchost.exe":     ["services.exe"],
    "explorer.exe":    ["userinit.exe"],
    "cmd.exe":         ["explorer.exe", "cmd.exe", "conhost.exe"],
    "powershell.exe":  ["explorer.exe", "cmd.exe", "svchost.exe"],
    "werfault.exe":    ["svchost.exe", "wininit.exe"],
    "taskhost.exe":    ["services.exe"],
    "spoolsv.exe":     ["services.exe"],
}

# Flag suspicious: process with unexpected parent
SUSPICIOUS_PARENTS = {
    "lsass.exe":       ["any process except wininit.exe"],
    "services.exe":    ["any process except wininit.exe"],
    "svchost.exe":     ["anything not services.exe"],
    "winlogon.exe":    ["anything not smss.exe"],
    "powershell.exe":  ["office apps", "browser processes"],
}

with open("/cases/CASE001/memory/pstree.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        proc = row.get("ImageFileName", "").lower()
        parent = row.get("__children", "")  # Volatility pstree output
        pid = row.get("PID", "")
        ppid = row.get("PPID", "")

        # Flag: processes with 0 or 4 as parent (SYSTEM spawning unexpected children)
        if ppid in ("0", "4") and proc not in (
            "system", "idle", "smss.exe", "csrss.exe", "wininit.exe", "winlogon.exe"
        ):
            print(f"[SUSPICIOUS] PID {pid} {proc} has parent PID {ppid} (SYSTEM)")

        # Flag: svchost not spawned by services.exe
        # (require correlation with pslist PPID lookup)
        print(f"PID {pid}: {proc} (parent: {ppid})")

Understanding malfind Output

malfind identifies memory regions that are executable and writable, with MZ/PE headers or shellcode signatures, that aren't backed by a file on disk — the hallmark of code injection.

  How to Interpret malfind Results
  ═══════════════════════════════════════════════════════════════════

  malfind output columns:
    Process name  — which process has the suspicious region
    PID           — process ID
    Start VPN     — virtual address of the suspicious region
    End VPN       — end address
    Tag           — memory region type (VadS = private, anonymous)
    Protection    — PAGE_EXECUTE_READWRITE (RWX) is classic injection
    CommitCharge  — pages committed
    PrivateMemory — True = not backed by file (key indicator)
    File output   — optionally dump the region to a file

  Classic injection signatures:
    PAGE_EXECUTE_READWRITE (RWX) + PrivateMemory=True + MZ header
    → Almost certainly injected PE (DLL or EXE)

    PAGE_EXECUTE_READWRITE + PrivateMemory=True + x86 shellcode
    → Shellcode injection (no PE header — raw shellcode)

  Not all malfind hits are malicious:
    .NET JIT compiled code appears as RWX private
    Some legitimate apps use JIT or dynamically generated code
    → Triage by process: svchost.exe with malfind = high suspicion
                         node.exe with malfind = possibly normal (V8 JIT)

  Extract the suspicious region for deeper analysis:
    vol -f RAM.raw windows.malfind --pid 1234 --dump
    → Saves .dmp files that can be analyzed with YARA or Ghidra

Q & A

Q: pslist and psscan disagree — a process appears in psscan but not pslist. What does that mean and what do you do?

A process in psscan but not pslist has been "unlinked" from the EPROCESS doubly-linked list — a rootkit or advanced implant technique called Direct Kernel Object Manipulation (DKOM). pslist walks the linked list; psscan scans raw memory for EPROCESS structures regardless of list membership. The hidden process almost certainly represents malware or an attacker tool that has patched kernel memory to hide itself. Actions: (1) Note the PID and all associated information from the psscan output. (2) Run windows.cmdline --pid [PID] to get the command line if possible. (3) Run windows.dlllist --pid [PID] to see loaded DLLs. (4) Run windows.handles --pid [PID] to see what files/sockets the process has open. (5) Run windows.dumpfiles or process dump to extract the binary for analysis. (6) Escalate severity immediately — DKOM-capable malware is sophisticated and suggests a more capable adversary than commodity malware.