What would you like to learn?

Try PowerShell, networks, XSS, or Step 138

Browse the full curriculum →

Forensics

Step 242. Memory Forensics: Volatility 3 — How to Catch Evidence That Vanishes When the Power Goes Off

Step 242Estimated practice · 5 hours

Level 3 — Forensics Track | Difficulty ★★★★☆ | Estimated time: 5 hours

Prerequisites: Step 239 (file signatures) complete. You know what a process is and how to search for strings (strings).

⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.

  • What you need: WSL Ubuntu + Python 3 (measured: 3.12). /proc access (root).
  • Caution: this environment has neither Volatility nor a practice memory image. All Volatility output is a "Screen example," while the principles of memory forensics — process lists, network connections, strings in memory — are measured live from Linux’s /proc.

The moment you power off a computer at a crime scene, the contents of RAM evaporate. Yet the most important evidence — the malicious process running right now, the command just typed, a decrypted key, a password sitting in cleartext — lives only in RAM, not on disk. Memory forensics is the technique of freezing this volatile evidence into a dump and dissecting it. Today you learn the map of the standard tool Volatility 3, and verify the principles behind its plugins directly on a live Linux system.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Enumerate the kinds of evidence that exist only in memory (processes, connections, command lines, cleartext data)
  • Reconstruct a process list and network connections by reading /proc
  • Extract strings directly from another process’s memory
  • Explain Volatility’s analysis order (windows.infopslist/psscanpstree → collection)
  • Know how the pslist/psscan difference is used for rootkit detection

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment WSL Ubuntu bash + Python 3 (measured: Python 3.12)
Today’s tools /proc/<pid>/ (comm, cmdline, maps, mem), /proc/net/tcp — all measured live
Concepts needed Volatility, process memory maps, dumping, the Volatility plugin system (Screen example)
Today’s deliverable A /proc observation script + "a secret string pulled from another process’s memory"

2-1. Why Memory — The Things That Aren’t on Disk

Disk is the world of "what was stored"; memory is the world of "what’s happening right now." The representative list of evidence that exists only in RAM:

  • Running processes and their command lines — including fileless malware that never exists as a file
  • Live network connections — who the machine is talking to at this very moment
  • Decrypted data — keys and strings that are encrypted on disk but cleartext while running
  • Command history and clipboard — what the user just did

This is why the beginner’s mistake of "just power it off first" is fatal in incident response (IR). Cut the power and this entire list evaporates.

2-2. Dump and Dissection — A Two-Stage Division of Labor

Memory forensics splits into two stages. ① Dump: freeze the RAM at the moment of the incident into a single file (tools like WinPmem on Windows or LiME on Linux read at the kernel level). ② Dissection: restore meaning from that raw blob of bytes. A dump is just gigabytes of bytes, so you need a tool that knows "where the process list lives and what each process’s structures look like" — that tool is Volatility.

2-3. /proc — The Memory Window of a Live System

Linux already has the perfect practice window open. /proc is a virtual filesystem the kernel maintains, showing the state of the currently running system as if it were files:

Path Contents Volatility counterpart
/proc/<pid>/comm, cmdline Process name & command line pslist, cmdline
/proc/net/tcp Current TCP connection table netstat
/proc/<pid>/maps The process’s memory map memmap
/proc/<pid>/mem The actual contents of that memory memmap --dump

Today we read the same information from a live system instead of a dump file — because what Volatility does on a dead dump, /proc shows you directly on a living system.

2-4. Volatility 3 — The Plugin Map (Screen Example)

It’s not installed in this environment, but the real-world standard workflow looks like this (Screen example — not actually executed):

# Screen example — stage 0: identify the image's OS (the first step of every analysis)
$ vol -f memory.raw windows.info
Variable        Value
Kernel Base     0xf8054b600000
NTBuildLab      19041.329.amd64fre.vb_release.190602-1739

# Screen example — stage 1: two kinds of process lists
$ vol -f memory.raw windows.pslist        # follows the kernel's "current list"
PID     PPID    ImageFileName
4       0       System
512     4       smss.exe
3844    512     powershell.exe

$ vol -f memory.raw windows.psscan        # sweeps all of memory for structure traces
PID     PPID    ImageFileName
...
5920    3844    evil.exe        ← a process that wasn't in pslist!

The difference between the two lists is the key clue. pslist reads the operating system’s official registry; psscan scans all of memory for traces of process structures. A process that erased its own name from the official registry (a rootkit) appears only in psscan.

