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:

Use Volatility 2 when:

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.

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:

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.malfind is now windows.malware.malfind, windows.hashdump is now windows.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:

export VOLATILITY_LOCATION=file://$PWD/mem.raw
export VOLATILITY_PROFILE=Win7SP1x64
vol.py pslist

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 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

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:

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


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