Blue Team · Hard
Memory Forensics

Master Volatility-based memory analysis — understanding why RAM holds evidence invisible to disk forensics, using pslist vs psscan to detect rootkit-hidden processes, extracting active network connections from memory dumps, detecting process injection via malfind, recovering NTLM credentials from LSASS, and reconstructing attacker command history even after bash history was deliberately cleared.

Hard Blue Team Path ⏱ 26 min read
Learning Progress
0%

Memory Forensics Explained

Memory forensics is the analysis of a computer's live RAM to uncover evidence of malicious activity. Unlike disk forensics, memory analysis reveals what was running at the moment of capture — processes, network connections, decrypted data, injected code, and credentials that never touch the disk.

Sophisticated malware specifically avoids writing to disk to evade antivirus and EDR tools. Fileless malware, process injection, and in-memory payloads can only be found through memory analysis — making it an essential skill for advanced incident response. The evidence on disk is incomplete by design; memory is where the complete picture lives.

🚨Volatility of evidence: Memory is lost permanently when the system powers off. In a live incident, acquiring a memory image before any other action is the first priority. A reboot or shutdown destroys the evidence. This is why memory acquisition appears before investigation and before remediation in the order of operations — you cannot go back.

Why Memory Is a Different Forensic Tier

The order of volatility — the principle that evidence sources should be collected from most volatile to least volatile — places CPU registers and RAM at the top, followed by running processes and network connections, with disk data at the bottom. Memory at the top is not because it's more important but because it disappears first. Disk evidence can wait hours; memory evidence is gone the moment power fails or the system restarts.

Beyond volatility, memory reveals a category of evidence that simply cannot exist on disk. Encryption keys are loaded into memory to decrypt files — the keys may never be written to disk in any form. Injected malware code exists only in the memory of the target process. Credentials cached in LSASS are in memory only. Network connections are kernel data structures in memory that an attacker with admin access can manipulate to hide from the OS — but not from raw memory analysis, which bypasses OS abstractions entirely.

📌 Non-Technical Analogy

Imagine a building where every office has a combination of permanent filing cabinets (disk) and an active whiteboard (RAM). The filing cabinets hold everything that's been officially recorded and stored. The whiteboard holds the work currently in progress — calculations being performed, names and phone numbers being referenced in active conversations, open meeting notes, things that were useful now but haven't been filed yet. A thief who works carefully might leave nothing in the filing cabinets — every document shredded, every trace wiped. But the whiteboard shows exactly what they were doing at the moment they were interrupted. When you turn the lights off (power off), the whiteboard erases. Memory forensics is the science of reading the whiteboard before someone reaches for the light switch.

What Memory Reveals That Disk Cannot

Memory-Only Evidence Sources
Injected code     Malware injected into legitimate process memory -- no disk file
Decrypted data    Encrypted files decrypted in RAM at access time -- key only in memory
Network sockets   Active C2 connections, including those hidden from OS-level netstat
Credentials       NTLM hashes, Kerberos tickets, plaintext passwords in LSASS
Process hollowing Legitimate process shell with malicious payload inside -- no disk trace
Rootkit artefacts Hooks, hidden processes, modified kernel structures in raw memory

Volatility Framework Core Commands

Volatility 3 Essential Plugins
python3 vol.py -f memory.raw [plugin]

windows.pslist      Running processes from EPROCESS doubly-linked list
windows.pstree      Process tree showing parent-child relationships
windows.psscan      Raw EPROCESS structure scan -- finds processes hidden from pslist
windows.netscan     All network connections and listening sockets
windows.cmdline     Full command line arguments for each process
windows.dlllist     DLLs loaded into each process address space
windows.malfind     RWX memory regions with PE headers -- injection detection
windows.hashdump    Extract NTLM hashes from SAM/SYSTEM in memory

Memory Analysis in Practice

Example 01Process list vs raw scan — finding hidden processes

pslist reads the OS linked list — rootkits can remove entries from this list to hide. psscan scans raw memory for EPROCESS structures directly, bypassing the OS. A process in psscan but not pslist is hidden by a rootkit.