# Screen example — stage 2: collecting relationships and traces
$ vol -f memory.raw windows.pstree    # parent-child: powershell.exe under winword.exe → the face of macro infection
$ vol -f memory.raw windows.cmdline   # what command launched each process
$ vol -f memory.raw windows.netstat   # network connections at dump time
$ vol -f memory.raw -o out/ windows.memmap --dump --pid 5920   # dump the suspicious process's memory to a file
$ strings out/pid.5920.dmp | grep -i "DH{"                    # find strings in it

Those last two lines — dumping a suspicious process’s memory and running strings over it — are exactly the job we’ll do ourselves today with /proc.


3. Follow Along

3-1. Staging the Incident — A Process Holding a Secret

Create a process that carries a secret only in memory (all local output in this chapter measured 2026-09-09 on WSL).

Input (victim.py)

import time, sys, os

SECRET = "DH{m3m0ry_n3v3r_f0rg3ts}"   # this value really exists in the running process's RAM
print(f"[victim] PID={os.getpid()} holding a secret in memory, waiting...")
sys.stdout.flush()
time.sleep(120)
python3 victim.py &
[victim] PID=616 holding a secret in memory, waiting...

How to read it: this process writes no files and uses no network. The secret lives only in this process’s RAM. No matter how hard you search the disk, this running process’s variable value won’t turn up — it’s evidence you can only see by looking at memory.

3-2. Reconstructing the Process List — The Principle of pslist

Each numbered directory in /proc is one process. Read the names and command lines:

import os
for pid in sorted((p for p in os.listdir("/proc") if p.isdigit()), key=int):
    try:
        name = open(f"/proc/{pid}/comm").read().strip()
        cmd = open(f"/proc/{pid}/cmdline", "rb").read().replace(b"x00", b" ").decode().strip()
        print(f"PID {pid:>6}  {name:16s}  {cmd[:60]}")
    except (FileNotFoundError, PermissionError):
        pass
