malware analysis

Performing Memory Forensics with Volatility3 Plugins

Analyze memory dumps using Volatility3 plugins to detect injected code, rootkits, credential theft, and malware artifacts in Windows, Linux, and macOS memory images.

dfirincident-responsemalware-analysismemory-forensicsprocess-injectionrootkit-detectionvolatility3
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Volatility3 (v2.26.0+, feature parity release May 2025) is the standard framework for memory forensics, replacing the deprecated Volatility2. It analyzes RAM dumps from Windows, Linux, and macOS to detect malicious processes, code injection, rootkits, credential harvesting, and network connections that disk-based forensics cannot reveal. Key plugins include windows.malfind (detecting RWX memory regions indicating injection), windows.psscan (finding hidden processes), windows.dlllist (enumerating loaded modules), windows.netscan (active network connections), and windows.handles (open file/registry handles). The 2024 Plugin Contest introduced ETW Scan for extracting Event Tracing for Windows data from memory.

When to Use

  • When conducting security assessments that involve performing memory forensics with volatility3 plugins
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Python 3.9+ with volatility3 framework installed
  • Memory dump files (.raw, .dmp, .vmem, .lime)
  • Windows symbol tables (ISF files, auto-downloaded)
  • Understanding of Windows process memory architecture
  • YARA integration for in-memory pattern scanning

Workflow

Step 1: Process Analysis for Malware Detection

#!/usr/bin/env python3
"""Volatility3-based memory forensics automation for malware analysis."""
import subprocess
import json
import sys
import os
 
 
class Vol3Analyzer:
    """Automate Volatility3 plugin execution for malware analysis."""
 
    def __init__(self, dump_path, vol3_path="vol"):
        self.dump_path = dump_path
        self.vol3 = vol3_path
        self.results = {}
 
    def run_plugin(self, plugin, extra_args=None):
        """Execute a Volatility3 plugin and capture output."""
        cmd = [
            self.vol3, "-f", self.dump_path,
            "-r", "json", plugin,
        ]
        if extra_args:
            cmd.extend(extra_args)
 
        try:
            result = subprocess.run(
                cmd, capture_output=True, text=True, timeout=300
            )
            if result.returncode == 0:
                return json.loads(result.stdout)
        except (subprocess.TimeoutExpired, json.JSONDecodeError) as e:
            print(f"  [!] {plugin} failed: {e}")
        return None
 
    def detect_process_injection(self):
        """Use malfind to detect injected code regions."""
        print("[+] Running windows.malfind (code injection detection)")
        results = self.run_plugin("windows.malfind")
 
        injected = []
        if results:
            for entry in results:
                injected.append({
                    "pid": entry.get("PID"),
                    "process": entry.get("Process"),
                    "address": entry.get("Start VPN"),
                    "protection": entry.get("Protection"),
                    "hexdump": entry.get("Hexdump", "")[:200],
                })
                print(f"  [!] Injection in PID {entry.get('PID')} "
                      f"({entry.get('Process')}) at {entry.get('Start VPN')}")
 
        self.results["injected_processes"] = injected
        return injected
 
    def find_hidden_processes(self):
        """Compare pslist vs psscan to find hidden processes."""
        print("[+] Running process comparison (pslist vs psscan)")
 
        pslist = self.run_plugin("windows.pslist")
        psscan = self.run_plugin("windows.psscan")
 
        if not pslist or not psscan:
            return []
 
        list_pids = {e.get("PID") for e in pslist}
        scan_pids = {e.get("PID") for e in psscan}
 
        hidden = scan_pids - list_pids
        if hidden:
            print(f"  [!] {len(hidden)} hidden processes found!")
            for entry in psscan:
                if entry.get("PID") in hidden:
                    print(f"    PID {entry['PID']}: {entry.get('ImageFileName')}")
 
        self.results["hidden_processes"] = list(hidden)
        return list(hidden)
 
    def analyze_network(self):
        """Extract active network connections."""
        print("[+] Running windows.netscan")
        results = self.run_plugin("windows.netscan")
 
        connections = []
        if results:
            for entry in results:
                conn = {
                    "pid": entry.get("PID"),
                    "process": entry.get("Owner"),
                    "local": f"{entry.get('LocalAddr')}:{entry.get('LocalPort')}",
                    "remote": f"{entry.get('ForeignAddr')}:{entry.get('ForeignPort')}",
                    "state": entry.get("State"),
                    "protocol": entry.get("Proto"),
                }
                connections.append(conn)
 
        self.results["network_connections"] = connections
        return connections
 
    def extract_dlls(self, pid=None):
        """List loaded DLLs per process."""
        print(f"[+] Running windows.dlllist{f' (PID {pid})' if pid else ''}")
        args = ["--pid", str(pid)] if pid else None
        results = self.run_plugin("windows.dlllist", args)
 
        dlls = []
        if results:
            for entry in results:
                dlls.append({
                    "pid": entry.get("PID"),
                    "process": entry.get("Process"),
                    "base": entry.get("Base"),
                    "name": entry.get("Name"),
                    "path": entry.get("Path"),
                    "size": entry.get("Size"),
                })
 
        self.results["loaded_dlls"] = dlls
        return dlls
 
    def scan_with_yara(self, rules_path):
        """Scan memory with YARA rules."""
        print(f"[+] Running windows.yarascan with {rules_path}")
        results = self.run_plugin(
            "windows.yarascan",
            ["--yara-file", rules_path]
        )
 
        matches = []
        if results:
            for entry in results:
                matches.append({
                    "rule": entry.get("Rule"),
                    "pid": entry.get("PID"),
                    "process": entry.get("Process"),
                    "offset": entry.get("Offset"),
                })
 
        self.results["yara_matches"] = matches
        return matches
 
    def full_triage(self):
        """Run full malware-focused memory triage."""
        print(f"[*] Full memory triage: {self.dump_path}")
        print("=" * 60)
 
        self.detect_process_injection()
        self.find_hidden_processes()
        self.analyze_network()
 
        return self.results
 
 
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <memory_dump>")
        sys.exit(1)
 
    analyzer = Vol3Analyzer(sys.argv[1])
    results = analyzer.full_triage()
    print(json.dumps(results, indent=2, default=str))