python3 vol.py -f memory.raw windows.pstree
648   winlogon.exe
  772  svchost32.exe   (not a real Windows process name)
    1204 cmd.exe         (child of suspicious process)

python3 vol.py -f memory.raw windows.psscan
PID 3321  rootkit_loader.exe  (NOT in pslist -- hidden by rootkit)
# PID 3321 is running but hidden from OS process list
# Only raw memory scan reveals it -- this is the rootkit's persistence mechanism
Example 02Network connections from memory

netscan extracts all network connections from kernel memory structures — including those hidden from netstat by rootkits and connections made before the dump.

python3 vol.py -f memory.raw windows.netscan
Proto  LocalAddr         ForeignAddr         State      PID  Name
TCPv4  10.0.1.55:49221   185.220.101.45:443  ESTABLISHED 772 svchost32.exe
TCPv4  10.0.1.55:49301   10.0.0.5:445        ESTABLISHED 772 svchost32.exe
# svchost32 has C2 connection AND internal SMB connection simultaneously
# C2 beaconing to 185.220.101.45 + lateral movement to 10.0.0.5 in progress
Example 03Process injection detection with malfind

malfind finds memory regions that are executable (PAGE_EXECUTE_READWRITE), not backed by any file on disk, and contain PE headers (MZ signature) — the classic indicators of injected code.

python3 vol.py -f memory.raw windows.malfind
PID   Process      VPN Start  Protect
1204  explorer.exe 0x1f0000   PAGE_EXECUTE_READWRITE
  Hexdump: 4d 5a 90 00  MZ...  (PE header in RWX non-file-backed memory = injection)
# Dump the injected region for malware triage analysis:
python3 vol.py -f memory.raw windows.malfind --dump --pid 1204
# Output: pid.1204.0x1f0000.dmp -- analyse with malware triage workflow
Example 04Extracting credentials from memory

Windows caches NTLM hashes in the LSASS process. hashdump extracts these forensically from a memory image — without running any tools on the live system that might alert the attacker.

python3 vol.py -f memory.raw windows.hashdump
User          NTLM Hash
Administrator 8846f7eaee8fb117ad06bdd830b7586c
jsmith        c4b0e1b10c7ce2c4723b4e2407ef81a2
svc_backup    3b0086cd3a4a36b8f31e25ed5d3219c3
# All NTLM hashes from locally cached logon sessions
# Every account here must be treated as fully compromised
# Hashes can be cracked offline or used directly in Pass-the-Hash attacks
Example 05Reconstructing command history from memory

cmdline reconstructs the full command-line arguments for every process from kernel memory structures — even if the attacker cleared bash history or Event Log 4688 was not enabled.

python3 vol.py -f memory.raw windows.cmdline
772   svchost32.exe  "C:\Users\Public\svchost32.exe" -c 185.220.101.45 -p 443
1204  cmd.exe        cmd /c "whoami && net user /domain && nltest /domain_trusts"
3104  powershell.exe powershell -ep bypass -enc JABzAGgAZQBsAGwA...
# C2 IP and port visible in svchost32 command line
# Domain enumeration: whoami, net user /domain, nltest -- AD recon in progress
# PowerShell encoded payload -- decode for IOC extraction

What You Need to Know

🧠
Volatile Evidence
RAM is lost at shutdown — permanently. Memory acquisition must be the very first action when advanced malware is suspected. Order of volatility: CPU registers → RAM → running processes → disk. Memory first, always.
💌
Process Injection
Malware injects code into legitimate processes to hide. malfind detects this via RWX (PAGE_EXECUTE_READWRITE) memory regions containing MZ/PE headers not backed by any file on disk.
🔍
pslist vs psscan
pslist reads the OS EPROCESS linked list — rootkits remove entries to hide processes. psscan scans raw memory for EPROCESS structures directly — cannot be fooled by OS-level manipulation.
🔑
Credentials in LSASS
LSASS holds NTLM hashes for all locally logged-in users. hashdump extracts these forensically from the memory image. Every extracted hash is a compromised account requiring forced password reset.
🧱
Fileless Malware
Malware running entirely in memory with no disk footprint. Invisible to disk-based AV and forensics. Only detectable through memory analysis (malfind, netscan, cmdline) or behavioural EDR telemetry.
💾
Memory Acquisition Tools
WinPmem, DumpIt, FTK Imager (live), or VM snapshot. Must be acquired from the running system — cannot reconstruct RAM from disk. Timestamp the acquisition: everything in the dump reflects state at that exact moment.

