12.2 Memory Analysis with Volatility 3: pslist, pstree, malfind, netscan & Vadinfo

Key Takeaways

  • Volatility 3 redesigns memory forensics architecture by replacing legacy static OS profiles with platform-agnostic symbol tables (Intermediate Symbol Format - ISF) dynamically retrieved from symbol servers.
  • Process enumeration through windows.pslist follows the kernel EPROCESS circular doubly-linked list (ActiveProcessLinks), whereas windows.pstree maps parent-child process hierarchies (PPID to PID).
  • Adversaries employ Direct Kernel Object Manipulation (DKOM) to unlink malicious processes from ActiveProcessLinks; windows.psscan bypasses this evasion by carving physical memory for unlinked _EPROCESS pool tags.
  • Code injection, process hollowing, and reflective DLL loading are detected using windows.malfind (identifying executable, non-file-backed VAD memory pages with PAGE_EXECUTE_READWRITE permissions) and windows.ldrmodules (cross-referencing PEB module lists).
  • Network telemetry is reconstructed using windows.netscan, which carves memory for TCP endpoint and UDP listener pool structures, exposing active and terminated C2 network sockets.
Last updated: September 2026

12.2 Memory Analysis with Volatility 3: pslist, pstree, malfind, netscan & Vadinfo

Quick Answer: The Volatility Framework is the industry-standard open-source platform for volatile memory forensics. Volatility 3 eliminates legacy Volatility 2 operating system "profiles" by adopting Intermediate Symbol Format (ISF) JSON symbol tables derived from Microsoft Program Database (PDB) debugging symbols. In Volatility 3, running processes are enumerated using windows.pslist (which traverses the ActiveProcessLinks doubly-linked list in the EPROCESS block) and windows.pstree (which displays parent-child process hierarchies). To defeat Direct Kernel Object Manipulation (DKOM) rootkits that unlink processes from ActiveProcessLinks, examiners run windows.psscan to carve unallocated memory for _EPROCESS pool tags. Injected code and reflective DLLs are uncovered using windows.malfind (flagging non-file-backed memory pages with PAGE_EXECUTE_READWRITE permissions hosting executable code) and windows.ldrmodules (cross-referencing PEB loader lists), while network connections are carved via windows.netscan.


Volatility Architecture: Volatility 2 vs. Volatility 3

Analyzing a physical memory dump requires understanding the precise internal structures of the target operating system's kernel. Operating system kernels frequently shift data structure offsets between minor service packs, build numbers, and monthly cumulative updates.

+-------------------------------------------------------------------------+
|               VOLATILITY 2 vs. VOLATILITY 3 ARCHITECTURE                |
+-------------------------------------------------------------------------+
| Architectural Dimension | Volatility 2 (Legacy)  | Volatility 3 (Current)       |
|-------------------------|------------------------|------------------------------|
| **Symbol Resolution**   | Hardcoded static       | Dynamic Intermediate Symbol  |
|                         | profiles (e.g.,        | Format (ISF) JSON tables;    |
|                         | Win7SP1x64)            | automated PDB symbol download|
|-------------------------|------------------------|------------------------------|
| **Operating System**    | Tightly bound to OS    | Platform-agnostic core       |
| **Abstraction**         | versions; requires manual| engine; auto-detects Windows, |
|                         | profile generation     | Linux, and macOS kernels     |
|-------------------------|------------------------|------------------------------|
| **Codebase Base**       | Python 2.7 (Deprecated)| Python 3 (Object-oriented    |
|                         |                        | modular plugin architecture) |
|-------------------------|------------------------|------------------------------|
| **Execution Syntax**    | vol.py -f mem.raw      | vol.py -f mem.raw            |
|                         |  --profile=Win10x64... |  windows.pslist              |
+-------------------------------------------------------------------------+

The Intermediate Symbol Format (ISF)

In Volatility 2, if an examiner analyzed a memory dump from a newly patched Windows 10 build that lacked a predefined profile, analysis failed until the examiner manually generated a custom profile using debugging tools.

Volatility 3 resolves this structural limitation through Intermediate Symbol Format (ISF):

  1. When ingesting a raw memory image, Volatility 3 scans the dump for the Windows kernel image (ntkrnlmp.exe or ntoskrnl.exe).
  2. It extracts the GUID and age of the corresponding Microsoft Program Database (PDB) debugging symbol file embedded within the kernel header.
  3. Volatility 3 automatically connects to Microsoft's public symbol server, downloads the exact PDB file corresponding to that specific kernel build, and converts it into an ISF JSON symbol table.
  4. This symbol table provides byte-exact offsets for critical structures (_EPROCESS, _KPROCESS, _VAD, _PEB, _ETHREAD), ensuring complete analytical accuracy across all operating system updates.