Validation Criteria

  • Memory dump successfully parsed with correct OS profile
  • Injected processes detected via malfind with RWX regions
  • Hidden processes identified through pslist/psscan comparison
  • Network connections reveal C2 communication endpoints
  • YARA rules match known malware signatures in memory
  • Credential artifacts extracted from lsass process memory

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md1.8 KB

API Reference — Performing Memory Forensics with Volatility3 Plugins

Libraries Used

  • subprocess: Execute Volatility3 CLI with JSON output
  • json: Parse Volatility3 JSON results

CLI Interface

python agent.py plugin --dump memory.raw --name pslist [--args --pid 1234]
python agent.py malproc --dump memory.raw
python agent.py inject --dump memory.raw
python agent.py network --dump memory.raw
python agent.py triage --dump memory.raw

Core Functions

run_vol3_plugin(memory_dump, plugin_name, extra_args) — Execute any Vol3 plugin

Supports 18 built-in plugins with JSON output parsing.

detect_malicious_processes(memory_dump) — Suspicious process detection

Checks pslist against 15 known attack tools (mimikatz, cobalt, rubeus, etc.). Flags cmd.exe and PowerShell execution.

detect_injected_code(memory_dump) — Code injection via malfind

Identifies memory regions with executable, non-image-backed pages.

analyze_network_connections(memory_dump) — Network artifact extraction

Extracts connections via netscan. Filters external (non-RFC1918) connections.

full_triage(memory_dump) — Combined analysis

Runs processes + injection + network analysis in single report.

Supported Volatility3 Plugins

Plugin Class Purpose
pslist windows.pslist.PsList Process listing
psscan windows.psscan.PsScan Hidden process scan
malfind windows.malfind.Malfind Code injection detection
netscan windows.netscan.NetScan Network connections
cmdline windows.cmdline.CmdLine Process command lines
dlllist windows.dlllist.DllList Loaded DLLs
hashdump windows.hashdump.Hashdump Password hash extraction
svcscan windows.svcscan.SvcScan Windows services

Dependencies

pip install volatility3
standards.md1.1 KB

Volatility3 Memory Forensics Standards

Key Plugins for Malware Analysis

Plugin Purpose
windows.malfind Detect injected code (RWX regions)
windows.psscan Find hidden/unlinked processes
windows.pslist List active processes from EPROCESS
windows.netscan Network connections and listeners
windows.dlllist Loaded DLLs per process
windows.handles Open handles (files, registry, mutexes)
windows.cmdline Command line arguments
windows.svcscan Windows services
windows.yarascan YARA rule scanning in memory
windows.registry.hivelist Registry hives in memory
windows.hashdump Extract password hashes

Memory Acquisition Formats

Format Tool Extension
Raw WinPmem, FTK Imager .raw, .bin
Crash dump Windows .dmp
VMware VMware .vmem
LiME LiME .lime
Hibernation Windows hiberfil.sys

References

workflows.md0.7 KB

Memory Forensics Workflows

Workflow 1: Malware Triage

[Memory Dump] --> [pslist/psscan] --> [malfind] --> [dlllist] --> [netscan]
                                          |
                                          v
                                  [Dump Injected Code] --> [YARA Scan]