Full Plugin Reference — What to Run and When

A systematic memory forensics investigation follows a structured plugin sequence. Each plugin answers a specific question; together they reconstruct the complete picture of compromise. The sequence matters: start with process enumeration (the inventory), move to network connections (active communication), then drill into injection and credentials.

PluginQuestion AnsweredKey Evidence FieldsWhen to Use
windows.pstreeWhat processes are running, and who spawned them?PID, PPID, process name, start timeAlways first — identifies suspicious parent-child pairs
windows.psscanAre any processes hidden from the OS?Same fields but scanned raw — includes hidden processesAlways — compare to pslist, investigate any discrepancies
windows.netscanWhat network connections exist?Protocol, local/foreign addr, state, PIDAlways — reveals C2 and lateral movement in progress
windows.cmdlineWhat exact commands were processes started with?Full command line per processAlways — reveals C2 IPs in malware args, attacker recon commands
windows.malfindIs code injected into any process?PID, process, virtual address, memory protection, hex dumpWhen injection suspected (suspicious process with C2 connection)
windows.dlllistWhat DLLs are loaded into each process?Base address, DLL path, per-processWhen DLL sideloading or reflective injection is suspected
windows.hashdumpWhat NTLM hashes are cached in LSASS?Username, NTLM hashAfter confirming compromise — maps all accounts at risk
windows.handlesWhat files/registry keys/mutexes does each process have open?Handle type, name, PIDTo identify mutex names (malware family IOC) and open file handles

Process Injection Deep Dive