Process Enumeration Internals & DKOM Detection

In the Windows NT kernel, every executing process is represented by an Executive Process Block (EPROCESS), an opaque kernel-mode data structure residing in system pool memory. Understanding how Windows links and tracks these blocks is essential for identifying stealth rootkits.

Kernel ActiveProcessLinks Doubly-Linked Circular List:

   +------------------+       +------------------+       +------------------+
   |  EPROCESS: PID 4 |       | EPROCESS: PID 480|       | EPROCESS: PID 720|
   |  (System)        |       | (smss.exe)       |       | (csrss.exe)      |
   |                  |       |                  |       |                  |
   | [Flink] -------->|-----> | [Flink] -------->|-----> | [Flink] -------->|--+ (Loops to)
   | [Blink] <--------|<----- | [Blink] <--------|<----- | [Blink] <--------|<-+ (Head)
   +------------------+       +------------------+       +------------------+
                                       | (Attacker unlinks pointers via DKOM)
                                       ▼
                              +------------------+
                              | HIDDEN PROCESS   |
                              | (rootkit.exe)    |
                              | PID 9999         |
                              | [Flink] points   |
                              | to itself        |
                              +------------------+

1. windows.pslist: Walking the ActiveProcessLinks List

The windows.pslist plugin walks the ActiveProcessLinks member of the _EPROCESS structure. ActiveProcessLinks is a circular doubly-linked list (LIST_ENTRY) containing a Forward Pointer (Flink) pointing to the next process's _EPROCESS structure and a Backward Pointer (Blink) pointing to the previous process.

  • Limitations: Because windows.pslist relies entirely on pointer traversal, it only sees processes currently linked into the kernel scheduler's active list. If an attacker unlinks an entry, pslist will never encounter it.

2. windows.pstree: Reconstructing Parent-Child Hierarchies

The windows.pstree plugin enumerates processes and arranges them into an indented tree hierarchy based on each process's Process ID (PID) and Inherited From Unique Process ID (PPID):

  • Spotted Anomalies: Standard Windows processes exhibit immutable parent-child relationships. For example, services.exe must always be the parent of svchost.exe, and smss.exe must spawn wininit.exe and csrss.exe. If pstree reveals an instance of svchost.exe whose PPID traces to cmd.exe or powershell.exe, process masquerading or exploitation has occurred.
  • PPID Spoofing: Modern malware frequently leverages the UpdateProcThreadAttribute API with the PROC_THREAD_ATTRIBUTE_PARENT_PROCESS flag to spoof its parent process ID in user mode. While user-mode tools are deceived by this attribute, memory analysis cross-referencing process creation timestamps and handle tables reveals the true lineage.

3. windows.psscan: Uncovering Rootkits via Pool Tag Carving

Advanced rootkits utilize Direct Kernel Object Manipulation (DKOM) to unlink a malicious process from ActiveProcessLinks:

  • The rootkit driver modifies the Flink pointer of the preceding process to point directly to the succeeding process, and adjusts the Blink pointer accordingly.
  • The unlinked malicious process continues to execute because the Windows thread scheduler schedules individual Threads (_KTHREAD), not processes.
  • The Countermeasure (windows.psscan): Rather than walking linked lists, windows.psscan performs pool carving. It scans physical memory pages for the unique 4-byte pool tag signature associated with process allocations (Proc in older Windows builds or 0x636f7250 in modern systems). When it discovers an _EPROCESS block, it parses the structure regardless of whether it is linked into ActiveProcessLinks.
CommandPrimary Data SourceDetects DKOM Unlinked Processes?Detects Terminated / Exited Processes?
windows.pslistActiveProcessLinks doubly-linked listNo (Bypassed by unlinking)No (Unlinked upon termination)
windows.pstreePID and PPID relationships in active listNo (Bypassed by unlinking)No (Unlinked upon termination)
windows.psscanPhysical memory pool tag carving (Proc)Yes (Identifies orphaned blocks)Yes (Remnants persist in deallocated RAM)
# Enumerate active processes via doubly-linked list
python3 vol.py -f /evidence/mem.raw windows.pslist

# Display parent-child process hierarchy
python3 vol.py -f /evidence/mem.raw windows.pstree

# Carve memory for unlinked and terminated processes
python3 vol.py -f /evidence/mem.raw windows.psscan

Injected Code & Hidden Module Detection: Malfind & Ldrmodules