Workflow 2: Rootkit Detection

[Memory Dump] --> [pslist vs psscan] --> [Hidden Processes]
                                               |
                                               v
                                      [SSDT Hook Detection]
                                               |
                                               v
                                      [Inline Hook Analysis]

Scripts 2

agent.py7.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for performing memory forensics with Volatility3 plugins."""

import json
import argparse
import subprocess
from datetime import datetime


VOL3_PLUGINS = {
    "pslist": "windows.pslist.PsList",
    "pstree": "windows.pstree.PsTree",
    "psscan": "windows.psscan.PsScan",
    "dlllist": "windows.dlllist.DllList",
    "handles": "windows.handles.Handles",
    "netscan": "windows.netscan.NetScan",
    "netstat": "windows.netstat.NetStat",
    "malfind": "windows.malfind.Malfind",
    "cmdline": "windows.cmdline.CmdLine",
    "filescan": "windows.filescan.FileScan",
    "hivelist": "windows.registry.hivelist.HiveList",
    "hashdump": "windows.hashdump.Hashdump",
    "lsadump": "windows.lsadump.Lsadump",
    "svcscan": "windows.svcscan.SvcScan",
    "ssdt": "windows.ssdt.SSDT",
    "callbacks": "windows.callbacks.Callbacks",
    "vadinfo": "windows.vadinfo.VadInfo",
    "envars": "windows.envars.Envars",
}


def run_vol3_plugin(memory_dump, plugin_name, extra_args=None):
    """Execute a Volatility3 plugin against a memory dump."""
    plugin_class = VOL3_PLUGINS.get(plugin_name, plugin_name)
    cmd = ["vol", "-f", memory_dump, "-r", "json", plugin_class]
    if extra_args:
        cmd += extra_args
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
        if result.returncode != 0:
            return {"error": result.stderr[:500], "plugin": plugin_name}
        data = json.loads(result.stdout)
        return {"plugin": plugin_name, "memory_dump": memory_dump, "results": data, "count": len(data) if isinstance(data, list) else 1}
    except FileNotFoundError:
        return {"error": "Volatility3 (vol) not found — pip install volatility3"}
    except json.JSONDecodeError:
        return {"plugin": plugin_name, "raw_output": result.stdout[:2000]}
    except subprocess.TimeoutExpired:
        return {"error": f"Plugin {plugin_name} timed out after 300s"}


def detect_malicious_processes(memory_dump):
    """Run process analysis plugins to detect suspicious processes."""
    suspicious_names = ["mimikatz", "procdump", "psexec", "cobalt", "beacon",
                        "meterpreter", "nc.exe", "ncat", "powercat", "lazagne",
                        "bloodhound", "rubeus", "certify", "seatbelt", "sharphound"]
    pslist = run_vol3_plugin(memory_dump, "pslist")
    cmdline = run_vol3_plugin(memory_dump, "cmdline")
    suspicious = []
    if isinstance(pslist.get("results"), list):
        for proc in pslist["results"]:
            name = str(proc.get("ImageFileName", proc.get("Name", ""))).lower()
            pid = proc.get("PID", proc.get("pid", ""))
            ppid = proc.get("PPID", proc.get("ppid", ""))
            if any(s in name for s in suspicious_names):
                suspicious.append({"pid": pid, "name": name, "ppid": ppid, "reason": "KNOWN_ATTACK_TOOL"})
            if name == "cmd.exe" and str(ppid) not in ("0", "1"):
                suspicious.append({"pid": pid, "name": name, "ppid": ppid, "reason": "CMD_SPAWNED"})
            if name in ("powershell.exe", "pwsh.exe"):
                suspicious.append({"pid": pid, "name": name, "ppid": ppid, "reason": "POWERSHELL_EXECUTION"})
    return {
        "memory_dump": memory_dump,
        "total_processes": len(pslist.get("results", [])) if isinstance(pslist.get("results"), list) else 0,
        "suspicious_processes": suspicious,
        "timestamp": datetime.utcnow().isoformat(),
    }


def detect_injected_code(memory_dump):
    """Run malfind to detect code injection."""
    malfind = run_vol3_plugin(memory_dump, "malfind")
    findings = []
    if isinstance(malfind.get("results"), list):
        for entry in malfind["results"]:
            findings.append({
                "pid": entry.get("PID", entry.get("pid")),
                "process": entry.get("Process", entry.get("process", "")),
                "address": entry.get("Start VPN", entry.get("start", "")),
                "protection": entry.get("Protection", entry.get("protection", "")),
                "tag": entry.get("Tag", ""),
            })
    return {
        "memory_dump": memory_dump,
        "injections_found": len(findings),
        "findings": findings[:30],
        "severity": "HIGH" if findings else "INFO",
    }


def analyze_network_connections(memory_dump):
    """Extract network connections from memory."""
    netscan = run_vol3_plugin(memory_dump, "netscan")
    connections = []
    if isinstance(netscan.get("results"), list):
        for conn in netscan["results"]:
            connections.append({
                "pid": conn.get("PID", conn.get("pid")),
                "process": conn.get("Owner", conn.get("process", "")),
                "local_addr": conn.get("LocalAddr", conn.get("local_addr", "")),
                "local_port": conn.get("LocalPort", conn.get("local_port", "")),
                "remote_addr": conn.get("ForeignAddr", conn.get("remote_addr", "")),
                "remote_port": conn.get("ForeignPort", conn.get("remote_port", "")),
                "state": conn.get("State", conn.get("state", "")),
            })
    external = [c for c in connections if c.get("remote_addr") and not c["remote_addr"].startswith(("0.0", "127.", "10.", "192.168.", "172."))]
    return {
        "total_connections": len(connections),
        "external_connections": len(external),
        "connections": connections[:30],
        "external_only": external[:20],
    }


def full_triage(memory_dump):
    """Run full memory triage with key plugins."""
    return {
        "memory_dump": memory_dump,
        "timestamp": datetime.utcnow().isoformat(),
        "processes": detect_malicious_processes(memory_dump),
        "injections": detect_injected_code(memory_dump),
        "network": analyze_network_connections(memory_dump),
    }


def main():
    parser = argparse.ArgumentParser(description="Volatility3 Memory Forensics Agent")
    sub = parser.add_subparsers(dest="command")
    p = sub.add_parser("plugin", help="Run specific Volatility3 plugin")
    p.add_argument("--dump", required=True, help="Memory dump file path")
    p.add_argument("--name", required=True, help="Plugin name or class", choices=list(VOL3_PLUGINS.keys()))
    p.add_argument("--args", nargs="*", help="Extra plugin arguments")
    m = sub.add_parser("malproc", help="Detect malicious processes")
    m.add_argument("--dump", required=True)
    i = sub.add_parser("inject", help="Detect code injection")
    i.add_argument("--dump", required=True)
    n = sub.add_parser("network", help="Analyze network connections")
    n.add_argument("--dump", required=True)
    t = sub.add_parser("triage", help="Full memory triage")
    t.add_argument("--dump", required=True)
    args = parser.parse_args()
    if args.command == "plugin":
        result = run_vol3_plugin(args.dump, args.name, args.args)
    elif args.command == "malproc":
        result = detect_malicious_processes(args.dump)
    elif args.command == "inject":
        result = detect_injected_code(args.dump)
    elif args.command == "network":
        result = analyze_network_connections(args.dump)
    elif args.command == "triage":
        result = full_triage(args.dump)
    else:
        parser.print_help()
        return
    print(json.dumps(result, indent=2, default=str))


if __name__ == "__main__":
    main()
process.py1.8 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Memory Forensics Automation with Volatility3

Requirements:
    pip install volatility3

Usage:
    python process.py --dump memory.raw --triage
    python process.py --dump memory.raw --plugin windows.malfind
"""