Process injection is the technique of running malicious code inside the memory space of a legitimate process. It serves two purposes: hiding (the malicious code appears to come from a trusted process like explorer.exe or svchost.exe) and privilege escalation (injecting into a higher-privileged process inherits that process's access token). Memory forensics detects injection precisely because it leaves specific memory signatures that malfind is designed to find.

Classic DLL Injection

Uses WriteProcessMemory to write the DLL path into the target process's memory, then CreateRemoteThread to call LoadLibrary — loading the malicious DLL into the target process's address space.

Memory signature: Unexpected DLL in the target process's DLL list (windows.dlllist) that is not present in other instances of the same process type, and has an unusual disk path (AppData, Temp, ProgramData).

Volatility detection: windows.dlllist --pid [target] shows all loaded DLLs. Any DLL outside System32/SysWOW64 in a system process is suspicious.

Process Hollowing

Creates a legitimate process in suspended state, unmaps (hollows out) its code section, and replaces it with malicious code before resuming. The process table shows the legitimate process name, but it executes malicious code.

Memory signature: The process's base executable image does not match what's on disk. The virtual address of the PE header in memory does not correspond to the file the process supposedly loaded from.

Volatility detection: windows.malfind finds RWX regions with PE headers in processes that should not have them. Dumping with --dump then comparing to disk image reveals the substitution.

Example 06Extracting and analysing an injected payload

After malfind identifies injected code, dump the region and analyse it with the malware triage workflow to extract C2 IOCs and identify the family.

# Step 1: malfind identifies injection in explorer.exe
python3 vol.py -f memory.raw windows.malfind --pid 1204
PID 1204  explorer.exe  0x1f0000  PAGE_EXECUTE_READWRITE
Hexdump: 4d 5a 90 00 03 00 00 00  (MZ header -- PE file injected)

# Step 2: Dump the injected region to file
python3 vol.py -f memory.raw windows.malfind --dump --pid 1204
Saved: pid.1204.0x1f0000.dmp

# Step 3: Analyse the dumped region as a PE file
strings pid.1204.0x1f0000.dmp | grep -E "http|gate|MUTEX|powershell"
http://185.220.101.45/gate.php
Global\CobaltStrikeMutex_4b2f
# Cobalt Strike beacon injected into explorer.exe
# C2 URL and mutex extracted as IOCs
# Hash the dumped PE for VirusTotal lookup and threat intel correlation

Memory Acquisition on a Live Incident

Forensic ScenarioFileless Cobalt Strike — Only Detectable Through Memory

Situation: EDR flags anomalous HTTPS traffic from explorer.exe (PID 1204) to 185.220.101.45 at 300-second intervals on CORP-WS-033. No malicious files detected on disk. AV scan clean. Suspicion: fileless malware operating entirely in memory.

Memory acquisition: WinPmem deployed via EDR remote execution to CORP-WS-033. Memory image acquired (8 GB): corp-ws-033-memory.raw. Timestamp: 2026-05-15 09:14:33. System NOT shut down — memory preserved for analysis. Image transferred to isolated analysis workstation.

windows.pstree: Normal-looking process tree. explorer.exe (PID 1204) present normally. No obviously suspicious process names. windows.psscan shows same results — no hidden processes (this is a stealthy injector, not a rootkit).

windows.netscan: explorer.exe (PID 1204) has ESTABLISHED TCP connection to 185.220.101.45:443 — confirmed. Also shows an internal SMB connection from the same PID to 10.0.0.5:445 (the file server) — lateral movement attempted.

windows.malfind on PID 1204: Finds one RWX memory region at 0x1f0000 with MZ header not backed by any file. Dumped to pid.1204.0x1f0000.dmp. String extraction: http://185.220.101.45/gate.php, Global\CobaltStrikeMutex, cobalt strike shellcode pattern in hex dump. Cobalt Strike beacon injected into explorer.exe confirmed.

windows.cmdline: explorer.exe command line normal. But cmd.exe (PID 3104, child of injected code): whoami && net user /domain && nltest /domain_trusts — AD enumeration commands visible in memory even though they completed and left no persistent log (4688 not enabled on this host).

windows.hashdump: Extracts Administrator hash and two other local account hashes. All three treated as compromised. AD credentials that were used on this host during the session are also in scope for reset.

Outcome: A machine that appeared clean on disk, with AV confirming no threats found, was running Cobalt Strike for an unknown period. Memory forensics was the only analysis technique that revealed the compromise. The injected PE hash, C2 IP, and mutex name are now IOCs for environment-wide hunting. The EPROCESS dump timestamp establishes the earliest proven compromise time for the incident timeline.

Core Concepts Summary

🧠
Acquire First
Memory acquisition before any other action. WinPmem or DumpIt for live acquisition. VM snapshot for virtual systems. Every minute of delay risks reboot destroying evidence. Timestamp the image — all findings reflect that moment.
💌
malfind = Injection Detector
PAGE_EXECUTE_READWRITE + MZ header + no backing file = injected PE. Dump with --dump for malware triage analysis. RWX alone is common; RWX + MZ header is the specific injection signature.
🔍
pslist vs psscan
Run both, compare. Processes in psscan not in pslist = rootkit-hidden. Processes with no parent (orphaned PIDs in psscan) may indicate process hollowing or terminated parent launchers.
🔑
hashdump scope
Every hash in LSASS = a session that was active since last reboot. All must be treated as compromised if the system was in attacker hands. Include service accounts — they often run with high privileges.
🧱
Fileless Malware
No disk file = no AV detection = no EDR file scan detection. Only detectable via memory analysis (malfind/netscan) or behavioural EDR (suspicious process API calls, network from unexpected process). Memory forensics is the definitive technique.
🌐
netscan vs netstat
Rootkits hide connections from OS-level netstat. netscan reads kernel memory structures directly — cannot be fooled. Always use netscan on a dump rather than running netstat on a potentially compromised live system.
📝
cmdline = Command Recovery
Full command-line arguments in memory — even for completed processes, even if 4688 wasn't enabled, even if attacker cleared event logs. The kernel stores process command lines in EPROCESS structures throughout the process lifetime.
🗃️
dlllist for DLL Injection
Unexpected DLLs in system processes loaded from AppData/Temp/ProgramData = DLL injection. Compare DLL list of suspicious process to other instances of the same process type on clean systems to identify anomalies.
Ready to put it into practice?
Proceed to the Lab

You've covered the theory. Now apply it hands-on in the simulated environment.

Start Lab — Memory Forensics
← Return to all labs