Adversaries rarely leave malicious executables running as independent, easily recognizable processes. Instead, they inject malicious shellcode or DLLs into legitimate system processes (e.g., explorer.exe, svchost.exe, lsass.exe) through techniques such as DLL Injection, Process Hollowing (RunPE), and Reflective DLL Injection.

Legitimate Process Virtual Address Space:
+-------------------------------------------------------------+
| 0x00400000 - Legitimate PE Executable Image (PAGE_READONLY)  |
+-------------------------------------------------------------+
| 0x7FFF0000 - ntdll.dll / kernel32.dll (PAGE_EXECUTE_READ)    |
+-------------------------------------------------------------+
| 0x02AB0000 - INJECTED CODE REGION                          |
|              • Memory Protection: PAGE_EXECUTE_READWRITE     |
|              • Memory Type: MEM_PRIVATE (Not mapped to disk) |
|              • First Bytes: 4D 5A (MZ Header) or Shellcode   |
|              ===> FLAGGED BY windows.malfind                 |
+-------------------------------------------------------------+

1. windows.malfind: Identifying Injected Memory Ranges

The windows.malfind plugin is the premier tool for detecting process injection in memory. It scans the Virtual Address Descriptor (VAD) tree of every process, evaluating memory ranges against three specific criteria:

  1. Memory Protection Mask: The memory page must be marked with executable and writable permissions—predominantly PAGE_EXECUTE_READWRITE (0x40 / RWX).
  2. Memory Allocation Type: The memory allocation must be MEM_PRIVATE or MEM_COMMIT rather than MEM_IMAGE (memory mapped directly from a legitimate executable or DLL file on disk). Legitimate code is virtually always mapped from a file on disk.
  3. Content Signature: The memory range contains executable code, characterized by an unmapped MZ (4D 5A) header, a Portable Executable stub, or common shellcode assembly instructions (e.g., NOP sleds 0x90, stack setup 0x55 0x8B 0xEC, or call instructions 0xE8).

When malfind flags a suspicious region, it outputs the process name, PID, memory start address, hex dump, and disassembled assembly instructions.

# Scan memory for injected code and dump suspicious regions to disk
python3 vol.py -f /evidence/mem.raw windows.malfind --dump

2. windows.ldrmodules: Detecting Unlinked / Reflective DLLs

In standard Windows execution, whenever a process loads a DLL, the OS loader updates three circular doubly-linked lists inside the Process Environment Block (PEB_LDR_DATA):

  • InLoadOrderModuleList: Tracks DLLs in the sequence they were loaded into memory.
  • InMemoryOrderModuleList: Tracks DLLs ordered by their virtual memory base addresses.
  • InInitializationOrderModuleList: Tracks DLLs in the order their initialization routines (DllMain) executed.

Reflective DLL Injection bypasses the standard Windows API loader entirely. The malware allocates private memory, maps its own DLL sections, and manually resolves import addresses without calling LoadLibrary. Consequently, the injected DLL exists in the process's Virtual Address Descriptors (VAD) but is completely absent from all three PEB loader lists.

The windows.ldrmodules plugin cross-references the VAD tree with the three PEB loader lists:

# Identify unlinked modules by cross-referencing VAD and PEB lists
python3 vol.py -f /evidence/mem.raw windows.ldrmodules
  • If InLoad, InInit, and InMem are False, but the module appears in the MappedPath or VAD with executable permissions, the DLL was injected reflectively or deliberately unlinked to evade detection.

Network Sockets, Process Arguments & Handles

1. windows.netscan: Reconstructing Network Activity

Network sockets reside in volatile memory and provide direct forensic evidence of Command and Control (C2) communication, lateral movement, and data exfiltration.

  • The windows.netscan plugin carves physical memory for network structures (_TCP_ENDPOINT, _TCP_LISTENER, _UDP_ENDPOINT).
  • It extracts the protocol (TCP/UDP), local IP address and port, foreign/remote IP address and listening port, socket operational state (ESTABLISHED, LISTENING, TIME_WAIT), owning Process ID (PID), process owner name, and the timestamp when the connection was created.
  • Forensic Advantage: Because netscan carves pool tags, it can recover closed or terminated network connections whose memory structures have not yet been overwritten by kernel reallocations.
# Scan memory for active and closed network connections
python3 vol.py -f /evidence/mem.raw windows.netscan

2. windows.cmdline: Extracting Command-Line Execution Arguments

Malware often executes system utilities (Living-off-the-Land Binaries - LotLBs) with complex arguments. The windows.cmdline plugin traverses the _RTL_USER_PROCESS_PARAMETERS structure located within each process's _PEB to extract the full command-line string used during process invocation.

  • Reveals encoded PowerShell commands (powershell.exe -nop -w hidden -enc JAB...), discovery commands (whoami /all, net group "Domain Admins" /domain), and credential dumping parameters (rundll32.exe comsvcs.dll, MiniDump).
