Memory Forensics with Volatility 3: Analyzing Dumps in a Reproducible Lab
Technical memory analysis workflow with Volatility 3, sandbox-reproduced dumps and cross-validation against Rekall and MemProcFS.

In this article
Memory forensics is the discipline of reconstructing the volatile truth of a compromised system from a RAM image: running processes, open sockets, decrypted payloads, and injected code that never touches disk. Volatility 3 is the open-source standard for this, rewritten entirely in Python 3, using symbol tables instead of rigid profiles. At Basilisk we analyze every dump in a reproducible lab so a result holds up in court and in peer review. This guide runs from image acquisition through the core plugins to detecting process injection, with commands you can follow directly.
Why volatile memory matters#
Almost every modern piece of malware exists only in RAM at some point: fileless loaders, beacons injected into explorer.exe, LUKS or BitLocker volumes whose keys sit in memory. Whoever only images the disk loses the process tree, network connections to C2, clipboard, decrypted strings, and remnants of terminated processes. The order of volatility per RFC 3227 is clear: CPU registers and cache first, then RAM, then swap, only then the disk. That is why the first action in a live incident is often a clean memory capture, before anyone shuts the system down or runs a cleanup.
Acquiring the memory image#
Acquisition must preserve integrity: minimal footprint, immediate hashing, documented chain of custody. On Windows we use WinPmem or Magnet RAM Capture, on Linux AVML or LiME as a kernel module, and in virtualized environments the hypervisor's native snapshot, which is often the cleanest path. Right after the dump we compute sha256sum image.raw and record the value in the case log. An image with no hash and no timestamp is forensically worthless. For Windows analysis the matching pagefile.sys is also valuable, because paged-out pages otherwise appear as gaps.
Building a reproducible lab#
Reproducibility is the core of serious forensics. We work in a Docker container or a Python venv with a pinned Volatility 3 version, hashed symbol packs, and the image mounted as a read-only volume. Every analysis starts from the same requirements.txt so a colleague produces the exact same result. The original image stays untouched; all work runs on a verified copy whose hash matches the original. We log every command executed with a timestamp in a case runbook, so the entire chain of analysis is later reproducible step by step.
Volatility 3 and symbol tables#
Volatility 3 drops the rigid profiles of version 2 and uses Intermediate Symbol Files (ISF) generated from the kernel's debug symbols instead. For Windows the framework loads symbols automatically from the Microsoft symbol server keyed on the kernel GUID; for Linux and macOS you generate the ISF from the matching System.map and kernel headers with dwarf2json. The first run is always vol -f image.raw windows.info to confirm OS version, kernel base, and timezone. If the symbol pack is wrong, every subsequent plugin returns garbage, which is why this step is never skipped.
Analyzing processes: pslist, psscan, pstree#
The heart of the analysis is process context. windows.pslist walks the doubly linked list of active processes as the kernel maintains it. windows.psscan, by contrast, scans memory for _EPROCESS signatures and therefore also finds terminated or DKOM-hidden processes that were unlinked from the list. The difference between the two outputs is a classic rootkit indicator. windows.pstree shows the parent-child hierarchy, where anomalies stand out immediately: a cmd.exe under winword.exe, a powershell.exe under outlook.exe, or an lsass.exe with the wrong parent are all red flags.
Network and handles#
After processes come the connections. windows.netscan reconstructs TCP and UDP endpoints with destination IP, port, state, and owning process, exposing active C2 channels even when the connection was brief. windows.handles lists a process's open handles to files, registry keys, mutexes, and named pipes; a suspicious mutex is often the signature of a known malware family. windows.dlllist and windows.ldrmodules compare loaded modules: a DLL present in the memory image but absent from all three load lists strongly suggests reflective DLL loading.
Detecting injection and hollowing#
The single most important plugin for active threats is windows.malfind. It hunts memory regions combining PAGE_EXECUTE_READWRITE protection, no file backing, and executable bytes at the start, which is the classic pattern of shellcode injection and process hollowing. The output shows a hex dump; an MZ header or the telltale 4D 5A signature in an RWX region confirms an injected PE. Complementing it, windows.hollowprocesses catches the case where a legitimate process was started and its memory replaced by foreign code. Every hit is cross-checked with windows.vadinfo to name the affected VAD region precisely.
Extracting artifacts#
Once a suspicious process is confirmed, we extract evidence. windows.memmap --pid N --dump saves the process's full addressable memory; windows.dumpfiles --pid N reconstructs cached files from memory. The injected region from malfind can be isolated and passed to a sandbox or a disassembler like Ghidra. We pull registry hives with windows.registry.hivelist and windows.registry.printkey to inspect persistence keys under Run and Services. Every extracted file is hashed immediately and linked to its origin PID in the case runbook.
Common pitfalls#
The first pitfall is the wrong symbol pack, which yields plausible-looking but entirely false results. The second is ignoring the difference between pslist and psscan, leaving hidden processes undetected. The third is working on the original instead of a hashed copy, which destroys the chain of evidence. The fourth is trusting a single indicator: an RWX segment alone is not necessarily malicious, because JIT compilers also create such regions. The fifth is forgetting the pagefile.sys, which leaves paged-out evidence missing and the analysis full of gaps.
Checklist#
Acquire the image with a minimal footprint and hash it immediately with SHA-256. Work in a reproducible lab on a read-only copy. Confirm OS version and the correct symbol pack with windows.info. Triage processes with pslist, psscan, and pstree and note the differences. Check network with netscan, modules with dlllist and ldrmodules. Hunt injection with malfind and hollowprocesses. Extract suspicious regions with memmap and dumpfiles, hash them, and document them in the runbook. Support every finding with at least two independent indicators.
Timeline and correlation#
A single plugin gives a snapshot; the story of the incident emerges only from correlating several sources along a timeline. windows.pstree with creation timestamps, combined with netscan connection times and the timestamps from windows.registry.userassist, yields a defensible sequence: when the loader started, when the C2 channel opened, when persistence was set. We export these artifacts and merge them in a super-timeline tool like Plaso so disk and memory events sit in a single chronological view. Only then can you separate cause from effect and name patient zero. Time zone alignment matters: windows.info gives the bias, and every timestamp is normalized consistently to UTC, otherwise you manufacture an apparent causality that never existed. A clean timeline is the backbone of the closing report and the first thing an expert reviewer checks.
Linux and macOS memory#
Windows dominates the examples, but Volatility 3 also analyzes Linux and macOS images, provided the matching ISF exists. For Linux you generate it with dwarf2json from the kernel-specific System.map and the debug symbols of the exact running kernel, because even a patch-level difference breaks the structure. The plugin names mirror the Windows logic: linux.pslist and linux.pstree for processes, linux.bash extracts command history straight from the shell's memory, and linux.check_syscall and linux.check_modules hunt for kernel rootkits that hooked the syscall table. For containers it matters that a single host dump contains all namespaces; processes from different containers appear side by side and are separated by their cgroup membership. macOS requires a symbol pack generated from the matching KDK. The principle stays identical: verified symbols first, then systematic triage, then evidenced extraction.
Automation with vol -r and YARA rules#
Repeatable analysis demands machine-readable output. Volatility 3 supports structured renderers via vol -r json or -r csv, whose results feed into a notebook or a SIEM instead of being read by hand from terminal text. On that basis we build a script that, right after acquisition, runs the core plugins automatically, computes the difference between pslist and psscan, extracts every RWX region from malfind, and immediately runs a YARA scan with curated rules for known beacon families over it. A hit automatically raises the case to a higher priority and notifies the assigned analyst. That turns manual triage into a reproducible pipeline step whose intermediate results are hashed and archived, so any later reviewer can retrace the exact path from raw image to finding without having to blindly trust the original analyst.
FAQ: Can I analyze a memory dump without an exact OS profile?#
In Volatility 3 there are no manually chosen profiles anymore; the framework derives structure from symbol tables. For Windows this happens automatically via the kernel GUID and the Microsoft symbol server. For Linux you must generate the matching ISF yourself from System.map and kernel debug symbols with dwarf2json, otherwise the plugins fail.
FAQ: What if malfind finds nothing but suspicion remains?#
malfind is powerful but not omniscient: advanced malware can avoid RWX regions, map memory as an image, or decompress code only at runtime. Complement the analysis then with ldrmodules for unlinked DLLs, netscan for C2 traces, and a YARA scan across the whole image with windows.vadyarascan to hit known signatures.
Conclusion#
Practical takeaway: build a reproducible lab today, acquire an image of a test VM with a known beacon, and run the full chain from windows.info to malfind until you see the injected code yourself. Memory forensics is not magic, it is methodical work: clean acquisition, verified symbols, systematic process triage, multiply-supported indicators, and an unbroken chain of evidence. Whoever hashes and logs every step delivers results that hold up in an incident response report as well as a courtroom. Repeat the exercise with different injection techniques until the pattern in the hex dump jumps out immediately.


