Skip to content
λmaldev wiki/
pagesHunting Unbacked Executable Memory
T1055WindowsSigmaVolatilityHunting

Hunting Unbacked Executable Memory

updated 2026-07-148 min readthehackersbrain
Authorized use only. This material is published for detection engineering, malware analysis and authorized red-team engagements. Running these techniques against systems you do not own or have written permission to test is illegal.

Overview

Most injection techniques converge on the same end state: code executing from memory that no file on disk accounts for. Hollowing, reflective loading, shellcode stagers and manual mappers differ enormously in how they get there and barely at all in what they leave behind.

That makes unbacked executable memory the most economical thing a defender can hunt for. One check covers a whole category, and the techniques that evade it — module stomping above all — have to give up something else to do so.

Caution.

This hunt has a real false-positive population: .NET, Java and every JavaScript engine generate private executable memory as a matter of course. Baseline those processes explicitly rather than raising the threshold until the alert goes quiet.

The hunt

  1. 1
    enumerate committed regions
    Walk the VAD tree for every process and keep MEM_COMMIT regions.
  2. 2
    filter for executable protection
    Keep PAGE_EXECUTE_*, discard everything read-only or writable-only.
  3. 3
    check the backing file
    A region with no FILE_OBJECT behind it is private executable memory.
  4. 4
    score the survivors
    Rank by size, entropy, presence of an MZ header and whether a thread starts inside it.

Running it

hunt.shbash
# offline: score every private executable region in a memory image
vol -f capture.raw windows.malfind --dump

# live triage: same idea, from the running box
Get-Process | ForEach-Object {
# regions with Protect = PAGE_EXECUTE_READWRITE and Type = MEM_PRIVATE
Get-ProcessMemoryRegion -Id $_.Id |
  Where-Object { $_.Type -eq 'Private' -and $_.Protect -match 'EXECUTE' }
}

Score, do not alert. A 4 KB private RX region in a browser is noise; a 60 KB one carrying an MZ header in svchost.exe, with a thread starting inside it, is an incident.

Detection

SYSMON EID 8
Remote thread whose StartModule is null — the cheapest continuous approximation of this hunt.
ETW-TI
Allocation or protection change producing executable private memory in another process.
MEMORY SCAN
Volatility malfind, tuned to drop the known-good JIT population before scoring.
BEHAVIOURAL
Signed system binaries — svchost, rundll32, dllhost — are never expected to host private RX memory.
Was this page useful?edit this page ↗