Memory forensics is one of the most powerful techniques a DFIR analyst has. RAM holds runtime state that never touches the disk: running and recently exited processes, command lines, network connections, decrypted malware, injected shellcode, clipboard contents, typed console commands, registry hives in use, and sometimes passwords and encryption keys.
This guide covers both major versions of Volatility. Every command below is a real command for the current releases: Volatility 3 v2.28.x and the final Volatility 2.6.1 codebase. For each investigation step you get the Volatility 3 command first, then the Volatility 2 equivalent.
Use the interactive path builder below when you know the question you want to answer but do not yet know which plugin to run. It gives you a safe first command, the legacy equivalent where it is useful, and the next piece of evidence to correlate.
What lives in RAM
| Artifact | Why it matters | Volatility 3 plugin |
|---|---|---|
| Process list and tree | What was running, who started it, when | windows.pslist, windows.pstree |
| Exited or unlinked processes | Short-lived malware, rootkit-hidden processes | windows.psscan, windows.malware.psxview |
| Command lines | Exact arguments, encoded PowerShell, LOLBins | windows.cmdline |
| Console history | Commands typed in cmd.exe |
windows.consoles, windows.cmdscan |
| Network sockets | C2 servers, listeners, lateral movement | windows.netscan, windows.netstat |
| Injected code | Shellcode, reflective DLLs, hollowed processes | windows.malware.malfind, windows.malware.hollowprocesses |
| Cached files | Documents, scripts, and binaries opened by the user | windows.filescan, windows.dumpfiles |
| Registry hives | Persistence, user activity, system configuration | windows.registry.* |
| Credentials | NTLM hashes, LSA secrets, cached domain logons | windows.registry.hashdump, windows.registry.lsadump |
Volatility 2 vs Volatility 3
Volatility 2 was the standard for about ten years. It is written in Python 2.7, which reached end of life in 2020. Volatility 3 is a complete rewrite in Python 3, first released in 2019. In 2025 the Volatility Foundation published v2.26.0 as the official "parity release": Volatility 3 now covers what Volatility 2 did, Volatility 2 is officially deprecated, and its GitHub repository is archived. New features and bug fixes only go into Volatility 3.
Key differences
| Volatility 2 | Volatility 3 | |
|---|---|---|
| Language | Python 2.7 (end of life) | Python 3.8+ |
| Status | Archived, no more updates | Actively developed (v2.28.x in 2026) |
| Identifying the OS | You choose a profile yourself (--profile=Win7SP1x64), usually with help from imageinfo or kdbgscan |
Automatic. It finds the kernel and loads the right symbol table |
| Windows support | Profiles built into the tool: XP to Windows 10 build 19041 and Server 2016. No Windows 11 | Symbols downloaded from Microsoft's symbol server for any build, including Windows 11 and Server 2022/2025 |
| Linux and macOS | You build a profile zip (module.dwarf + System.map) |
You build an ISF JSON file with dwarf2json, or download a prebuilt one |
| Plugin names | Flat: pslist, malfind, hashdump |
Grouped by OS and area: windows.pslist, windows.malware.malfind, windows.registry.hashdump, linux.bash |
| Dumping files | -D/--dump-dir on each plugin |
Global -o <dir> plus a --dump flag on the plugin |
| Output formats | --output=text/csv/json/html/sqlite/dot (not every plugin supports every format) |
-r quick/pretty/csv/json/jsonl for every plugin, plus --filters and --hide-columns |
| Speed | imageinfo and KDBG scanning are slow on large images |
Symbol and layer caching make later runs much faster |
| Community plugins | Very large collection (mimikatz, chromehistory, firefoxhistory, usnparser, autoruns...) |
Smaller but growing, plus many newer detection plugins |
When to use each one
Use Volatility 3 by default. In particular:
- Windows 11 and Server 2022/2025. Volatility 2 has no profiles for them, so it will fail or give wrong results. (Windows 10 20H2 to 22H2 all use the 19041 kernel, which Volatility 2's last profile covers.)
- Any modern Linux kernel.
- When you want CSV or JSON output for scripts, SIEM ingestion, or timelines.
- When you need the newer detection plugins:
hollowprocesses,processghosting,pebmasquerade,suspicious_threads,unhooked_system_calls,direct_system_calls,svcdiff,etwpatch. - When you write your own plugins. Only the Volatility 3 API is maintained.
Use Volatility 2 when:
- The image is Windows XP, Vista, 7, or Server 2003/2008 and you are following training material built around Volatility 2. MemLabs, many older CyberDefenders labs, and older TryHackMe rooms expect Volatility 2 output. Volatility 3 also works on these images, but the tutorials assume Volatility 2 plugin names.
- You need a plugin that only exists for Volatility 2:
screenshot,clipboard,editbox,notepad(XP only),iehistory,shellbags,mftparser, the communitymimikatz, orchromehistory/firefoxhistoryfrom superponible/volatility-plugins. - You want a second opinion. If one version returns empty or strange output, running the other version on the same image quickly tells you whether the problem is the image or the tool.
Rule of thumb: start with Volatility 3. Use Volatility 2 only when you need an old OS plugin that Volatility 3 does not have, or to check a result.
Getting a memory image
If you are given a CTF dump you can skip this section. In a real incident, capture memory first, before you pull the plug or run antivirus tools, because RAM is lost on shutdown.
| Source | Tool | Command / method |
|---|---|---|
| Windows (live) | WinPmem | winpmem_mini_x64.exe C:\evidence\mem.raw (run from an elevated prompt) |
| Windows (live) | FTK Imager, Magnet RAM Capture, DumpIt | GUI: File → Capture Memory in FTK Imager |
| Linux (live) | AVML | sudo avml acquire /evidence/mem.lime (older releases: sudo ./avml /evidence/mem.lime) |
| Linux (live) | LiME | sudo insmod lime-$(uname -r).ko "path=/evidence/mem.lime format=lime" |
| VMware VM | Suspend or snapshot | Copy the .vmem together with its .vmss/.vmsn file |
| VirtualBox VM | VBoxManage | vboxmanage debugvm "VM name" dumpvmcore --filename mem.elf |
| Offline disk | Hibernation file | C:\hiberfil.sys holds compressed RAM from the last hibernation |
Always hash the image right after acquisition and work on a copy:
sha256sum mem.raw | tee mem.raw.sha256
Installing Volatility 3
Volatility 3 is on PyPI. Use a virtual environment so its dependencies stay separate from your system Python.
Linux / macOS
python3 -m venv ~/venvs/vol3
source ~/venvs/vol3/bin/activate
pip install "volatility3[full]" # [full] adds yara-python, pycryptodome, capstone, pillow
vol -h | head -n 1 # prints "Volatility 3 Framework 2.x.x"
Windows (PowerShell)
py -m venv $HOME\venvs\vol3
& $HOME\venvs\vol3\Scripts\Activate.ps1
pip install "volatility3[full]"
vol -h
Latest development version from source
git clone https://github.com/volatilityfoundation/volatility3.git
cd volatility3
python3 -m venv venv && . venv/bin/activate
pip install -e ".[full]"
The pip install gives you the vol and volshell commands. From a source checkout, python3 vol.py works too; it is the same program.
Symbol tables
Volatility 3 needs a symbol table (an ISF JSON file) for the exact kernel in the image.
- Windows: handled automatically. On the first run, Volatility reads the kernel's PDB GUID from the image, downloads the matching PDB from Microsoft, converts it, and caches it (
~/.cache/volatility3on Linux). This requires internet access. For offline machines, download the windows.zip symbol pack and place it, still zipped, involatility3/symbols/or in a directory you pass with-s. - Linux and macOS: you must provide the ISF yourself. See the Linux section.
The first run against a new image can take a few minutes while symbols download and the cache builds. Later runs are fast.
Installing Volatility 2 (legacy)
Option 1: standalone binary (easiest). No Python needed. It is version 2.6 (2016), so its profiles stop at Windows 10 build 14393 / Server 2016.
wget http://downloads.volatilityfoundation.org/releases/2.6/volatility_2.6_lin64_standalone.zip
unzip volatility_2.6_lin64_standalone.zip
cd volatility_2.6_lin64_standalone
./volatility_2.6_lin64_standalone -h
For Windows, use volatility_2.6_win64_standalone.zip from the same URL.
Option 2: from source (profiles up to Windows 10 build 19041). You need Python 2.7. Ubuntu 22.04 and Debian 11 still ship it. Newer distributions (Ubuntu 24.04, Debian 12, current Kali) do not, so run it inside an ubuntu:22.04 container: docker run --rm -it -v "$PWD":/case ubuntu:22.04 bash.
sudo apt install -y python2 python2-dev build-essential git curl
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
sudo python2 get-pip.py
python2 -m pip install distorm3==3.4.4 pycrypto # distorm3 >= 3.5 breaks Volatility 2
git clone https://github.com/volatilityfoundation/volatility.git
cd volatility
python2 vol.py -h
In this guide, vol.py means python2 vol.py (or the standalone binary).
Volatility 3 basics
The command shape is:
vol [global options] -f <image> <plugin> [plugin options]
Global options you will use all the time:
| Option | Purpose |
|---|---|
-f mem.raw |
Memory image to analyse |
-o out/ |
Directory where --dump and other output files are written (must exist) |
-r csv / -r json / -r pretty |
Output renderer. quick (the default) is fastest for reading in a terminal |
-q |
Hide the progress bar. Useful when redirecting output to a file |
-s ~/symbols |
Extra symbol directory (offline packs, your own Linux ISFs) |
--offline |
Never try to download symbols |
-p ~/vol3-plugins |
Extra plugin directory (community plugins) |
-vvv |
Verbose debug logging. Use it when a plugin fails and you need to know why |
--filters "Column,text" |
Keep only rows where the column contains text. Prefix with - to exclude. Add ! at the end to treat it as a regex |
vol -h # list every plugin
vol windows.pslist -h # options for a single plugin
vol -f mem.raw windows.info # first command on every Windows image
windows.info tells you whether Volatility can read the image. Check these fields:
NtMajorVersion/NtMinorVersionandNtBuildLab: exact Windows build (for example19041.1.amd64fre.vb_release...)Is64Bit: architectureSystemTime: when the memory was captured. Every other timestamp is relative to thisNtSystemRoot: usuallyC:\Windows
Plugin names can be shortened as long as they stay unique. windows.pslist is short for windows.pslist.PsList. When a module contains several plugins, write the full name, for example windows.mftscan.MFTScan or timeliner.Timeliner.
Renamed plugins: since v2.26 several plugins moved.
windows.malfindis nowwindows.malware.malfind,windows.hashdumpis nowwindows.registry.hashdump, and so on. The old names still run with a deprecation warning, but they have passed their removal dates and will disappear. Older tutorials still use the old names.
Volatility 2 basics
Volatility 2 needs a profile that matches the OS build of the image.
vol.py -f mem.raw imageinfo # suggests profiles (slow on big images)
vol.py -f mem.raw kdbgscan # more reliable; also prints the KDBG address
vol.py --info | grep -i win10 # list every profile this install knows
vol.py -f mem.raw --profile=Win7SP1x64 pslist
Tips that save a lot of time:
- Pass the KDBG address from
kdbgscanso Volatility does not scan for it every time:--kdbg=0xf80002c4b0a0. On Windows 8 and later, use theKdCopyDataBlockvalue thatkdbgscanprints. - Set environment variables once per shell so you don't retype
-fand--profile:
export VOLATILITY_LOCATION=file://$PWD/mem.raw
export VOLATILITY_PROFILE=Win7SP1x64
vol.py pslist
imageinfooften suggests several Windows 10 profiles. Run Volatility 3'swindows.infoand take the build number fromNtBuildLab. For example, 19041 means--profile=Win10x64_19041.- Plugins that write files take
-D out/(--dump-dir). - Community plugins are loaded with
--plugins=<dir>, which must be the first argument:vol.py --plugins=/opt/volatility-plugins -f mem.raw --profile=Win7SP1x64 chromehistory.
Windows investigation workflow
The steps below follow the order of a real investigation, from broad (what was running) to specific (credentials, timelines). In each code block, the Volatility 3 commands come first and the Volatility 2 equivalents second. The examples use mem.raw as the image, Win7SP1x64 as the Volatility 2 profile, and 4312 as the suspicious PID. Replace them with your own.
Step 1: Processes
Start by understanding what was running.
# Volatility 3
vol -f mem.raw windows.pslist # walks the active process list
vol -f mem.raw windows.pstree # parent/child tree
vol -f mem.raw windows.psscan # pool-tag scan: finds exited and unlinked processes
vol -f mem.raw windows.malware.psxview # compares several sources side by side
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 pslist
vol.py -f mem.raw --profile=Win7SP1x64 pstree
vol.py -f mem.raw --profile=Win7SP1x64 psscan
vol.py -f mem.raw --profile=Win7SP1x64 psxview
Why several plugins? pslist follows the kernel's doubly linked list of _EPROCESS structures. A rootkit can unlink a process from that list using DKOM (Direct Kernel Object Manipulation), and the process disappears from pslist and from Task Manager. psscan ignores the list and scans memory for process structures directly. This finds unlinked processes and processes that have already exited. psxview shows each process with a True/False column for every source. A False in pslist with True in psscan is worth investigating. Processes that simply exited also show this pattern, so check the ExitTime column.
What normal looks like
| Process | Normal parent | Instances | Red flags |
|---|---|---|---|
System (PID 4) |
none | 1 | — |
smss.exe |
System |
1 (plus short-lived children) | Running from anywhere other than System32 |
csrss.exe |
smss.exe (exited, so the parent looks missing) |
1 per session | Visible parent, wrong path |
wininit.exe |
smss.exe (exited) |
1 | More than one |
winlogon.exe |
smss.exe (exited) |
1 per interactive session | Unusual children |
services.exe |
wininit.exe |
1 | Any other parent |
lsass.exe |
wininit.exe |
Exactly 1 | Any children, a second copy, a misspelling (lsas.exe, lsasss.exe) |
svchost.exe |
services.exe |
Many | Parent other than services.exe, no -k argument, misspellings (svch0st.exe, scvhost.exe) |
explorer.exe |
userinit.exe (exited) |
1 per logged-on user | Spawning processes from %TEMP% or AppData |
Other patterns to look for: Office applications (WINWORD.EXE, EXCEL.EXE, OUTLOOK.EXE) starting cmd.exe, powershell.exe, mshta.exe, wscript.exe, or rundll32.exe; processes started seconds before SystemTime; and a known process name with a creation time far later than the other system processes.
Filtering examples:
vol -f mem.raw windows.pstree --filters "ImageFileName,powershell"
vol -q -f mem.raw -r csv windows.pslist > pslist.csv # open in a spreadsheet or Timeline Explorer
Step 2: Command lines and console history
# Volatility 3
vol -f mem.raw windows.cmdline
vol -f mem.raw windows.cmdline --pid 4312
vol -f mem.raw windows.consoles # console input history and screen buffer (conhost.exe)
vol -f mem.raw windows.cmdscan # command history structures only
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 cmdline
vol.py -f mem.raw --profile=Win7SP1x64 cmdline -p 4312
vol.py -f mem.raw --profile=Win7SP1x64 consoles
vol.py -f mem.raw --profile=Win7SP1x64 cmdscan
consoles is often the most valuable plugin in CTFs because it shows what the attacker or user typed and what was printed in the console window.
Look for:
powershell -enc/-EncodedCommand,-nop -w hidden,IEX (New-Object Net.WebClient).DownloadString(...)- LOLBins:
certutil -urlcache -f,bitsadmin /transfer,mshta http://...,regsvr32 /s /i:http://... scrobj.dll,rundll32 <dll>,<export> - Executables in
C:\Users\<user>\AppData\,C:\ProgramData\,C:\Windows\Temp\, orC:\Users\Public\
PowerShell -enc payloads are Base64 of UTF-16LE text. Decode them like this:
echo 'SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkA' \
| base64 -d | iconv -f UTF-16LE -t UTF-8
# IEX (New-Object Net.WebClient)
Step 3: Users, environment, and privileges
# Volatility 3
vol -f mem.raw windows.envars --pid 4312 # USERNAME, COMPUTERNAME, TEMP, custom variables
vol -f mem.raw windows.getsids --pid 4312 # which account owns the process
vol -f mem.raw windows.privileges --pid 4312 # e.g. SeDebugPrivilege enabled
vol -f mem.raw windows.sessions # logon sessions and their processes
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 envars -p 4312
vol.py -f mem.raw --profile=Win7SP1x64 getsids -p 4312
vol.py -f mem.raw --profile=Win7SP1x64 privs -p 4312
vol.py -f mem.raw --profile=Win7SP1x64 sessions
A normal user process with SeDebugPrivilege enabled usually means privilege escalation or a credential-dumping tool. CTF authors also hide flags in environment variables, so windows.envars without --pid followed by grep -i flag is a quick check.
Step 4: Network connections
# Volatility 3
vol -f mem.raw windows.netscan # pool scan: current AND closed connections, listeners
vol -f mem.raw windows.netstat # walks live tcpip structures: active only
vol -f mem.raw windows.netscan | grep -i established
# Volatility 2 (Vista / 7 / 8 / 10)
vol.py -f mem.raw --profile=Win7SP1x64 netscan
# Volatility 2 (XP / Server 2003 only)
vol.py -f mem.raw --profile=WinXPSP2x86 connections # active TCP
vol.py -f mem.raw --profile=WinXPSP2x86 connscan # includes closed TCP
vol.py -f mem.raw --profile=WinXPSP2x86 sockets # listening sockets
| Indicator | Why it's suspicious |
|---|---|
| Ports 4444, 1337, 8080, 8443 to a public IP | Metasploit or other reverse shell defaults |
ESTABLISHED from rundll32.exe, regsvr32.exe, notepad.exe, mshta.exe |
These programs rarely talk to the internet |
powershell.exe connecting out |
Download cradle or C2 |
lsass.exe or winlogon.exe with outbound connections |
Injection into a credential process |
| Many connections to one IP on high ports | Beaconing |
| A listener on an unusual port owned by a user process | Bind shell or backdoor |
The Owner column may be empty or show a PID for a process that has exited. Cross-check the PID in windows.psscan. Pivot on any suspicious IP: search the strings output for it and run yarascan for it (Step 7).
Step 5: DLLs, handles, and mutexes
# Volatility 3
vol -f mem.raw windows.dlllist --pid 4312
vol -f mem.raw windows.malware.ldrmodules --pid 4312 # DLLs missing from PEB lists
vol -f mem.raw windows.handles --pid 4312 | grep -E "File|Key|Mutant"
vol -f mem.raw windows.mutantscan # all named mutexes
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 dlllist -p 4312
vol.py -f mem.raw --profile=Win7SP1x64 ldrmodules -p 4312
vol.py -f mem.raw --profile=Win7SP1x64 handles -p 4312 -t Mutant,File,Key -s
vol.py -f mem.raw --profile=Win7SP1x64 mutantscan -s
dlllist: look for DLLs loaded from user-writable paths (AppData,Temp,Downloads) and misspelled system DLLs.ldrmodules: the PEB keeps three lists of loaded modules (InLoad,InInit,InMem). A module that isFalsein all three but backed by a mapped file was probably unlinked to hide it. Legitimate exception: the process's own.exeis never inInInit.- Mutexes are strong malware fingerprints. Many families use fixed mutex names so only one copy runs at a time. Search any unusual name online.
Step 6: Injected code
# Volatility 3
vol -f mem.raw windows.malware.malfind
mkdir -p out # -o needs an existing directory
vol -f mem.raw -o out/ windows.malware.malfind --pid 4312 --dump # writes pid.4312.vad.0x...-0x....dmp
vol -f mem.raw windows.malware.hollowprocesses
vol -f mem.raw windows.malware.processghosting
vol -f mem.raw windows.malware.suspicious_threads
vol -f mem.raw windows.vadinfo --pid 4312
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 malfind
vol.py -f mem.raw --profile=Win7SP1x64 malfind -p 4312 -D out/
vol.py -f mem.raw --profile=Win7SP1x64 vadinfo -p 4312
malfind looks for private memory regions (VADs) that are executable and writable (PAGE_EXECUTE_READWRITE) and not backed by a file on disk. Loaders normally map code from files. Shellcode and reflectively loaded DLLs live in private RWX memory.
How to read a hit:
4d 5a("MZ") at the start: a whole PE file is sitting in private memory. This is a reflective DLL or a manually mapped payload, and is almost always malicious.fc 48 83 e4 f0(cld; and rsp, -16): the start of the standard Metasploit / Cobalt Strike x64 shellcode stub.- Zeros or random bytes with a jump: possibly shellcode with the PE header wiped. Look at the disassembly column.
- False positives: .NET and JavaScript JIT engines (browsers,
powershell.exe, Teams, Slack) allocate RWX memory legitimately. Treat hits in those processes as leads, not proof.
The newer plugins cover other techniques. hollowprocesses flags a process whose image in memory doesn't match its file on disk (process hollowing). processghosting flags processes created from deleted files. suspicious_threads flags threads that start in memory not backed by a module.
Scan what you dumped:
yara -r rules/ out/ # your YARA rules
clamscan out/ # quick signature check
strings -n 8 out/pid.4312.vad.*.dmp | less
sha256sum out/*.dmp # check hashes against threat intel (don't upload client data)
Step 7: YARA and regex searches
This is the quickest way to answer "where in memory is this string?"
# Volatility 3: whole memory
vol -f mem.raw yarascan.YaraScan --yara-string "evil-domain.com"
vol -f mem.raw yarascan.YaraScan --yara-file rules.yar
vol -f mem.raw regexscan.RegExScan --pattern "flag\{[^}]+\}"
# Volatility 3: only inside one process's memory
vol -f mem.raw windows.vadyarascan --pid 4312 --yara-file rules.yar
vol -f mem.raw windows.vadregexscan --pid 4312 --pattern "https?://[a-zA-Z0-9./?=_-]+"
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 yarascan -Y "evil-domain.com"
vol.py -f mem.raw --profile=Win7SP1x64 yarascan -y rules.yar -p 4312
yarascan.YaraScan also accepts --wide to match UTF-16LE text (how Windows stores most strings) and --insensitive for case-insensitive matching.
Step 8: Files in memory
Windows caches file contents in RAM. Documents the user opened, scripts that ran, and dropped payloads can often be recovered even if they were deleted from disk.
# Volatility 3
vol -f mem.raw windows.filescan > filescan.txt
grep -i -E "\.(exe|dll|ps1|bat|vbs|js|hta|zip|rar|7z|docx?|xlsm?|pdf|txt|kdbx)$" filescan.txt
grep -i -E "\\\\(Desktop|Downloads|Documents|Temp)\\\\" filescan.txt
vol -f mem.raw -o out/ windows.dumpfiles --virtaddr 0xe0018a3f2c40 # one file (Offset from filescan)
vol -f mem.raw -o out/ windows.dumpfiles --pid 4312 # everything a process has open
vol -f mem.raw -o out/ windows.dumpfiles --filter "\.ps1$" --ignore-case # regex on the file name
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 filescan > filescan.txt
vol.py -f mem.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000003e8c9f20 -D out/ -n
vol.py -f mem.raw --profile=Win7SP1x64 dumpfiles -r "\.ps1$" -i -D out/ -n
In Volatility 3 the Offset from windows.filescan is a virtual address, so pass it to --virtaddr. In Volatility 2 the filescan offset is physical, and dumpfiles -Q takes it directly.
Output files look like file.0xe0018a3f2c40.0xe0018b1d0a20.DataSectionObject.invoice.docm.dat. The suffix tells you which cache the data came from:
| Suffix | Source | What you get |
|---|---|---|
.dat |
DataSectionObject |
File contents as read or written. Usually what you want for documents and scripts |
.img |
ImageSectionObject |
An executable mapped for execution. Laid out as in memory, so it is not byte-identical to the file on disk |
.vacb |
SharedCacheMap |
Cache manager views, useful for large files |
Recovered files are often incomplete: pages that were never read or were paged out come back as zeros. That means a recovered file's hash may not match the original. If you need the real hash of an executable, look in AmCache (windows.registry.amcache), which stores the SHA1 of executed programs.
Dumping processes and their memory
# Volatility 3
vol -f mem.raw -o out/ windows.pslist --pid 4312 --dump # executable -> 4312.<name>.0x<base>.dmp
vol -f mem.raw -o out/ windows.dlllist --pid 4312 --dump # every loaded DLL
vol -f mem.raw -o out/ windows.memmap --pid 4312 --dump # all addressable memory -> pid.4312.dmp
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 procdump -p 4312 -D out/ # executable.4312.exe
vol.py -f mem.raw --profile=Win7SP1x64 dlldump -p 4312 -D out/
vol.py -f mem.raw --profile=Win7SP1x64 memdump -p 4312 -D out/ # 4312.dmp
The full process memory dump (memmap --dump / memdump) is the one to search with strings. For example, what the user typed in Notepad lives in notepad.exe's heap as UTF-16:
strings -e l out/pid.4312.dmp | grep -i -E "flag|password|secret"
Step 9: Registry
The hives that were loaded at capture time are in memory, including the user's NTUSER.DAT and UsrClass.dat.
# Volatility 3
vol -f mem.raw windows.registry.hivelist
vol -f mem.raw windows.registry.printkey --key "Microsoft\Windows\CurrentVersion\Run" # SOFTWARE hive
vol -f mem.raw windows.registry.printkey --key "Software\Microsoft\Windows\CurrentVersion\Run" # NTUSER hives
vol -f mem.raw windows.registry.printkey --offset 0xc0018a4c2000 --key "Software\Microsoft\Windows\CurrentVersion\Run"
vol -f mem.raw windows.registry.printkey --key "ControlSet001\Control\ComputerName\ComputerName"
vol -f mem.raw windows.registry.userassist
mkdir -p hives && vol -f mem.raw -o hives/ windows.registry.hivelist --dump # export hives for Registry Explorer / RegRipper
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 hivelist
vol.py -f mem.raw --profile=Win7SP1x64 printkey -K "Microsoft\Windows\CurrentVersion\Run"
vol.py -f mem.raw --profile=Win7SP1x64 printkey -o 0xfffff8a000c3e010 -K "Software\Microsoft\Windows\CurrentVersion\Run"
vol.py -f mem.raw --profile=Win7SP1x64 userassist
vol.py -f mem.raw --profile=Win7SP1x64 shellbags
vol.py -f mem.raw --profile=Win7SP1x64 shimcache
vol.py -f mem.raw --profile=Win7SP1x64 dumpregistry -D hives/
Key paths are relative to the root of the hive, not to HKLM/HKCU. That is why the Run key is Microsoft\... in the SOFTWARE hive and Software\Microsoft\... in NTUSER.DAT. Without --offset, printkey searches every hive for the path you give. Add --recurse to also print all subkeys.
Keys worth checking:
| Key (relative to hive) | Hive | What it tells you |
|---|---|---|
Microsoft\Windows\CurrentVersion\Run, RunOnce |
SOFTWARE | Machine-wide autostart |
Software\Microsoft\Windows\CurrentVersion\Run, RunOnce |
NTUSER.DAT | Per-user autostart |
Microsoft\Windows NT\CurrentVersion\Winlogon (Shell, Userinit) |
SOFTWARE | Logon hijacking |
ControlSet001\Services |
SYSTEM | Services and drivers (persistence) |
ControlSet001\Control\ComputerName\ComputerName |
SYSTEM | Hostname |
ControlSet001\Services\Tcpip\Parameters\Interfaces |
SYSTEM | IP configuration |
Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs |
NTUSER.DAT | Recently opened files |
Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU |
NTUSER.DAT | Commands typed in Win+R |
Software\Microsoft\Windows\CurrentVersion\Explorer\TypedPaths |
NTUSER.DAT | Paths typed into the Explorer address bar |
Step 10: Credentials
# Volatility 3
vol -f mem.raw windows.registry.hashdump # local account NTLM hashes (SAM + SYSTEM)
vol -f mem.raw windows.registry.lsadump # LSA secrets (service passwords, DefaultPassword...)
vol -f mem.raw windows.registry.cachedump # cached domain logons (DCC2)
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 hashdump
vol.py -f mem.raw --profile=Win7SP1x64 lsadump
vol.py -f mem.raw --profile=Win7SP1x64 cachedump
hashdump output is User : RID : LM hash : NT hash. Crack the NT hash:
hashcat -m 1000 -a 0 nt_hashes.txt /usr/share/wordlists/rockyou.txt # NTLM
hashcat -m 2100 -a 0 dcc2_hashes.txt /usr/share/wordlists/rockyou.txt # cached domain creds (DCC2)
john --format=NT --wordlist=/usr/share/wordlists/rockyou.txt nt_hashes.txt
aad3b435b51404eeaad3b435b51404ee in the LM column just means "no LM hash stored", which is normal on modern Windows. The NT hash 31d6cfe0d16ae931b73c59d7e0c089c0 is an empty password.
For plaintext passwords and Kerberos tickets from lsass.exe, use pypykatz-volatility3 (install pypykatz in the same environment first) or the community mimikatz plugin for Volatility 2:
vol -f mem.raw -p ~/tools/pypykatz-volatility3 pypykatz
Plaintext passwords only appear on older systems or when WDigest was re-enabled (a common attacker trick: UseLogonCredential = 1).
Step 11: Persistence and execution evidence
# Volatility 3
vol -f mem.raw windows.svcscan # services, including binary path
vol -f mem.raw windows.malware.svcdiff # services hidden from the services.exe list
vol -f mem.raw windows.registry.scheduled_tasks # scheduled tasks from the SOFTWARE hive
vol -f mem.raw windows.registry.amcache # executed programs with SHA1 hashes
vol -f mem.raw windows.shimcachemem # AppCompatCache from kernel memory
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 svcscan
vol.py -f mem.raw --profile=Win7SP1x64 shimcache
For services, look for a Binary path in Users, ProgramData, or Temp, a ServiceDll in a strange path, a random-looking service name, or cmd.exe /c / powershell in the path.
Step 12: Timeline
A timeline puts everything in order and often reveals the attack sequence on its own.
# Volatility 3: runs every plugin that supports timelines and writes volatility.body
mkdir -p tl
vol -f mem.raw -o tl/ timeliner.Timeliner --create-bodyfile > tl/timeliner.txt
vol -f mem.raw windows.mftscan.MFTScan > mftscan.txt # MFT entries still in memory
vol -f mem.raw windows.mftscan.ResidentData # tiny files stored inside their MFT entry
mactime -b tl/volatility.body -d -z UTC > timeline.csv # mactime ships with The Sleuth Kit
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 timeliner --output=body --output-file=tl.body
vol.py -f mem.raw --profile=Win7SP1x64 mftparser --output=body --output-file=mft.body
vol.py -f mem.raw --profile=Win7SP1x64 shellbags --output=body --output-file=sb.body
cat tl.body mft.body sb.body > all.body
mactime -b all.body -d -z UTC > timeline.csv
windows.mftscan.ResidentData is an underrated CTF trick. NTFS stores files smaller than about 700 bytes directly inside their MFT record. If that record is in memory, you get the full file contents, including notes or flags that were deleted.
All Volatility timestamps are UTC.
Step 13: Strings with process context
strings finds text anywhere in the image. Volatility can then tell you which process each string belongs to.
strings -a -t d mem.raw > strings.txt # ASCII, decimal offsets
strings -a -t d -e l mem.raw >> strings.txt # UTF-16LE (most Windows text)
# Volatility 3
vol -f mem.raw windows.strings --strings-file strings.txt > strings_mapped.txt
# Volatility 2
vol.py -f mem.raw --profile=Win7SP1x64 strings -s strings.txt --output-file=strings_mapped.txt
# quick wins without Volatility
grep -a -o -E "flag\{[^}]{1,100}\}" mem.raw
grep -a -i -E "password=|pass:|pwd=" strings.txt | head
Mapping strings to processes is slow on large images. Only run it on a filtered strings.txt (for example after grep -i -E "http|flag|pass").
Kernel and rootkit checks
These matter less in most CTFs, but they are essential when you suspect a kernel rootkit.
| Check | Volatility 3 | Volatility 2 |
|---|---|---|
| System call table hooks | windows.ssdt |
ssdt |
| Kernel callbacks (process/image/registry notify) | windows.callbacks |
callbacks |
| Loaded drivers, including hidden ones | windows.modules vs windows.modscan |
modules vs modscan |
| Driver objects without a module | windows.malware.drivermodule |
— |
| IRP hooks | windows.driverirp |
driverirp |
| Driver objects | windows.driverscan |
driverscan |
| User-mode API hooks | — | apihooks |
A driver listed by modscan but missing from modules was unlinked to hide it.
Linux memory forensics
Volatility 3 needs an ISF for the exact kernel build. First, identify the kernel:
vol -f mem.lime banners.Banners
# Linux version 5.15.0-91-generic (buildd@...) #101-Ubuntu SMP ...
Then get a matching symbol table in one of three ways.
1. Prebuilt symbols. Abyss-W4tcher/volatility3-symbols has ISFs for many Ubuntu, Debian, and other distribution kernels:
vol --remote-isf-url 'https://github.com/Abyss-W4tcher/volatility3-symbols/raw/master/banners/banners.json' \
-f mem.lime linux.pslist
2. Build it yourself with dwarf2json on a machine with the same kernel's debug symbols (Ubuntu: linux-image-<version>-dbgsym from the ddebs repository; Debian: linux-image-<version>-dbg; RHEL/Fedora: kernel-debuginfo):
git clone https://github.com/volatilityfoundation/dwarf2json && cd dwarf2json && go build
./dwarf2json linux --elf /usr/lib/debug/boot/vmlinux-5.15.0-91-generic \
--system-map /boot/System.map-5.15.0-91-generic > 5.15.0-91-generic.json
xz 5.15.0-91-generic.json
mkdir -p ~/vol3-symbols/linux && mv 5.15.0-91-generic.json.xz ~/vol3-symbols/linux/
vol -s ~/vol3-symbols -f mem.lime linux.pslist
3. The official symbol pack (linux.zip). It only covers a small set of kernels.
Useful Linux plugins:
vol -f mem.lime linux.pslist
vol -f mem.lime linux.pstree
vol -f mem.lime linux.psaux # full command lines
vol -f mem.lime linux.bash # bash history recovered from memory
vol -f mem.lime linux.envars
vol -f mem.lime linux.sockstat # sockets per process
vol -f mem.lime linux.ip.Addr # interfaces and IPs
vol -f mem.lime linux.lsof --pid 1337
vol -f mem.lime linux.malware.malfind
vol -f mem.lime linux.malware.check_syscall # hooked syscall table
vol -f mem.lime linux.malware.hidden_modules # LKM rootkits
vol -f mem.lime linux.malware.tty_check
vol -f mem.lime linux.pagecache.Files --find /etc/shadow
vol -f mem.lime -o out/ linux.pagecache.InodePages --find /tmp/payload.sh --dump
vol -f mem.lime -o out/ linux.pagecache.RecoverFs # rebuild the cached filesystem as an archive
linux.bash recovers history even when the attacker ran unset HISTFILE or history -c, because it reads the history list from the bash process's heap.
Volatility 2 on Linux needs a profile built from the same kernel:
cd volatility/tools/linux && make # builds module.dwarf (needs kernel headers)
zip Ubuntu1804.zip module.dwarf /boot/System.map-$(uname -r)
cp Ubuntu1804.zip ../../volatility/plugins/overlays/linux/
vol.py --info | grep Linux # -> LinuxUbuntu1804x64
vol.py -f mem.lime --profile=LinuxUbuntu1804x64 linux_bash
vol.py -f mem.lime --profile=LinuxUbuntu1804x64 linux_find_file -F /etc/shadow # prints inode
vol.py -f mem.lime --profile=LinuxUbuntu1804x64 linux_find_file -i 0xffff88003d2e4a28 -O shadow
CTF tricks
- Paint or image viewers: dump the process memory (
windows.memmap --dumpormemdump), rename it to.data, open it in GIMP as Raw image data, and adjust width and offset until an image appears. This is the classic MemLabs Lab 1 technique. - Browser history:
windows.filescan | grep -i "History", dump the Chrome/EdgeHistoryfile, then runsqlite3 History "select url, title, last_visit_time from urls order by last_visit_time desc". With Volatility 2 you can use the communitychromehistoryplugin instead. - KeePass: find
.kdbxfiles infilescanand dump them, thenkeepass2john db.kdbx > hash.txt. KeePass 2.x before 2.54 also leaks most of the master password in process memory (CVE-2023-32784). keepass-password-dumper extracts it from a process dump. - Clipboard and window text (Volatility 2 only):
clipboard,editbox, andscreenshot -D out/.screenshotdraws window outlines and titles, not real pixels. - Deleted flag files:
windows.mftscan.ResidentDatafor small files, thenwindows.filescan+windows.dumpfilesfor larger ones. - Archives with passwords: the password is often in
windows.consoles,windows.cmdline, orwindows.envars. - Hibernation file given instead of RAM: Volatility 2 converts it with
vol.py -f hiberfil.sys --profile=Win7SP1x64 imagecopy -O hiber.raw. - Crash dumps:
.dmpfiles work directly with-f. Volatility 3 haswindows.crashinfoto show their header.
Troubleshooting
| Error / symptom | Likely cause | Fix |
|---|---|---|
Volatility 3: Unsatisfied requirement plugins.PsList.kernel.layer_name / symbol_table_name |
No symbols (offline or blocked download), wrong OS plugin (windows.* on a Linux image), or a truncated/corrupted image |
Rerun with -vvv; check the OS with banners.Banners; use a symbol pack with --offline; confirm the file size matches the RAM size |
| Volatility 3 is very slow on the first run | Downloading the PDB and building the cache | Wait once. Later runs are fast. vol --clear-cache if the cache is broken |
Volatility 2: No suitable address space mapping found |
Wrong profile or unsupported format | Use kdbgscan; try other suggested profiles; confirm the build with Volatility 3 windows.info |
| Volatility 2: plugins return empty output on Windows 10 | Profile build doesn't match (Win10 structures change between builds) | Use the exact Win10x64_<build> profile or switch to Volatility 3 |
imageinfo lists Win7SP0x64, Win7SP1x64, Win2008R2SP0x64... |
These share the same kernel structures | Win7SP1x64 usually works; confirm that pslist output looks sensible |
| Linux image doesn't load | ISF doesn't match the exact kernel build | Compare the banners.Banners string with your ISF's kernel version |
AVML .lime.compressed file doesn't load |
Compressed format | Convert it first with AVML's convert tool (avml-convert in older releases) |
Volatility 2 → 3 command reference
| Task | Volatility 2 | Volatility 3 |
|---|---|---|
| Identify image | imageinfo, kdbgscan |
windows.info / banners.Banners |
| Processes | pslist, pstree, psscan, psxview |
windows.pslist, windows.pstree, windows.psscan, windows.malware.psxview |
| Command lines | cmdline |
windows.cmdline |
| Console history | cmdscan, consoles |
windows.cmdscan, windows.consoles |
| Environment | envars |
windows.envars |
| SIDs / privileges | getsids, privs |
windows.getsids, windows.privileges |
| Network | netscan / connscan, sockets |
windows.netscan, windows.netstat |
| DLLs | dlllist, ldrmodules |
windows.dlllist, windows.malware.ldrmodules |
| Handles / mutexes | handles, mutantscan |
windows.handles, windows.mutantscan |
| Injection | malfind |
windows.malware.malfind |
| Hollowing | community hollowfind |
windows.malware.hollowprocesses |
| VADs | vadinfo, vaddump |
windows.vadinfo, windows.vadwalk |
| YARA | yarascan -Y / -y |
yarascan.YaraScan --yara-string / --yara-file, windows.vadyarascan |
| Find files | filescan |
windows.filescan |
| Dump files | dumpfiles -Q <phys> -D out/ |
windows.dumpfiles --virtaddr <addr> with -o out/ |
| Dump executable | procdump -p <pid> -D out/ |
windows.pslist --pid <pid> --dump |
| Dump process memory | memdump -p <pid> -D out/ |
windows.memmap --pid <pid> --dump |
| Dump DLLs | dlldump -p <pid> -D out/ |
windows.dlllist --pid <pid> --dump |
| Registry hives | hivelist, dumpregistry |
windows.registry.hivelist [--dump] |
| Registry keys | printkey -K |
windows.registry.printkey --key |
| UserAssist | userassist |
windows.registry.userassist |
| Hashes | hashdump, lsadump, cachedump |
windows.registry.hashdump, windows.registry.lsadump, windows.registry.cachedump |
| Services | svcscan |
windows.svcscan, windows.malware.svcdiff |
| Shimcache | shimcache |
windows.shimcachemem |
| MFT | mftparser |
windows.mftscan.MFTScan, windows.mftscan.ResidentData |
| Timeline | timeliner --output=body |
timeliner.Timeliner --create-bodyfile |
| Strings mapping | strings -s file |
windows.strings --strings-file file |
| Kernel | ssdt, callbacks, modules, modscan, driverscan |
windows.ssdt, windows.callbacks, windows.modules, windows.modscan, windows.driverscan |
| Clipboard / GUI | clipboard, screenshot, editbox |
not available |
| Interactive shell | volshell |
volshell |
Investigation checklist
1. sha256sum + windows.info → confirm the image loads, note SystemTime
2. pstree / psscan / psxview → odd parents, misspellings, hidden or exited processes
3. cmdline / consoles → what was executed and typed
4. netscan → C2 IPs and ports, which process owns them
5. malfind / hollowprocesses → injected code, dump it, run YARA
6. dlllist / ldrmodules / handles → odd DLL paths, unlinked modules, mutexes
7. filescan + dumpfiles → recover payloads and documents
8. registry printkey / userassist → persistence and user activity
9. hashdump / lsadump → credentials, if in scope
10. timeliner + mactime → put it all in order
11. strings + yarascan → pivot on every IOC you found
Practice and resources
- MemLabs: six free memory CTF labs (Windows 7, designed for Volatility 2; good practice for doing each one with both versions)
- TryHackMe Volatility room, CyberDefenders endpoint forensics labs, and HTB Sherlocks
- Volatility 3 documentation and GitHub
- Announcing the parity release of Volatility 3
- The Art of Memory Forensics: the reference book (written for Volatility 2, and the concepts still apply)
- SANS Hunt Evil poster: normal Windows process behaviour, useful alongside Step 1
- My writeups that use these techniques: QnQSec 2025 — Masks and Securinets 2025 — Lost File