import argparse
import json
import subprocess
import sys


def run_vol3(dump_path, plugin, extra_args=None, vol3_cmd="vol"):
    cmd = [vol3_cmd, "-f", dump_path, "-r", "json", plugin]
    if extra_args:
        cmd.extend(extra_args)
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
        if result.returncode == 0 and result.stdout.strip():
            return json.loads(result.stdout)
    except Exception as e:
        print(f"[-] {plugin}: {e}")
    return None


def triage(dump_path):
    plugins = [
        "windows.pslist",
        "windows.psscan",
        "windows.malfind",
        "windows.netscan",
        "windows.cmdline",
    ]
    report = {}
    for plugin in plugins:
        print(f"[+] Running {plugin}...")
        report[plugin] = run_vol3(dump_path, plugin)
    return report


def main():
    parser = argparse.ArgumentParser(description="Volatility3 Automation")
    parser.add_argument("--dump", required=True, help="Memory dump file")
    parser.add_argument("--triage", action="store_true")
    parser.add_argument("--plugin", help="Specific plugin to run")
    parser.add_argument("--output", help="Output JSON file")

    args = parser.parse_args()

    if args.triage:
        report = triage(args.dump)
    elif args.plugin:
        report = {args.plugin: run_vol3(args.dump, args.plugin)}
    else:
        parser.print_help()
        return

    print(json.dumps(report, indent=2, default=str))
    if args.output:
        with open(args.output, 'w') as f:
            json.dump(report, f, indent=2, default=str)


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 0.6 KB
Keep exploring