PID      1  systemd           /sbin/init
PID      2  init-systemd(Co   /init
PID     49  systemd-journal   /usr/lib/systemd/systemd-journald
PID     98  systemd-udevd     /usr/lib/systemd/systemd-udevd
PID    616  python3           python3 victim.py
...

(Measured 2026-09-09 — 52 processes total, excerpt shown.)

How to read it: PID 616 catches the eye — you can even see the command line python3 victim.py. This is exactly the information Volatility’s pslist (list) and cmdline (command line) plugins restore from a dump. We just read the same thing from a live system.

3-3. Reconstructing Network Connections — The Principle of netstat

/proc/net/tcp is the current TCP connection table. The addresses are little-endian hex, so they need decoding:

import socket, struct
STATES = {"0A": "LISTEN", "01": "ESTABLISHED"}
def dec(hexaddr):
    h, p = hexaddr.split(":")
    ip = socket.inet_ntoa(struct.pack("<I", int(h, 16)))
    return f"{ip}:{int(p, 16)}"
with open("/proc/net/tcp") as f:
    next(f)
    for line in f:
        cols = line.split()
        st = STATES.get(cols[3], cols[3])
        if st:
            print(f"{dec(cols[1]):22s} -> {dec(cols[2]):22s} {st}")
127.0.0.54:53          -> 0.0.0.0:0              LISTEN
127.0.0.1:46047        -> 0.0.0.0:0              LISTEN
10.255.255.254:53      -> 0.0.0.0:0              LISTEN
127.0.0.53:53          -> 0.0.0.0:0              LISTEN

(Measured 2026-09-09. The LISTEN entries on port 53 are WSL’s internal DNS windows.)

How to read it: it’s quiet right now, but if there were an ESTABLISHED to an unknown external IP here — that’s the evidence "who is connected at this very moment," the kind that vanishes when the power goes off. What windows.netstat restores from a dump is this table’s incident-moment version.

3-4. Pulling the Secret from Process Memory — The Principle of memmap+strings

Today’s peak. Read /proc/<pid>/maps (the memory map), then search the actual contents of the writable regions (/proc/<pid>/mem) for a string starting with DH{:

import os, re, sys
target = sys.argv[1]                       # the victim's PID
found = []
with open(f"/proc/{target}/maps") as f:
    maps = [l.split() for l in f if "rw" in l.split()[1]]   # writable regions only
mem = os.open(f"/proc/{target}/mem", os.O_RDONLY)
scanned = 0
for m in maps:
    start, end = (int(x, 16) for x in m[0].split("-"))
    size = end - start
    if size > 8 * 1024 * 1024:
        continue
    try:
        os.lseek(mem, start, os.SEEK_SET)
        data = os.read(mem, size)
        scanned += size
        for mt in re.finditer(rb"DH{[ -~]{1,60}}", data):
            s = mt.group().decode()
            if s not in found:
                found.append(s)
    except OSError:
        continue
os.close(mem)
print(f"PID {target}: scanned {scanned/1024:.0f}KB of writable memory")
for s in found:
    print("found:", s)
python3 memscan.py 616
PID 616: scanned 5140KB of writable memory
found: DH{m3m0ry_n3v3r_f0rg3ts}

(Measured 2026-09-09.)

How to read it: a string that was never stored anywhere on disk was pulled out of another process’s RAM. These 30 lines are the very principle of what Volatility’s memmap --dump + strings combination does — follow the memory map (maps), read the contents (mem), and find string patterns. In real cases, decrypted keys, C2 addresses, and cleartext credentials come out here. "Even an encrypted file is cleartext in memory the moment it runs" — the power of memory forensics in one line.

3-5. The Full Workflow, Organized — If You Had a Dump

Translate today’s practice into Volatility’s language and it looks like this (Screen example):

# Screen example — the standard order for analyzing a memory dump
1. windows.info      → identify which OS this dump is (the basis for symbol selection)
2. windows.pslist    → the official process list
3. windows.psscan    → the scanned list — a difference from pslist means suspected hiding   ← 3-2's principle
4. windows.pstree    → strange parent-child pairs (a document spawning a shell?)
5. windows.cmdline   → command lines of suspicious processes                                 ← 3-2's principle
6. windows.netstat   → connections at dump time                                              ← 3-3's principle
7. memmap --dump + strings → strings from a suspicious process's memory                      ← 3-4's principle

What we did in /proc today in 3-2, 3-3, and 3-4 follows the same principles as steps 2, 5, 6, and 7 of this order. The tool works on "a dead dump"; we worked on "a live system" — that’s the only difference.


4. Missions & Exercises

Mission — An Observation Report on My Own System

  1. Extend 3-2’s code into a list that also reads each process’s PPID (parent number) (the PPid: line of /proc/<pid>/status)
  2. Use that list to print a parent-child tree (indented form) — the principle of pstree
  3. With 3-4’s scanner, scan one other process besides victim (e.g., a running shell) and observe what strings come out
  4. Write, in five sentences or fewer, "why incident response dumps memory first instead of powering off," citing today’s three experiments as evidence

Exercises

Problem 1. What is the difference between pslist and psscan, and why is a difference between their results suspicious?

Problem 2. An encrypted malware file couldn’t be found on disk, but memory analysis found it. Why?

Problem 3. In 3-4, only the "writable (rw)" regions of maps were read. Guess the reason for this choice.

Problem 4. Reading /proc/<pid>/mem can fail with a permission error. What privilege is needed, and what’s the corresponding barrier at a real incident scene?


Answers & completion criteria · expand/collapse

5. Model Answers & Completion Criteria

Mission Model Answer

①–② Core code for the PPID tree:

procs = {}
for pid in (p for p in os.listdir("/proc") if p.isdigit()):
    try:
        status = open(f"/proc/{pid}/status").read()
        name = re.search(r"Name:t(.+)", status).group(1)
        ppid = int(re.search(r"PPid:t(d+)", status).group(1))
        procs[int(pid)] = (name, ppid)
    except Exception:
        pass
def tree(pid, depth=0):
    if pid in procs:
        print("  " * depth + f"{procs[pid][0]} ({pid})")
    for p, (n, pp) in procs.items():
        if pp == pid:
            tree(p, depth + 1)
tree(1)

③ Observation example: scanning a shell process shows environment variables, fragments of recent commands, and remnants of history — a direct confirmation that "a process’s memory holds the residue of what that process has done."

④ Skeleton of the answer: the process list (3-2), the connection table (3-3), and the cleartext secret (3-4) all exist only in RAM and disappear when the power goes off. The string we pulled from PID 616 today is the proof. That’s why the field dumps memory before disk.

How to verify: ① Does the tree draw the actual hierarchy under systemd? ② Was the scan observation recorded? ③ Does the answer to ④ cite all three experiments?

Exercise Answers

Answer 1. pslist walks the linked list the kernel maintains (the official registry); psscan scans all of memory for traces of process structures. A rootkit hides by detaching its own node from the official registry, but erasing every structure trace somewhere in memory is hard. That’s why a process that appears only in psscan is suspected of being hidden — it could also be the residue of a terminated process, so further confirmation is needed.

Answer 2. To run, the CPU must read the code, and at that moment the code and data unwind into cleartext in memory. Disk encryption protects the "stored state" only; it cannot protect the "running state." As we saw in 3-4 today, memory is the place where every secret of execution lies exposed in cleartext.

Answer 3. Writable regions hold the heap and stack — the data a process produced while running (variables, input values, decryption results). Read-only regions are mostly program code and constants, identical to the original file on disk. Since the goal is finding "what came into being while running," rw regions are the first search area.

Answer 4. On Linux you need to be the same user or have root privileges (including ptrace constraints). The corresponding barrier at a real scene is kernel-level protection — it’s why memory-dump tools (WinPmem, LiME) demand administrator/kernel-driver privileges, and modern OS protections (kernel memory access restrictions, encrypted RAM) are making this collection steadily harder.

Completion Criteria Checklist

  • [ ] I can name the four kinds of evidence that exist only in memory
  • [ ] I read the process list and command lines from /proc myself
  • [ ] I decoded /proc/net/tcp and reconstructed the connection table
  • [ ] I extracted a secret string from another process’s memory
  • [ ] I memorized Volatility’s analysis order (info → pslist/psscan → pstree → collection)
  • [ ] I can explain the relationship between the pslist/psscan difference and rootkit detection
  • [ ] Mission: completed the PPID tree and observation report

6. Common Pitfalls & Fixes

Wall 1. Permission denied keeps you from reading /proc/<pid>/mem

Symptom: os.open raises PermissionError.
Cause: another user’s process memory is protected. The ptrace security policy (ptrace_scope) can also block reads.
Fix: target a process you launched, or run as root. This very constraint is the operating system’s answer that "memory is a protected area" — and the reason real dump tools need kernel privileges.

Wall 2. Volatility fails on every plugin

Symptom (Screen example): vol -f dump.raw windows.pslist throws a symbol error.
Cause: without symbols (the structure map) matching the dump’s OS version, Volatility cannot interpret what the bytes mean.
Fix: always start with windows.info — its output is the basis for symbol selection. Reverse the order and every plugin lies to you.

Wall 3. psscan returns too many results

Symptom (Screen example): psscan shows dozens of unknown processes.
Cause: scanning also catches the debris of terminated processes. They’re not all alive.
Fix: narrow the "hiding" candidates by taking the difference from pslist, and look at the exit-time field if present. Distinguishing debris from concealment is the analyst’s job.

Wall 4. Reading mem at an address from maps raises OSError

Symptom (verifiable during the 2026-09-09 exercise): lseek/read fails on some regions.
Cause: pages that are mapped but not currently in physical memory (swapped out), or kernel-only regions.
Fix: catch the exception and skip — that’s the job of try/except OSError in the practice code. In real dumps too, swapped pages simply aren’t there. "There are things even a memory dump doesn’t have" is a fact worth remembering.

Wall 5. You ran strings but nothing meaningful came out

Symptom (Screen example): tens of thousands of string lines pour out of the dumped process memory and you can’t find the point.
Cause: memory mixes in strings from code and libraries. Not everything is a clue.
Fix: narrow with patterns — the flag format (DH{), IP shapes, http, .exe, file paths. Deciding "the shape of what you’re looking for" first, like the regex in 3-4, is half of string analysis.


7. Summary

Today’s Concepts

Concept One-line explanation
Volatility RAM’s contents evaporate with the power — so dump first
Memory-only evidence Running processes, live connections, cleartext data, command history
/proc The memory window of a live system — a practice ground for Volatility’s principles
Dump and dissection A two-stage division of labor: collection (kernel privileges) and interpretation (symbols)
pslist vs psscan Official registry vs full scan — the difference is the signal of concealment
Symbols The dictionary that translates a dump’s bytes into structures — info comes first

Today’s Commands & Tools

Command/tool What it does
/proc/<pid>/comm, cmdline Process name & command line (the principle of pslist & cmdline)
/proc/net/tcp Current TCP connection table (the principle of netstat)
/proc/<pid>/maps + mem Process memory map and contents (the principle of memmap)
vol -f dump.raw windows.info (Screen example) Confirm the dump’s OS & symbols — always the first step
vol ... windows.pslist / psscan / pstree (Screen example) Process list, hiding detection, relationships
vol ... windows.memmap --dump --pid N (Screen example) Collect a suspicious process’s memory

An Instinct More Important Than Commands

Memory forensics’ worldview is one sentence — everything that runs exists somewhere in cleartext. Encryption, packing, and fileless techniques are disguises on disk, and memory is the backstage where those disguises come off. Today’s 30 lines that pulled a secret string from another process’s RAM are the proof of that worldview.

At the same time, you’ve seen the opposite direction too: evidence this rich disappears with a single power button. "Don’t power it off — dump it," the first action at an incident scene, is a simple rule, and now you know in your bones why it’s the golden rule.


Once every box is checked, Step 242 is complete.

ONE STEP FURTHER

Finished this lesson?

Check the completion criteria, then mark your progress.

Something wrong with this page or a link? Let us know.
Next