> Where you are: Part III, Chapter 22 of 40. Chapter 14 taught you to image a drive and prove it unaltered; Chapter 15 taught you the order of volatility and the hard decision of whether to capture a live system before you pull the plug; Chapter 21...
In This Chapter
- Evidence that never touched the disk
- The volatility of the source: capture before you pull the plug
- Acquiring memory
- How memory is organized: the model you must hold
- The Volatility framework
- Enumerating processes: pslist, psscan, pstree
- Network connections: netscan
- DLLs, handles, command lines, and identity
- Registry, credentials, and command history in memory
- Detecting code that lives only in memory
- Extracting executables and data from memory
- Tool demonstration: a full Volatility 3 triage pass
- Worked example: the laptop seized powered-on
- Common mistakes
- Limitations: knowing when to stop
- Progressive project: capture and analyze memory if the case system is live
- Summary
Chapter 22: Memory Forensics — Analyzing RAM for Evidence That Never Touched the Disk
Where you are: Part III, Chapter 22 of 40. Chapter 14 taught you to image a drive and prove it unaltered; Chapter 15 taught you the order of volatility and the hard decision of whether to capture a live system before you pull the plug; Chapter 21 taught you to weave artifacts into a timeline. This chapter goes after the most fragile evidence of all — the contents of physical memory, which vanish the instant the power dies. Some of the most decisive facts in a modern case (the encryption key that unlocks an otherwise-impenetrable disk, the malware that never wrote itself to storage, the network connection that proves command-and-control) exist only here, in RAM, and only for as long as the machine stays on.
Learning paths: This is a flagship chapter for 🛡️ Incident Response — fileless malware, code injection, live C2, and credential theft are read out of memory or not at all. It is equally central to the 🔍 Forensic Examiner, who increasingly faces full-disk encryption that only memory can defeat. 📜 Legal/eDiscovery practitioners need the doctrine here: a key lifted from RAM raises different Fourth- and Fifth-Amendment questions than a compelled passphrase, and a memory capture is the one piece of evidence that genuinely cannot be re-acquired. 💾 Data Recovery gets a narrower but real payoff — an unsaved document living in a process's working set, or an encryption key that turns an unreadable volume back into recoverable data.
Evidence that never touched the disk
Everything you have analyzed so far in this book sat still and waited for you. A drive that arrives in an evidence bag is the same drive next week, next month, next year — image it once, hash it, and the bytes are frozen. Memory is the opposite kind of evidence. The contents of RAM exist only while current flows through the DRAM cells that hold them, refreshed thousands of times per second; cut the power and, within seconds, they are gone. There is no "image it later." There is only "image it now, or never."
That fragility is exactly why memory is so valuable. Because RAM is where computation actually happens, it holds the live state of the machine — facts that the disk either never records or records only in scrambled, encrypted, or fragmentary form. Consider what lives in physical memory and nowhere else at the moment of seizure. The decryption keys for full-disk encryption — BitLocker's Full Volume Encryption Key, a LUKS master key, a VeraCrypt volume key — are derived from a passphrase at unlock time and held in RAM so the operating system can read the disk transparently; the disk itself stores only the encrypted key blob. The list of running processes, including the malicious one that exists nowhere on disk. The open network connections — the foreign IP and port a beacon is talking to right now. Injected code sitting in another process's address space. Decrypted content of files that are encrypted at rest. Command history typed into a console whose window has since been closed. The clipboard. Cached credentials and password hashes. Fragments of private-browsing sessions and chat windows. And the entire category that gives this chapter its urgency: malware that exists only in RAM — fileless implants, reflectively-loaded DLLs, packed payloads that are encrypted on disk and only decrypt themselves in memory.
Why This Matters. The shift to encryption and fileless attacks has made memory the deciding evidence in a growing share of cases. A perfectly imaged disk can be a wall of ciphertext you cannot read, while the very key that opens it sits in the RAM of the running machine in front of you. An advanced intrusion may leave nothing on disk to find — the implant runs from memory, the staging tool is reflectively loaded, the data is exfiltrated over an encrypted channel. If you walk past the running machine and "do it right" by pulling the plug and imaging the disk, you may destroy the only evidence that ever existed. Knowing when memory is the prize, and how to take it without contaminating it, is a core competency, not an advanced elective.
What memory holds that the disk never will
It helps to see the categories laid out, because each one maps to specific tooling later in the chapter:
WHAT RAM HOLDS — AND WHERE ELSE (IF ANYWHERE) IT EXISTS
┌──────────────────────────┬───────────────────────────────────────────────┐
│ Artifact in memory │ On disk? │
├──────────────────────────┼───────────────────────────────────────────────┤
│ FDE keys (FVEK, LUKS MK) │ Only as an ENCRYPTED blob → useless w/o key │
│ Running process list │ No (Prefetch/Amcache show PAST execution only) │
│ Live network connections │ No (logs may show some; live state is RAM) │
│ Injected / fileless code │ No — that is the whole point of fileless │
│ Unpacked malware image │ Disk has the PACKED form; RAM has the real one │
│ Decrypted file content │ Disk has ciphertext; RAM has plaintext │
│ Console command history │ Sometimes (PSReadLine); often RAM-only │
│ Clipboard contents │ No │
│ Cached creds / NTLM hashes │ SAM has hashes at rest; RAM has more, live │
│ Open handles (files/keys) │ No — shows what was OPEN at capture time │
└──────────────────────────┴───────────────────────────────────────────────┘
This is the first of the book's six themes wearing new clothes. Deleted ≠ destroyed you learned on disk: deletion removes a pointer, not the data. The memory analog is even more pointed. When a program frees a buffer or a process exits, the operating system marks that memory available — but it does not scrub the bytes. Freed pages keep their contents until something else is allocated over them, and a terminated process's kernel structures linger in the memory pool until that pool is reused. That is why, as you will see, you can find a process that has already exited, recover a chat message after the window closed, and read a key that the application "cleared" but never actually overwrote. In RAM, as on disk, the data outlives the pointer.
The volatility of the source: capture before you pull the plug
Chapter 15 introduced the order of volatility — the principle, codified in IETF RFC 3227, that when evidence is perishable you collect it from most-fragile to least-fragile. Memory forensics is where that principle bites hardest, so it is worth restating the ladder with memory in its rightful place near the top:
ORDER OF VOLATILITY (collect top-down: most perishable first)
┌─────────────────────────────────────────────────────────────────────┐
│ 1. CPU registers, cache nanoseconds — effectively │
│ uncollectable in the field │
│ 2. ROUTING/ARP table, kernel stats, seconds │
│ live network connections │
│ 3. PHYSICAL MEMORY (RAM) ◄── this chapter seconds (gone at poweroff)│
│ 4. Temporary file systems, /tmp, pagefile minutes–reboot │
│ 5. DISK (the forensic image) stable — survives power-off │
│ 6. Remote logging / monitoring data stable, off-box │
│ 7. Physical configuration, topology stable │
└─────────────────────────────────────────────────────────────────────┘
▲
If the machine is ON, RAM is collected BEFORE you
ever touch the power switch. Pulling the plug first
forecloses the option permanently — there is no undo.
The operational rule follows directly: if a system is live and within your lawful authority, acquire memory before you power it off. This is the single most consequential decision in volatile forensics, and it is irreversible. A drive you can re-image tomorrow. RAM you can capture exactly once, in exactly the state the machine is in at that instant, and the very act of pulling the plug to "preserve the disk properly" is the act that destroys the memory. Chapter 15 is the home of the live-versus-dead decision and the triage workflow; this chapter is what you do with the memory once that decision goes in its favor.
There is a subtlety that separates competent responders from novices. RAM does not vanish instantly at power-off — DRAM cells lose their charge over a span of seconds, and longer if the chips are cold (the basis of the historical "cold boot attack," in which researchers chilled memory modules to slow decay and recovered keys after a reboot). But you must never plan around remanence. By the time you could exploit it you would be running a non-standard, hard-to-defend procedure, and modern systems increasingly scrub memory on boot or encrypt it in the controller. Treat power-off as instantaneous loss. The professional posture is simple: the machine is on, the capture is authorized, you capture now.
Recovery vs. Forensics. Memory is usually framed as pure forensics, but it serves recovery in two genuinely important ways. First, the encryption key. A 💾 recovery technician handed a BitLocker- or VeraCrypt-encrypted volume with a forgotten passphrase faces an unreadable wall — unless the machine is still running with the volume mounted, in which case the Full Volume Encryption Key is sitting in RAM. Capture memory, extract the key (tools that scan for AES key schedules can find it), and the "unrecoverable" volume becomes readable; the data is recovered. Second, unsaved work. The document a user was editing but never saved to disk does not exist as a file — but its text lives in the editor's process memory. Carving a process's working set can return content that no file-level recovery could ever produce. Same capture, two disciplines: the forensic examiner reads memory to prove, the recovery technician reads it to restore.
Memory lives in more places than RAM
Before you decide a machine must be live to yield memory, remember that the operating system spills RAM onto disk in several predictable places. These are not substitutes for a clean live capture — they are stale, partial, and sometimes compressed — but they are forensic gold when the live moment has passed, and you will find them on the very disk images you already acquired in Chapter 14.
MEMORY THAT LANDS ON DISK (analyze these from your verified disk image)
┌──────────────────────┬─────────────────────────────────────────────────────┐
│ Source │ What it is / how to use it │
├──────────────────────┼─────────────────────────────────────────────────────┤
│ hiberfil.sys (Win) │ Compressed snapshot of RAM written at HIBERNATE. │
│ C:\hiberfil.sys │ Header signature 'hibr'/'HIBR' (or 'wake'/'RSTR' │
│ │ after a resume). Xpress-compressed; tools convert │
│ │ it to a raw image (vol2 imagecopy; Hibr2Bin). │
│ pagefile.sys / swap │ Pages EVICTED from RAM. No header, raw page data. │
│ C:\pagefile.sys │ Holds fragments of process memory; strings-mineable. │
│ /swapfile, swap part│ MUST be supplied to Volatility for complete address │
│ │ translation of paged-out regions. │
│ Crash dumps (.dmp) │ MEMORY.DMP (full/kernel) in C:\Windows. Microsoft │
│ │ crash-dump format; vol3 reads it directly. │
│ VM memory files │ VMware .vmem (+ .vmss/.vmsn snapshot state); │
│ │ Hyper-V .bin/.vsv or .vmrs; VirtualBox .sav. │
│ │ For a VM, you often don't need a live tool at all — │
│ │ pause/snapshot and copy the .vmem (near-atomic). │
└──────────────────────┴─────────────────────────────────────────────────────┘
The practical lesson: when you cannot capture live RAM, you have not necessarily lost all of memory. A hibernation file is a frozen snapshot of RAM from the last time the laptop slept with hibernation enabled. The pagefile holds whatever the memory manager evicted under pressure. A virtual machine's .vmem is the cleanest of all — pausing a VM and copying its memory file gives you a near-atomic image without any in-guest acquisition tool. The fourth theme — technology changes, principles don't — is already visible: the medium varies (DRAM, a compressed hiberfil, a .vmem), but the method is constant.
Acquiring memory
Memory acquisition has one defining difficulty that disk imaging does not: the act of capturing changes the thing being captured. To run an acquisition tool you must load it into the very memory you are imaging; it occupies pages, allocates buffers, and touches kernel structures. You cannot eliminate this footprint, only minimize it and document it. And because a running system keeps mutating memory throughout the seconds or minutes the capture takes, the resulting image is never perfectly atomic — page contents and the page tables that map them change mid-read, producing inconsistencies called page smear that can occasionally break address translation for a process. You accept this. A slightly smeared, honestly documented memory image is infinitely more valuable than no memory image, and the second theme — the original is sacred — applies with a twist unique to RAM: you cannot work on a copy of the original state, because acquiring it is the only way to make a copy at all, and that copy can never be reproduced. This is the one kind of evidence in the entire book that is genuinely un-re-acquirable.
The rules that follow from this:
- Acquire to external, removable media, never to the system's own disk — writing the capture locally overwrites unallocated space (and possibly the very pagefile/hiberfil you might want) and is indefensible.
- Use the smallest-footprint tool available and record exactly which tool, which version, and the time.
- Hash the image immediately after capture, and understand what that hash does and does not prove (below).
- Document everything, because the one question you will always be asked is "how do you know your tool didn't alter the evidence?"
Windows acquisition tools
Several mature, court-recognized tools capture Windows physical memory. They differ mainly in footprint, output format, and whether they are GUI or command-line.
- FTK Imager (GUI). The most widely deployed. File → Capture Memory…, choose a destination on external media, optionally tick Include pagefile (do it — you want the paged-out data) and Create AD1 if you also want an AccessData logical container. It writes a raw
memdump.mem(pluspagefile.sysif selected). Familiar, free, and defensible. - WinPmem (CLI, open-source, from the Velocidex/Rekall lineage). Small footprint, scriptable, supports raw and the AFF4 container format. Runs from a USB stick with no installation.
- DumpIt (CLI, Comae/Magnet). Famously simple — double-click or one command and it writes a memory image to the current directory. The modern build defaults to a Microsoft crash-dump (
.dmp) and can emit raw with a switch. - Magnet RAM Capture and Belkasoft Live RAM Capturer (GUI). Both purpose-built, free, minimal-footprint capturers favored in IR.
# WinPmem — raw output straight to external media (E: = USB), then verify.
# (Run from the USB stick; do not install onto the evidence machine.)
winpmem.exe --format raw -o E:\evidence\HOST-PC_mem.raw
# Or the AFF4 container (carries acquisition metadata, supports compression):
winpmem.exe -o E:\evidence\HOST-PC_mem.aff4
# DumpIt — one-shot capture to an explicit path
DumpIt.exe /OUTPUT E:\evidence\HOST-PC_mem.dmp
# Hash IMMEDIATELY after capture and write it to the chain-of-custody log.
certutil -hashfile E:\evidence\HOST-PC_mem.raw SHA256
# Equivalent hash + a minimal capture log entry, PowerShell-side.
$img = 'E:\evidence\HOST-PC_mem.raw'
$h = (Get-FileHash -Algorithm SHA256 -Path $img).Hash
$log = "{0} SHA256={1} tool=WinPmem 4.0 size={2}" -f `
(Get-Date -Format o), $h, (Get-Item $img).Length
Add-Content -Path 'E:\evidence\custody.txt' -Value $log
Linux acquisition tools
Linux long ago locked down the old shortcut of reading /dev/mem (it now exposes only low, mostly-useless ranges), so memory acquisition uses one of two purpose-built tools.
- LiME (Linux Memory Extractor) is a loadable kernel module. You compile it against the running kernel's headers,
insmodit, and it streams physical memory to a file or over the network. It offers three formats —raw,padded(raw with zero-filled gaps for unmapped ranges), andlime(the native format, which records the address ranges so analysis tools can map physical offsets correctly). Thelimeformat is preferred because it preserves the memory map. - AVML (Acquire Volatile Memory for Linux, from Microsoft) is a single userland static binary — no kernel module to compile, which is a huge advantage when you cannot match kernel headers in the field. It outputs LiME format and works across many distributions out of the box.
# LiME — build against the running kernel, load, capture in native 'lime' format.
make # builds lime.ko for $(uname -r)
sudo insmod ./lime.ko "path=/media/usb/host_mem.lime format=lime"
# (path= can also be tcp:<port> to stream off-box and avoid local writes)
# AVML — no module needed; just run the static binary, output LiME format.
sudo ./avml /media/usb/host_mem.lime
# Verify.
sha256sum /media/usb/host_mem.lime | tee -a /media/usb/custody.txt
A Linux caveat that bites the unprepared: Volatility cannot analyze a Linux image without a symbol table built from that exact kernel (more on this under the framework section). Capture the kernel version (uname -a) and, if you can, the System.map/debug symbols at acquisition time — you may need them to build the profile later, and the machine may not be available when you do.
Tool Tip. For VMs, skip the in-guest tools entirely when you can. Pausing the guest and copying VMware's
.vmem(with its.vmss/.vmsn) or taking a Hyper-V production checkpoint gives you a memory image that is far closer to atomic than any agent running inside the guest, with zero footprint in the evidence. Volatility reads.vmemnatively. When the "machine" is virtual, the hypervisor is your write-blocker.
macOS, and a word on hardware methods
macOS acquisition is the hardest of the three. System Integrity Protection and the locked-down kernel have made the old OSXPmem approach unreliable on modern releases; in practice, examiners lean on vendor tooling, full-disk-encryption key extraction, and the FileVault posture rather than generic RAM dumps. Hardware-based acquisition — reading memory over a DMA-capable interface such as Thunderbolt/PCIe (the kind of technique behind tools like PCILeech) — exists and can capture memory of a locked machine, but it is specialized, risky, and as likely to appear in an attacker's toolkit as a responder's. Treat it as a niche capability, not a default. For the overwhelming majority of cases, a software capturer run on a live, authenticated Windows or Linux host is the workflow.
Chain of Custody. A memory capture's provenance is interrogated harder than a disk's, precisely because the source is gone the moment you finish. Your notes must let you testify to the whole chain: "At 14:07 EDT I ran WinPmem 4.0 from a write-once USB device on the live host (hostname HOST-PC, confirmed via the on-screen session). Capture completed 14:11. Output
HOST-PC_mem.raw, 17,179,869,184 bytes (16 GiB), SHA-256c4e9…. Image and hash transferred to evidence drive EV-22, logged on the custody form (template in Appendix F)." Note what the hash proves and what it does not: it proves the image has not changed since capture, locking integrity from that instant forward. It cannot prove the image is a perfect, atomic snapshot of RAM — no memory hash can, because there is no stable original to hash against. State that distinction plainly; overclaiming it is how acquisitions get impeached. The acquisition fundamentals are owned by Chapter 14 — Forensic Acquisition; the live-system decision by Chapter 15 — Live Response and Triage.
How memory is organized: the model you must hold
You do not need to parse memory by hand — Volatility does — but you cannot interpret its output, defend it, or recognize when it is wrong without a working mental model of how an operating system arranges RAM. Three ideas carry almost everything.
Pages and address translation. Physical memory is divided into fixed-size pages (4 KiB on x86/x64 by default, with larger pages for some allocations). Programs never see physical addresses; each process runs in its own virtual address space, and the CPU translates virtual to physical on every access using a hierarchy of page tables. The root of that hierarchy for a given process is the Directory Table Base (DTB) — the value the CPU loads into the CR3 register when it schedules that process. This is why a memory analysis tool's very first job is to find a DTB: without it, a virtual address in a process is just a number it cannot resolve to a physical page. (It is also why the pagefile/swap matters — a virtual page may be marked "not present" in RAM because it was evicted to the pagefile, and only by supplying the pagefile can the tool follow it.)
Processes are kernel structures in a linked list. Windows represents every running process with an _EPROCESS structure in kernel memory. Among its fields: the image name (ImageFileName, a 15-character short name), the process ID (UniqueProcessId), the parent's PID (InheritedFromUniqueProcessId), creation and exit times (FILETIMEs), a pointer to the user-space PEB, the handle table, the thread list, and — crucially — ActiveProcessLinks, a LIST_ENTRY that threads every active process into a doubly-linked list (forward and backward pointers, FLINK/BLINK). Walking that list from the kernel's PsActiveProcessHead is how you enumerate "the running processes." Hold onto that, because it is exactly what a rootkit attacks.
Pool tags let you find structures by signature. The kernel allocates these objects from pool memory, and each allocation is prefixed with a _POOL_HEADER that includes a four-character pool tag identifying what it is. Processes are tagged Proc (bytes 50 72 6F 63), threads Thre, file objects File, TCP endpoints TcpE, TCP listeners TcpL, UDP endpoints UdpA. Because these tags are fixed signatures, you can scan all of memory for them and find the structures directly — even ones that have been unlinked from their lists or belong to processes that already exited. This is the difference between listing (walk the official list) and scanning (sweep memory for the signature), and that difference is the foundation of rootkit detection.
ENUMERATION vs. SCANNING — and how DKOM hides a process
(A) pslist — walk the official doubly-linked list from PsActiveProcessHead:
HEAD ⇄ [EPROCESS: explorer] ⇄ [EPROCESS: svchost] ⇄ [EPROCESS: chrome] ⇄ HEAD
FLINK/BLINK FLINK/BLINK FLINK/BLINK
(B) A DKOM rootkit UNLINKS its process by rewriting neighbors' pointers:
HEAD ⇄ [explorer] ⇄ ───────────────────────⇄ [chrome] ⇄ HEAD
[EPROCESS: evil.exe] ◄── still in RAM, just not
(FLINK/BLINK bypassed) reachable by the list walk
Pool tag "Proc" INTACT
(C) psscan — sweep ALL memory for the "Proc" pool tag (50 72 6F 63):
finds explorer, svchost, chrome AND evil.exe ◄── the hidden one surfaces
► The DIFFERENCE between pslist and psscan IS the hidden process.
(Theme: every action leaves a trace; unlinking from the list does
not remove the structure — and the leftover structure is the trace.)
The scanning idea is simpler than it sounds: sweep the raw image for the four-byte tag and treat each hit as a candidate the tool then validates as a real structure. In skeleton form (illustrative reference code — never executed here):
# The IDEA behind psscan: sweep raw memory for the "Proc" pool tag — the
# signature on every _EPROCESS allocation. Real tools then validate each hit
# as a true _EPROCESS; this just shows why a list-walk and a scan differ.
PROC_TAG = b"Proc" # 50 72 6F 63 — pool tag for process objects
def find_proc_tags(image_path, chunk=8 * 1024 * 1024):
hits, overlap = [], len(PROC_TAG) - 1
with open(image_path, "rb") as f:
carry, file_pos = b"", 0 # file_pos = file offset where `carry` begins
while True:
buf = f.read(chunk)
if not buf:
break
data = carry + buf
i = data.find(PROC_TAG)
while i != -1:
hits.append(file_pos + i) # physical offset of this tag
i = data.find(PROC_TAG, i + 1)
carry = data[-overlap:] # keep tail so a tag split across
file_pos += len(data) - overlap # the chunk boundary is not missed
return hits
# pslist WALKS the official linked list; psscan does this SWEEP. A process a
# DKOM rootkit unlinked is invisible to the walk but still carries its "Proc"
# tag — so the scan finds it. The structure outliving its pointer IS the clue.
There is one more structure worth naming because Volatility 2 depends on it: the Kernel Debugger Block (_KDDEBUGGER_DATA64), marked with the signature KDBG. It points to kernel globals — including PsActiveProcessHead (the head of the process list) and the OS build markers — so finding it lets the tool both locate the process list and confirm the profile. Volatility 3 takes a different route (symbol tables), but the concept of "find an anchor structure, then read the kernel from it" is the same.
The Volatility framework
Volatility is the open-source, court-recognized standard for memory analysis, and the rest of this chapter is built around it. It exists in two living generations, and you should know both because real labs still run both.
Volatility 2 vs. Volatility 3
Volatility 2 is the long-serving Python 2 version. It works by matching the image to a profile — a pre-built description of a specific operating-system build's kernel structures (e.g., Win10x64_19041, Win7SP1x64, LinuxUbuntu...x64). You tell it the profile (or let imageinfo/kdbgscan suggest one), and it knows where the fields live. Profiles are a strength (self-contained) and a weakness (you need the right one, and brand-new builds may lack a profile).
Volatility 3 is the rewrite on Python 3. It abandons profiles for symbol tables in the Intermediate Symbol Format (ISF) — JSON files generated from the operating system's own debug symbols. For Windows it fetches the matching kernel symbols from Microsoft's public symbol server automatically and caches them; you usually do not specify anything. For Linux/macOS there is no public symbol server, so you build an ISF from the exact kernel's debug info (with dwarf2json against the vmlinux/System.map) and drop it into the symbols directory — which is why capturing the kernel version at acquisition time matters so much.
The naming also changed. Volatility 2 plugins are bare names (pslist, netscan, malfind); Volatility 3 plugins are namespaced by OS (windows.pslist, windows.netscan, windows.malfind; linux.pslist, mac.pslist). The concepts are identical; learn one mapping table and you can drive either:
CONCEPT Volatility 2 Volatility 3
─────────────────────────────────────────────────────────────────────────────
identify the image imageinfo, kdbgscan windows.info
processes (walk list) pslist windows.pslist
processes (pool scan) psscan windows.psscan
process tree (parent/child) pstree windows.pstree
cross-view hidden-proc check psxview (compare pslist/psscan)
network connections netscan windows.netscan
loaded DLLs dlllist windows.dlllist
handles (files/keys/etc.) handles windows.handles
command line of a process cmdline windows.cmdline
console INPUT history cmdscan windows.cmdscan
console SCREEN buffer (+output) consoles windows.consoles
clipboard clipboard windows.clipboard
process tokens / SIDs getsids windows.getsids
services svcscan windows.svcscan
registry: list hives in RAM hivelist windows.registry.hivelist
registry: print a key printkey windows.registry.printkey
dump SAM hashes hashdump windows.hashdump
injected code detection malfind windows.malfind
DLL list-consistency check ldrmodules windows.ldrmodules
kernel modules (list / scan) modules / modscan windows.modules / .modscan
SSDT (syscall table) check ssdt windows.ssdt
kernel callbacks callbacks windows.callbacks
dump a file cached in memory dumpfiles windows.dumpfiles
dump a process's memory memdump / procdump windows.memmap --dump / .pslist --dump
YARA scan over memory yarascan windows.vadyarascan / yarascan.yarascan
build a super-timeline timeliner timeliner.timeliner
─────────────────────────────────────────────────────────────────────────────
Linux equivalents (vol3): linux.pslist, linux.pstree, linux.psaux, linux.bash,
linux.lsof, linux.proc.Maps, linux.lsmod, linux.check_syscall, linux.malfind
Identifying the image
Your first command tells you what you are looking at. In Volatility 2 you scan for the profile; in Volatility 3 you read the kernel info.
# Volatility 2: find a usable profile.
vol.py -f HOST-PC_mem.raw imageinfo
vol.py -f HOST-PC_mem.raw kdbgscan # more reliable when imageinfo is unsure
# Volatility 3: identify the kernel/build (downloads matching symbols).
vol -f HOST-PC_mem.raw windows.info
$ vol -f HOST-PC_mem.raw windows.info
Variable Value
Kernel Base 0xf80267a00000
DTB 0x1aa000
Symbols file:///.../symbols/windows/ntkrnlmp.pdb/....json.xz
Is64Bit True
IsPAE False
primary 0 WindowsIntel32e
NTBuildLab 19041.1.amd64fre.vb_release.191206-1406
MajorOperatingSystemVersion 10
NumberProcessors 8
SystemTime 2024-05-14 13:58:21
That output anchors everything that follows: 64-bit Windows 10 build 19041, eight processors, and — read the SystemTime carefully — the machine's clock said 13:58:21 UTC when the capture ran. Every process creation time you read next is comparable against that anchor.
Tool Tip. Volatility is the standard but not the only option. Rekall (now largely dormant) shares ancestry with WinPmem. MemProcFS mounts a memory image as a browsable file system — you literally explore processes, handles, and injected regions as folders, which is superb for triage and for analysts who think in directories. bulk_extractor sweeps a raw image for emails, URLs, network packets, and crucially AES key schedules, and aeskeyfind does the same for FDE keys. Use Volatility for structured analysis and the scanners for fast IOC and key extraction. The full toolkit lives in Chapter 36 — The Forensic Toolkit and the command reference in Appendix C.
Enumerating processes: pslist, psscan, pstree
Process enumeration is where almost every memory investigation begins, and the art is appreciating that the three plugins answer three different questions.
pslist walks the active-process doubly-linked list — the same list the operating system itself uses. It is fast and accurate for honest processes, and it can show exit times for processes whose structures linger.
$ vol -f HOST-PC_mem.raw windows.pslist
PID PPID ImageFileName Offset(V) Threads Handles CreateTime
4 0 System 0x... 142 - 2024-05-14 12:31:02
308 4 smss.exe 0x... 2 - 2024-05-14 12:31:02
420 512 csrss.exe 0x... 11 - 2024-05-14 12:31:05
512 420 wininit.exe 0x... 1 - 2024-05-14 12:31:05
712 512 services.exe 0x... 6 - 2024-05-14 12:31:06
760 512 lsass.exe 0x... 9 - 2024-05-14 12:31:06
1120 712 svchost.exe 0x... 18 - 2024-05-14 12:31:07
3120 3088 explorer.exe 0x... 54 - 2024-05-14 12:33:40
2340 3120 chrome.exe 0x... 38 - 2024-05-14 12:41:18
4488 3120 svchost.exe 0x... 7 - 2024-05-14 13:52:09 ◄── ?
5012 3120 powershell.exe 0x... 12 - 2024-05-14 13:55:44
Already two anomalies leap out to a trained eye, and they are the kind a beginner walks past. A real svchost.exe is launched by services.exe (PID 712) — it is a service host. The svchost.exe at PID 4488 has explorer.exe (3120) as its parent, which services never do, and it started at 13:52, long after boot. Second, a powershell.exe parented to explorer.exe is ordinary for an interactive admin — but in context (started 13:55, right after the rogue svchost) it is worth pursuing. We will confirm the svchost is hollowed shortly.
psscan ignores the list and sweeps memory for the Proc pool tag. It finds processes the list omits — terminated processes whose structures survive in the pool, and, critically, processes a DKOM rootkit unlinked to hide. The investigative move is to diff psscan against pslist: anything in the scan but not the list demands an explanation.
$ vol -f HOST-PC_mem.raw windows.psscan (excerpt — entries NOT seen in pslist)
PID PPID ImageFileName Offset(P) CreateTime ExitTime
6004 4488 rundll32.exe 0x... 2024-05-14 13:52:33 (none) ◄── hidden:
running, but
absent from pslist
1860 712 svchost.exe 0x... 2024-05-14 12:31:09 2024-05-14 13:10:55 (exited)
rundll32.exe (PID 6004) appears in the scan with no exit time — it is running — yet it never showed in pslist. That is the signature of a hidden process: present in memory by pool tag, absent from the official list. The exited svchost (1860) is a normal artifact — a process that ended but whose Proc structure has not yet been overwritten, the memory analog of deleted ≠ destroyed. (Volatility 2's psxview automated this cross-view comparison across many sources — pslist, psscan, the CSRSS handle table, the session list — and flagged discrepancies; in Volatility 3 you compare pslist and psscan outputs directly, or use community cross-view plugins.)
pstree renders the parent/child hierarchy, which is how you spot relationship anomalies that a flat list hides.
$ vol -f HOST-PC_mem.raw windows.pstree
... 0xeproc 712 services.exe
*** 0xeproc 1120 svchost.exe
... 0xeproc 3088 userinit.exe (exited; spawned explorer)
*** 0xeproc 3120 explorer.exe
*** 0xeproc 2340 chrome.exe
*** 0xeproc 4488 svchost.exe ◄── svchost under explorer = WRONG ancestry
*** 0xeproc 6004 rundll32.exe ◄── child of the rogue svchost (the hidden one)
*** 0xeproc 5012 powershell.exe
The tree makes the story plain. A svchost.exe should descend from services.exe; this one descends from explorer.exe, and it in turn spawned the hidden rundll32.exe. Parent-child ancestry is one of the most reliable, hardest-to-fake tells in memory forensics, because the parent PID is recorded at process creation and an attacker rarely bothers to forge it convincingly. Every action leaves a trace — here, the attacker's process-creation chain is itself the trace.
Network connections: netscan
Live network state is the second pillar of a memory investigation, and on Vista and later you get it from a single pool-tag sweep. netscan scans for the TCP/UDP endpoint structures (TcpE, TcpL, UdpA) and reports local and foreign addresses, ports, connection state, the owning PID, and (often) the creation time.
$ vol -f HOST-PC_mem.raw windows.netscan
Proto LocalAddr LPort ForeignAddr FPort State PID Owner
TCPv4 10.0.0.50 49774 203.0.113.45 443 ESTABLISHED 4488 svchost.exe ◄──
TCPv4 10.0.0.50 49810 142.250.72.196 443 ESTABLISHED 2340 chrome.exe
TCPv4 0.0.0.0 445 0.0.0.0 0 LISTENING 4 System
TCPv4 10.0.0.50 49801 52.96.165.18 443 ESTABLISHED 3120 explorer.exe
UDPv4 0.0.0.0 5353 * * 1120 svchost.exe
The chrome connection to a Google address on 443 is unremarkable. The connection that should stop you cold is PID 4488 (the rogue svchost.exe) talking to 203.0.113.45:443 — an external host, on a port that is normally HTTPS, owned by the process whose parentage we already flagged. HTTPS on 443 is the favorite hiding place for command-and-control because it blends into normal encrypted web traffic; the content is opaque, but the existence and ownership of the connection — visible only because we caught it live in memory — is the evidence. You will pursue this same connection from the wire side in Chapter 23 — Network Forensics; memory tells you which process owns a flow that packet capture alone cannot attribute to a binary.
DLLs, handles, command lines, and identity
With suspect processes identified, you interrogate them.
cmdline reveals how each process was launched — and for a hollowed process this is frequently the clinching anomaly. A legitimate svchost.exe is always launched with a service-group argument (-k netsvcs, -k LocalService, etc.). One launched with no -k argument, or from the wrong path, is not a service host at all.
$ vol -f HOST-PC_mem.raw windows.cmdline
PID Process Args
1120 svchost.exe C:\Windows\system32\svchost.exe -k netsvcs -p
4488 svchost.exe C:\Users\Public\svchost.exe ◄── no -k, wrong path!
5012 powershell.exe powershell.exe -nop -w hidden -enc SQBFAFgA... ◄── encoded, hidden
2340 chrome.exe "C:\Program Files\Google\Chrome\...\chrome.exe"
Two more red flags surface. PID 4488 runs from C:\Users\Public\svchost.exe — the real one lives in C:\Windows\System32 — and carries no service argument. And PID 5012's PowerShell uses -nop -w hidden -enc …: no-profile, hidden window, Base64-encoded command, the classic shape of a launcher (-enc decodes a Base64 UTF-16 script; you would decode it to read intent). None of this is on disk in this form; it is reconstructed from the process's environment in memory.
dlllist enumerates the modules loaded into a process via the normal loader, with each DLL's base address, size, load time, and full path — useful for spotting a malicious DLL loaded into a legitimate process. handles lists every kernel object a process holds open — files, registry keys, mutexes, events, other processes. A mutex with an odd hard-coded name is a common malware-family fingerprint; an open handle to another process's memory hints at injection. getsids shows the security identifiers in a process's token, telling you which user a process runs as (and whether it has escalated). privileges lists the token privileges (a process holding SeDebugPrivilege it has no business holding is suspicious), and envars dumps environment variables, which sometimes leak paths, proxy settings, or staging directories.
$ vol -f HOST-PC_mem.raw windows.dlllist --pid 4488 (excerpt)
Base Size Name Path
0x7ff6... 0x2a000 svchost.exe C:\Users\Public\svchost.exe
0x7fff... 0x1f0000 ntdll.dll C:\Windows\System32\ntdll.dll
0x7fff... 0x9c000 kernel32.dll C:\Windows\System32\kernel32.dll
0x7fff... 0x70000 ws2_32.dll C:\Windows\System32\ws2_32.dll ◄── sockets
0x7fff... 0xb8000 wininet.dll C:\Windows\System32\wininet.dll ◄── HTTP
The presence of ws2_32.dll and wininet.dll in a "svchost" that has no business doing web requests corroborates the C2 connection from netscan. Each artifact is a thread; the finding is the rope you braid from them.
Registry, credentials, and command history in memory
The operating system keeps active registry hives mapped in RAM, which means memory analysis is also, partly, registry analysis — and uniquely, it can reach the volatile hives that never exist as files (HKLM\HARDWARE) and the live state of keys that may differ from what is on disk.
hivelist enumerates the hives present in memory and their virtual offsets; printkey reads a specific key, including its values and subkeys with last-write times. This lets you confirm autostart entries, recently set keys, and configuration as it was in memory at capture.
$ vol -f HOST-PC_mem.raw windows.registry.printkey \
--key "Software\Microsoft\Windows\CurrentVersion\Run"
Key: ...\CurrentVersion\Run Last Write: 2024-05-14 13:52:40
Value: "Updater" Data: C:\Users\Public\svchost.exe (REG_SZ) ◄── persistence
There it is: the rogue binary wired into Run for persistence, with a last-write time minutes after the process started — the malware's own configuration change, captured in memory, corroborating the disk-side autostart analysis from Chapter 16 — Windows Forensics.
hashdump extracts the NTLM password hashes for local accounts by combining the SAM and SYSTEM hives in memory (and lsadump/cachedump recover LSA secrets and cached domain credentials). This is genuinely useful — for proving an attacker had the means to crack or pass credentials, or, in a lawful exam, for accessing a custodian's own protected data — and genuinely sensitive.
Legal Note. Recovering password hashes, LSA secrets, or an encryption key from memory is powerful and tightly bounded by authority. Whether you may crack a hash, use a recovered key to decrypt a volume, or extract credentials at all depends entirely on the warrant, consent, or engagement scope that authorizes the examination — decided in Chapter 25 — The Legal Framework, never improvised at the keyboard. There is also a doctrine unique to memory: a Full Volume Encryption Key lifted from RAM is seized evidence, not compelled testimony, which can sidestep the Fifth-Amendment problems of forcing a suspect to disclose a passphrase. That distinction is precisely why capturing memory from a live, unlocked machine can be decisive — but it presupposes lawful access to the running system in the first place. Note what you can do; do only what you are authorized to do.
Command history is one of memory's most evocative artifacts. cmdscan finds the COMMAND_HISTORY structures that conhost.exe/csrss.exe maintain — the list of commands typed into a console, even after the window is closed. consoles goes further and reconstructs the console screen buffer, which includes the command output, not just the input — you can read what the attacker typed and what the system printed back.
$ vol -f HOST-PC_mem.raw windows.cmdscan (input history)
Cmd #0: whoami
Cmd #1: net user admin P@ssw0rd!2024 /add
Cmd #2: net localgroup administrators admin /add
Cmd #3: reg add ...\Run /v Updater /d C:\Users\Public\svchost.exe /f
Cmd #4: del C:\Users\Public\dropper.exe
$ vol -f HOST-PC_mem.raw windows.consoles (screen buffer = input + OUTPUT)
C:\> net user admin P@ssw0rd!2024 /add
The command completed successfully.
C:\> del C:\Users\Public\dropper.exe
Read those four commands and the intrusion narrates itself: enumerate identity, create a backdoor admin account, escalate it, install persistence, and delete the dropper to cover the on-disk tracks. The deletion is the point — the dropper is gone from the disk, but the command that deleted it survives in memory, and so does the running payload it dropped. This is the third theme at full strength: the absence of a trace is itself a trace. The attacker erased the file; memory recorded the erasure. (clipboard rounds out this family, recovering whatever text was last copied — sometimes a password, a wallet address, or a snippet of a document.)
Detecting code that lives only in memory
We now reach the heart of the chapter — and of modern incident response. The most capable malware does not sit on disk waiting to be hashed and quarantined. It runs from memory: injected into a legitimate process, reflectively loaded so it never appears in the loader's records, or grafted into a hollowed-out host process. On disk there is nothing, or nothing incriminating. In memory, the injection leaves structural fingerprints that Volatility is built to find.
Code injection and malfind
The workhorse is malfind. It hunts for memory regions that should not contain code but do: regions that are private (not backed by any file on disk), committed, and marked executable and writable at once — the PAGE_EXECUTE_READWRITE (RWX) protection. Legitimate code is almost always mapped from a file (an .exe or .dll) and is executable-but-not-writable; an RWX private region is the classic signature of injected shellcode or a manually-mapped PE, because the injector needs to write the code and then execute it. malfind dumps the start of each such region and disassembles it, so you can see whether it begins with an MZ header (an injected executable) or raw shellcode.
$ vol -f HOST-PC_mem.raw windows.malfind
PID: 4488 Process: svchost.exe
Vad Tag: VadS Protection: PAGE_EXECUTE_READWRITE CommitCharge: 12 PrivateMemory: 1
Address range: 0x1d0000 - 0x1e2fff
0x1d0000 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 MZ..............
0x1d0010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@.......
0x1d0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Disassembly:
0x1d0000 4d 5a dec ebp ; pop edx ◄── "MZ" interpreted as code
0x1d0002 90 nop
0x1d0003 00 03 add [ebx], al
Three facts make this a finding, not a maybe. The region is private (PrivateMemory: 1) — no file backs it. Its protection is RWX, the injector's tell. And it begins with 4D 5A ("MZ"), the DOS/PE header magic — there is a whole executable living in a memory region that the disk knows nothing about. That is fileless code injection caught red-handed. (Not every RWX private region is malicious — some JIT compilers and packers legitimately use them — which is why malfind is a lead generator: it surfaces candidates you then corroborate with parentage, network, command history, and disk artifacts.)
WHY RWX-PRIVATE-WITH-MZ IS SUSPICIOUS
NORMAL module: VAD ──maps──► \Windows\System32\foo.dll (file-backed)
protection: PAGE_EXECUTE_READ (executable, NOT writable)
INJECTED code: VAD ──► (nothing on disk) (private)
protection: PAGE_EXECUTE_READWRITE (writable AND
content begins with 4D 5A "MZ" executable)
└── attacker had to WRITE it, then RUN it → RWX is the scar
DLL injection and ldrmodules
A subtler technique loads a malicious DLL into a victim process without going through the normal Windows loader — reflective DLL injection maps the DLL into memory and fixes it up by hand, so the loader's bookkeeping never records it. The detection exploits exactly that omission. The Process Environment Block (PEB) maintains three linked lists of loaded modules — InLoadOrder, InMemoryOrder, and InInitializationOrder — and a normally-loaded DLL appears in all three. ldrmodules cross-references every memory-mapped image against those three lists and flags any that are missing from some or all of them.
$ vol -f HOST-PC_mem.raw windows.ldrmodules
PID Process Base InLoad InInit InMem MappedPath
2340 chrome.exe 0x7fff... True True True \...\chrome.dll
2340 chrome.exe 0x7fff... True True True \Windows\System32\ntdll.dll
2340 chrome.exe 0x2a0000 False False False (no name) ◄── injected DLL
Read the flags. The legitimate modules are True/True/True and have a file path. The region at 0x2a0000 is mapped and executable yet appears in none of the loader lists and has no backing file path — a DLL that was injected, not loaded. False/False/False with no MappedPath is the canonical reflective-injection signature.
THE THREE PEB MODULE LISTS — and what "missing" means
PEB ─► Ldr ─► InLoadOrderModuleList ──────► ntdll, kernel32, chrome ...
─► InMemoryOrderModuleList ────► ntdll, kernel32, chrome ...
─► InInitializationOrderList ──► ntdll, kernel32, chrome ...
Normal DLL : present in ALL THREE → ldrmodules True/True/True
Reflective : present in NONE → ldrmodules False/False/False
(mapped + executable, but the loader never recorded it
→ the gap in the bookkeeping IS the evidence)
Process hollowing
Process hollowing (process replacement) is the technique behind our rogue svchost.exe. The attacker starts a legitimate process suspended, unmaps (hollows out) its real image, writes a malicious executable into the freed space, repoints the entry thread, and resumes it. To the casual eye it is svchost.exe; inside, it is something else. Memory exposes it several ways at once, which is why corroboration is so powerful:
malfindfinds the injected PE as a private RWX region with anMZheader (as we saw for PID 4488).- The VAD for the process's main image shows a private region where a mapped, file-backed image should be — the on-disk
svchost.exeis no longer what occupies the process's base. - The command line and path are wrong (
C:\Users\Public\svchost.exe, no-k), and the parent is wrong (explorer.exe, notservices.exe). - Comparing the in-memory image to the on-disk binary (dump the process's executable, hash it, compare to the legitimate
System32\svchost.exe) shows they differ — the in-memory code is not the disk's code. - Loaded DLLs (
ws2_32,wininet) and a live C2 connection that a service host would never make complete the picture.
No single one of these is proof; together they are airtight. That is the methodology of memory forensics in miniature: each plugin answers a narrow question, and the correlation across independent structures — parentage, protection bits, loader lists, network ownership, on-disk comparison — is what survives cross-examination.
Kernel rootkits
The deepest hiding happens in kernel space, and memory is often the only place to catch it because a rootkit can lie to every tool running on the live operating system while being unable to lie to a raw image of RAM. The detection plugins each target a specific subversion technique:
ssdtdumps the System Service Descriptor Table — the kernel's syscall dispatch table. Entries that point outside the legitimatentoskrnl.exe/win32k.sysranges indicate SSDT hooking: the rootkit redirected system calls through its own code to filter results (e.g., to hide files or processes).callbackslists registered kernel notification callbacks (process/thread/image-load/registry callbacks). Malicious drivers register here for stealth and persistence; an unfamiliar driver in the callback list is a lead.modules(walk the loaded-module list) versusmodscan(pool-scan forMmLddriver objects) is the kernel analog ofpslist-versus-psscan: a driver that appears in the scan but not the list has unlinked itself to hide. The diff is the rootkit.driverirp/driverscanexamine driver objects and their IRP dispatch tables for hooks that intercept I/O.
$ vol -f HOST-PC_mem.raw windows.modules | wc -l → 142 driver entries
$ vol -f HOST-PC_mem.raw windows.modscan | wc -l → 143 driver entries
▲
modscan finds ONE MORE than the list → a hidden/unlinked driver.
Identify the extra entry; it is your kernel rootkit candidate.
Everything in this section is framed for detection, in keeping with this book's hard constraint: memory forensics teaches you to find injected and hidden code after an incident, not to build it. The deep treatment of analyzing a recovered sample — unpacking, behavioral analysis, IOC extraction — is post-incident work covered in Chapter 32 — Malware Forensics; the anti-forensic techniques an adversary uses to resist this analysis, and how to detect them, are in Chapter 30 — Anti-Forensics.
Extracting executables and data from memory
Finding the malicious code is half the job; getting a copy out so you can analyze and hash it is the other half. Volatility can extract at several granularities:
pslist --dump(or vol2procdump) writes a process's main executable image back to a PE file — your copy of the hollowedsvchost's real payload.memmap --dump(vol2memdump) writes all of a process's addressable memory to a file — every page it can reach, ideal forstrings-mining or carving embedded data.dumpfilescarves files cached in memory (from the file cache / mapped sections) by PID or by address — you can recover an open document, a loaded DLL, or a script the process read.- vadinfo / vadwalk enumerate the VAD tree and let you dump individual regions (vol2
vaddump), which is how you extract precisely the injected regionmalfindflagged. dlllist --dump(vol2dlldump) extracts a specific loaded or injected DLL.
# Dump the hollowed process's executable image and hash it for comparison.
vol -f HOST-PC_mem.raw -o ./out windows.pslist --pid 4488 --dump
sha256sum ./out/*4488*.exe
# → compare against the SHA-256 of the legitimate C:\Windows\System32\svchost.exe
# (different ⇒ the in-memory image is NOT the real svchost — hollowing confirmed)
# Dump everything PID 4488 can address, then mine it for IOCs.
vol -f HOST-PC_mem.raw -o ./out windows.memmap --pid 4488 --dump
strings -el ./out/pid.4488.dmp | grep -Ei '203\.0\.113|http|\.onion|BEGIN RSA'
Once you have raw bytes out, two scanners earn their keep. strings plus targeted grep surfaces IP addresses, URLs, registry paths, wallet addresses, and key material in seconds. yarascan (vol3 windows.vadyarascan or yarascan.yarascan) runs YARA signatures across memory — feed it the rules for a known malware family and it points you straight to the matching process and region. A standard triage habit is to run bulk_extractor over the whole raw image up front; it independently pulls emails, URLs, network artifacts, and AES key schedules, often handing you IOCs and keys before you have opened Volatility at all.
Recovery vs. Forensics. The same extraction plugins that a 🔍 examiner uses to pull a malicious payload serve the 💾 recovery technician's most dramatic save. Recall the encryption scenario: a volume is BitLocker-encrypted and the passphrase is lost, but the machine is running with the volume mounted. Run
bulk_extractororaeskeyfindover the captured RAM, recover the AES key schedule for the Full Volume Encryption Key, and you can decrypt the "unrecoverable" volume and restore the data. Or the gentler case: a user's hours of unsaved work in a crashed editor —memmap --dumpthe editor's process,stringsit, and the text is there to reconstruct. The forensic examiner reads memory to prove an intrusion; the recovery technician reads the very same bytes to give a client back their key or their document. Full-disk-encryption mechanics and key recovery are owned by Chapter 29 — Encrypted Device Forensics.
Tool demonstration: a full Volatility 3 triage pass
In practice you do not run plugins ad hoc; you run a disciplined sequence, save everything, and let the outputs cross-check each other. Here is a defensible first pass over a Windows image, the order an experienced responder uses, with a one-line rationale for each step.
IMG=HOST-PC_mem.raw
OUT=./vol_out; mkdir -p "$OUT"
vol -f $IMG windows.info > $OUT/00_info.txt # build/time anchor + symbols
vol -f $IMG windows.pslist > $OUT/01_pslist.txt # honest process list
vol -f $IMG windows.psscan > $OUT/02_psscan.txt # pool scan (find hidden/exited)
vol -f $IMG windows.pstree > $OUT/03_pstree.txt # ancestry anomalies
vol -f $IMG windows.cmdline > $OUT/04_cmdline.txt# how each process launched
vol -f $IMG windows.netscan > $OUT/05_netscan.txt# live connections + owners
vol -f $IMG windows.malfind > $OUT/06_malfind.txt# injected RWX-private code
vol -f $IMG windows.ldrmodules > $OUT/07_ldrmod.txt # unlinked / reflective DLLs
vol -f $IMG windows.svcscan > $OUT/08_svcscan.txt# service persistence
vol -f $IMG windows.cmdscan > $OUT/09_cmdscan.txt# typed console history
vol -f $IMG windows.consoles > $OUT/10_consoles.txt# console screen (input+output)
vol -f $IMG windows.registry.printkey \
--key "Software\Microsoft\Windows\CurrentVersion\Run" > $OUT/11_run.txt # autostart
vol -f $IMG windows.modules > $OUT/12_modules.txt # kernel module list
vol -f $IMG windows.modscan > $OUT/13_modscan.txt # kernel pool scan (diff vs list)
vol -f $IMG timeliner.Timeliner --create-bodyfile > $OUT/14_timeline.body # for Ch.21
sha256sum $OUT/* > $OUT/_hashes.txt # hash your OUTPUTS too — your work is evidence
Reading the pass as a whole is the skill. pslist→psscan diff exposes hidden PID 6004; pstree shows the wrong ancestry for PID 4488; cmdline shows its wrong path and missing -k; netscan ties 4488 to a C2 IP; malfind finds the injected PE inside it; ldrmodules finds a reflective DLL in chrome; cmdscan/consoles narrate the attacker's hands-on commands; printkey shows the persistence; modules-vs-modscan hints at a kernel component. Eleven plugins, one coherent, corroborated account. And note the last line: you hash your outputs, because in a contested matter your analysis files are themselves evidence, and the timeliner bodyfile feeds directly into the super-timeline you build in Chapter 21 — Timeline Analysis.
Worked example: the laptop seized powered-on
Now the consequential case — anchor case #4, the child-exploitation prosecution that recurs through this book — at the precise moment where memory forensics is decisive. This account stays strictly at the level of procedure, law, and ethics. No content is examined or described here; the point is the method and the decisions, not the material.
Investigators execute a search warrant and find the subject's laptop powered on and logged in, the screen unlocked. A field examiner on the team recognizes immediately what is at stake. The laptop's system drive is BitLocker-encrypted. If they follow the old reflex — photograph the scene, pull the plug, bag the device, image the disk later — the machine will reboot to a BitLocker pre-boot prompt, the Full Volume Encryption Key will be gone from RAM, and absent the recovery key the disk will be an unreadable wall of ciphertext. The single most important investigative decision in the case is being made in the first ninety seconds, exactly as Chapter 15 — Live Response and Triage trains: capture volatile state before power-off.
Working within the warrant's authority for on-scene live capture, the examiner inserts a prepared, write-once USB device and runs a vetted capturer (FTK Imager's memory capture, with the pagefile included). The capture log records the tool and version, start time 09:14, completion 09:21, output filename and size, and the SHA-256 computed on the spot. Only then is the machine powered down and the disk imaged per Chapter 14. The memory image — call it SUBJECT-LT_mem.raw, SHA-256 logged — goes into evidence alongside the disk image.
Back in the lab, the memory image yields what the disk alone never could:
SUBJECT-LT_mem.raw — analysis (procedural findings only)
windows.info → Windows 10 19041; SystemTime 2024-05-14 13:14:02 UTC
(the machine clock at capture — anchors all process times)
bulk_extractor / → recovered an AES key schedule consistent with the BitLocker
aeskeyfind FVEK → the encrypted system volume is now decryptable
WITHOUT the subject's passphrase (key seized, not compelled)
windows.pslist → the mounted-volume state and a media application running at
seizure (PID/CreateTime logged) — establishes the volume was
OPEN and decrypted at the moment of seizure
windows.handles → open handles to paths on the (now-decryptable) volume at
--pid <app> capture time → ties live access to the device, at a time
windows.netscan → connection state at seizure (logged for the report)
The forensic chain is now intact in a way it could not have been otherwise. The recovered key makes the encrypted disk image readable, so the disk examination — deleted-file recovery, EXIF/GPS analysis, browser history, the access timeline — can proceed at all (those techniques live in Chapters 16, 18, and 20). Independently, the memory establishes that the volume was mounted and an application had files open at the instant of seizure, a fact about the live state that no later disk analysis can reconstruct. The two evidence streams corroborate, and every step traces to a hashed image.
Legal Note. The key recovered from RAM is physical evidence the investigators seized, not testimony the subject was compelled to give — a distinction with real Fifth-Amendment weight, because compelling a passphrase implicates the right against self-incrimination in ways that seizing a key in memory does not. This is one of the strongest practical arguments for live capture of an unlocked, running device. But it stands entirely on lawful access: the warrant must authorize the on-scene live acquisition, and scope discipline governs what you may then examine. The doctrine — Fourth- and Fifth-Amendment boundaries, the limits of warrant scope — is Chapter 25 — The Legal Framework; the encryption mechanics are Chapter 29.
Ethics Note. This is the book's sixth theme at its most demanding: the human cost is real — for victims first and foremost, and for the examiner. Three obligations are non-negotiable. Scope: you examine only what the authority permits, and you do not go looking beyond it. Mandatory reporting: an examiner who encounters child sexual abuse material has reporting duties (in the U.S., providers under 18 U.S.C. §2258A; examiners under their jurisdiction's law and policy) — you do not "handle it quietly." Well-being: this work carries a real risk of secondary trauma, and managing it (rotation, support, mandated counseling, tooling that minimizes unnecessary exposure such as hashing against known-file databases) is a professional duty, not a weakness. The full treatment — law, reporting, and examiner well-being — is owned by Chapter 28 — Ethics. The technical skill in this chapter exists to serve those human stakes, never to be exercised carelessly around them.
War Story. A manufacturing firm's endpoint protection kept flagging "suspicious activity" on a finance workstation, yet every disk scan came back clean — no malicious file, nothing in the usual autostart locations, no obvious dropper. The on-call analyst captured RAM before the scheduled nightly reboot.
malfindlit up aPAGE_EXECUTE_READWRITEprivate region inside a perfectly ordinaryexplorer.exe, beginning with4D 5A;ldrmodulesshowed aFalse/False/Falsemodule with no path;netscantied the host to a beaconing IP every sixty seconds. The implant lived entirely in memory, re-injected on each boot by a small, heavily-obfuscated registry-run script that the disk scanners had dismissed as benign. Had they followed the reboot-and-reimage reflex, the volatile implant would have been wiped from RAM and the disk would have stayed "clean" — the investigation would have closed with a shrug. Memory was not a supplementary source in that case. It was the only source. That is the lesson to carry: when an attack is built to leave nothing on disk, refusing to capture memory is choosing not to find it.
Common mistakes
- Pulling the plug on a live machine before capturing RAM. This is the cardinal error. It is irreversible, it destroys the keys/processes/connections/injected code that may be the whole case, and there is no recovery from it. If the machine is on and you are authorized, memory comes first — every time.
- Writing the memory image to the system's own disk. It overwrites unallocated space (and possibly the pagefile/hiberfil you wanted), contaminates the disk evidence, and is indefensible. Always capture to external, removable media.
- Forgetting the pagefile/swap. A virtual page evicted to the pagefile is not in RAM; without supplying
pagefile.sys/swap, Volatility cannot translate those addresses and you silently miss data. Capture the pagefile (FTK Imager's checkbox) and feed it to your analysis. - Trusting
pslistalone. A DKOM-hidden process never appears in the list. If you do not also runpsscanand diff the two, you will report "no malicious process found" while one runs in front of you. List and scan, always. - Reading a ShimCache-style assumption into memory.
malfindsurfaces RWX-private regions that are candidates, not convictions — JIT engines and some packers use RWX legitimately. Corroborate (MZ header, parentage, network, on-disk comparison) before you call it injection. - Using the wrong profile / missing symbols. A mismatched Volatility 2 profile or a missing Volatility 3 symbol table produces empty or garbage output that looks like "nothing here." Confirm the build with
imageinfo/windows.infofirst, and for Linux build the ISF from the exact kernel. - Overclaiming the hash. A memory image's SHA-256 proves it hasn't changed since capture; it does not prove the capture was an atomic, smear-free snapshot. State both halves honestly — defense counsel knows the difference.
- Treating the encryption-key find as automatically lawful to use. Recovering an FVEK is a technical act; using it to decrypt a volume is an act governed by authority. Confirm scope before you turn the key.
Limitations: knowing when to stop
Memory forensics is powerful precisely because of how perishable its source is, and that same perishability defines its limits. A professional report states them as plainly as the findings.
The image is never perfectly atomic. A running system mutates memory throughout the capture, so page contents and page tables can be inconsistent — page smear — and occasionally a process simply will not reconstruct because the page table that mapped it changed mid-read. You disclose this. It does not invalidate the evidence, but it bounds the certainty of any single reconstructed structure, and it is why corroboration across plugins matters so much.
You cannot re-acquire it. This is the limitation with no workaround in the entire book. Memory is the one piece of evidence that is genuinely un-reproducible — the state existed once, you captured it once, and it is gone. If the capture is botched, there is no second attempt. Plan, prepare the tools, and rehearse, because the field is the only take.
No symbols, no analysis. Without a matching profile (vol2) or symbol table (vol3) — especially for Linux/macOS, where you must build the ISF from the precise kernel — Volatility cannot parse the image at all. An image whose kernel you cannot symbolize is, for now, an opaque blob; capturing kernel version and debug symbols at acquisition time is the cheap insurance against this.
Paged-out, freed, and overwritten data may be gone. A page evicted to a pagefile you do not have is unreachable. A freed structure that has been reused is overwritten — deleted ≠ destroyed holds only until reuse, and a long-running, memory-pressured system overwrites aggressively. The longer between the event and the capture, the more has been recycled.
Anti-forensics targets memory too. Sophisticated implants minimize their footprint, encrypt their own pages until needed, hook the very structures your tools read, or run from kernel space to lie to live tooling (which is why a raw image beats live inspection, but even a raw image can be obfuscated). And the hardest wall: if the target was powered off before you arrived, or used a system that writes nothing volatile you can reach, or the relevant data was never in RAM, there may simply be nothing to capture.
When the memory does not support a conclusion, the fifth theme governs: "the captured memory is insufficient to determine whether X occurred" is a valid, defensible finding. The mark of an expert is not forcing volatile, smeared, partial evidence to say more than it can — it is knowing exactly how far the bytes carry and stopping there.
Progressive project: capture and analyze memory if the case system is live
Continue building your Forensic Case File (introduced in Chapter 5 — The Forensic Process, acquired in Chapters 14–15). This chapter adds the volatile-evidence layer. If, in your case scenario, the subject system was encountered live, memory acquisition is mandatory and happens before any power-off; if it was already off, document that decision and work from hiberfil.sys/pagefile.sys/any crash dump or VM .vmem instead.
- Capture (or source) memory. If the system is live and authorized, acquire RAM to external media with a documented tool (WinPmem/FTK Imager/DumpIt on Windows; LiME/AVML on Linux), including the pagefile. Hash the image immediately and log the capture (tool, version, start/end time, size, SHA-256) on your custody form (Appendix F). If the system was off, extract
hiberfil.sys/pagefile.sysfrom your verified disk image and note why no live capture exists. - Identify and enumerate. Run
windows.info(orimageinfo), thenpslist,psscan, andpstree. Diffpsscanagainstpslistand record any process present in the scan but not the list. - Hunt for in-memory code and live state. Run
cmdline,netscan,malfind, andldrmodules. For every suspicious process, capture: its parentage, its command line/path, any owned network connections, any RWX-private region (with the first bytes), and anyFalse/False/Falsemodule. - Recover the narrative and the artifacts. Run
cmdscan/consolesfor attacker commands,printkeyon theRunkey for persistence, andhivelist/hashdumpas authority permits. Dump at least one suspect executable (pslist --dump), hash it, and compare to the legitimate on-disk binary where applicable. - Feed the timeline and state limits. Emit a
timelinerbodyfile for merger into your master timeline in Chapter 21. For every memory finding, write the limitation beside it (smear, paged-out, candidate-not-conviction, authority-bounded) so future-you reports it honestly.
Save all Volatility outputs and your dumped artifacts into the case-file folder, and hash the outputs. The capstone in Chapter 38 — The Capstone Investigation assembles the whole file; volatile evidence that lives only in your head cannot be assembled later — because, uniquely, it cannot be recaptured at all.
Summary
Memory forensics is the discipline of reading evidence that exists only while the machine is running — and that makes it both the most fragile and, increasingly, the most decisive part of an investigation. You learned why it matters: encryption keys, the live process list, open network connections, injected and fileless code, decrypted content, command history, and credentials live in RAM and frequently nowhere else, and the move to full-disk encryption and memory-resident malware has turned "capture the RAM" from optional to essential. You placed memory in the order of volatility and internalized the irreversible rule it implies — if the system is live and authorized, acquire memory before you power it off — while noting that memory also lands on disk in hiberfil.sys, the pagefile, crash dumps, and VM .vmem files you can analyze after the live moment has passed. You learned to acquire it (FTK Imager, WinPmem, DumpIt on Windows; LiME and AVML on Linux), to capture to external media, to hash immediately, and to state honestly what that hash does and does not prove — because a memory image is the one piece of evidence in this book that can never be re-acquired. You built the structural model that makes analysis possible: pages and the DTB-rooted address translation that requires the pagefile, the _EPROCESS doubly-linked list, and pool-tag scanning — the difference between listing and scanning that exposes a DKOM-hidden process. Then you drove Volatility (2 and 3; profiles versus symbol tables; the plugin-name mapping): identifying the image, enumerating processes with pslist/psscan/pstree, reading live connections with netscan, interrogating processes with cmdline/dlllist/handles/getsids, mining registry/credentials/command history with printkey/hashdump/cmdscan/consoles, and — the heart of the chapter — detecting code that touched no disk: malfind for RWX-private injected PEs, ldrmodules for reflectively-loaded DLLs, the multi-signal exposure of process hollowing, and ssdt/callbacks/modules-vs-modscan for kernel rootkits. You extracted executables and data (pslist --dump, memmap --dump, dumpfiles, yarascan, bulk_extractor/aeskeyfind for keys), and you worked the consequential anchor case clinically: a powered-on, BitLocker-locked laptop whose Full Volume Encryption Key — seized from RAM, not compelled from a mind — kept the entire case readable, paired with the legal and ethical duties that govern such power. The technology will keep changing; the method will not — capture before power-off, build the model, scan as well as list, corroborate across structures, and report every finding with its limits intact.
You can now: - Explain why memory forensics is essential — keys, live processes, connections, fileless and injected code — and place RAM correctly in the order of volatility so you capture it before power-off. - Acquire physical memory with the right tool per platform (FTK Imager/WinPmem/DumpIt; LiME/AVML), to external media, with immediate hashing and a defensible chain of custody, and source memory from hiberfil/pagefile/crash dumps/VM
.vmemwhen no live capture exists. - Drive Volatility 2 and 3 — identify the image (imageinfo/windows.info), and enumerate processes, network connections, DLLs, handles, registry, credentials, and console history with the right plugins. - Detect in-memory malware: find injected RWX-private code withmalfind, reflective DLLs withldrmodules, process hollowing by correlating parentage/path/VAD/on-disk comparison, and kernel rootkits by diffing list-walks against pool scans. - Extract executables and data from memory, recover encryption keys (bulk_extractor/aeskeyfind), and feed atimelinerbodyfile into your master timeline. - State the limits of volatile evidence honestly — non-atomic smear, un-re-acquirability, missing symbols, paged-out/overwritten data, and anti-forensics — and recognize when memory is insufficient to support a conclusion.
What's next. Chapter 23 — Network Forensics — follows the beacon you found in memory out onto the wire: packet capture, flow records, protocol analysis, and the artifacts of communication — proving not just what ran on a host, but what it said to the world — and showing once more that technology changes, principles don't.
Practice in exercises.md, test yourself with the quiz, apply it in the case studies, review the key takeaways, and go deeper with further reading.