# Extract process execution command-line parameters
python3 vol.py -f /evidence/mem.raw windows.cmdline

3. windows.handles: Inspecting Open Kernel Handles

Processes interact with system resources through Handles managed by the kernel. The windows.handles plugin enumerates open handles for a given process:

  • Mutants (Mutexes): Malware frequently creates unique named mutexes (e.g., Global\\Injector_Mutex_v1) to prevent multiple instances from executing simultaneously. Discovering a known malicious mutex identifies malware families instantly.
  • Opened Files & Named Pipes: Identifies open handles to sensitive files, encrypted ransom staging archives, or named pipes utilized by C2 frameworks (e.g., named pipes like msagent_12 used by Cobalt Strike).
  • Registry Keys: Identifies registry persistence keys currently held open by malicious threads.
# Enumerate Mutex (Mutant) handles across all processes
python3 vol.py -f /evidence/mem.raw windows.handles --object-type Mutant

4. windows.vadinfo: Virtual Address Descriptor Analysis

The Windows kernel uses a balanced self-balancing binary search tree (the VAD Tree) to track virtual memory address ranges allocated to each process.

  • The windows.vadinfo plugin traverses the VAD tree of a target process.
  • It details the start and end virtual addresses, allocation protection flags (PAGE_NOACCESS, PAGE_READONLY, PAGE_EXECUTE_READWRITE), commit status, private memory flags, and the mapped file path (if the allocation maps an image from disk).
  • Forensic Utility: Essential for deep reverse engineering of complex rootkits and confirming memory hollowings by inspecting memory protection permissions across the process's allocated address space.

Practical Forensic Case Scenario: Investigating In-Memory Beaconing

The Incident

A defense contractor's security operations center (SOC) detects sporadic, beacon-like HTTPS traffic originating from an internal engineering workstation to a foreign IP address. Disk-based antivirus scans return zero detections.

Volatility 3 Analysis Workflow

  1. Process Lineage Examination (windows.pstree): The examiner runs windows.pstree and notes a suspicious instance of spoolsv.exe (Print Spooler) with PID 4128. However, instead of being a child of services.exe, PID 4128 was spawned by powershell.exe (PID 3104).
  2. Network Connection Mapping (windows.netscan): Running windows.netscan reveals that PID 4128 (spoolsv.exe) maintains an active, established TCP connection to 198.51.100[.]88 over port 8443. Legitimate print spoolers never establish external internet connections.
  3. Injected Code Detection (windows.malfind): The examiner targets PID 4128 with windows.malfind. The plugin immediately flags a memory range at virtual address 0x0000018f2a100000 with PAGE_EXECUTE_READWRITE protection. The hex dump reveals an unmapped MZ header (4D 5A 90 00) followed by string references to ReflectiveLoader.
  4. Handle Inspection (windows.handles): Inspecting mutant handles for PID 4128 reveals an active mutex named Global\\MSSE-8492-Enc. Threat intelligence databases correlate this exact mutex string to a customized Cobalt Strike Beacon payload.
  5. Remediation: Armed with the remote C2 IP address and injected payload offsets, the incident response team blocks the C2 communication at the perimeter firewall and purges the persistence mechanisms established via PowerShell.
Loading diagram...
Volatility 3 Memory Analysis & Process Triage Workflow
Test Your Knowledge

A forensic analyst analyzes a memory capture from a compromised server. The analyst executes windows.pslist, which returns 42 running processes. Next, the analyst executes windows.psscan, which identifies 45 process structures, including a suspicious executable named kworker_stealth.exe that was completely absent from the pslist output. What technical mechanism explains the discrepancy between the two plugins?

A
B
C
D
Test Your Knowledge

An investigator executes windows.malfind against a memory image acquired from an endpoint suspected of hosting a Cobalt Strike payload. The output displays a memory allocation inside svchost.exe at virtual address 0x0000021b4a000000 with PAGE_EXECUTE_READWRITE permissions, containing an MZ header and an instruction sequence starting with 4D 5A 90 00. Why is this specific finding indicative of malicious code injection?

A
B
C
D
Test Your Knowledge

During a malware investigation, an analyst discovers that a suspected malicious DLL is executing inside explorer.exe. However, when the analyst reviews the Process Environment Block loader structures using windows.ldrmodules, the DLL is completely missing from InLoadOrderModuleList, InMemoryOrderModuleList, and InInitializationOrderModuleList, despite appearing in the process VAD tree with executable rights. What attack technique does this pattern represent?

A
B
C
D