Memory Regions
A process's memory is divided into regions, and each has a type and a protection. Code normally runs from image regions, which hold the program file and its DLLs. Private memory is the program's own working space, normally data that can be read and written but not run. A private region created to run and be written, with no file behind it, is a lead for code that arrived without the loader. Just-in-time compilers make such regions too, so check which program holds it.
What It Is
Every process gets its own private range of memory addresses, its address space. Windows divides it into regions, and marks each with what may be done there, such as read, write or run.
- Why Windows Needs It
- Keeping each program's memory private means one program cannot read or overwrite another's data by mistake. The marks stop a program running what should only be data, or changing its own code.
- An Everyday Example
- When Notepad starts, notepad.exe and its DLLs fill image regions that can run, and a font file it reads sits in a mapped region. The text alice types sits in its heap, private memory it can read and write but not run.
notepad.exe
- Imagenotepad.exe, the programRuns Code
- Imagekernel32.dll, user32.dll and its other DLLsRuns Code
- MappedA font file it readsRead Only
- PrivateIts heap, holding the text alice typesRead and Write
- PrivateA stack for each thread, its scratch padRead and Write
Only notepad.exe sees these addresses. Another program needs a handle with the right to read them.
- Kept by the Kernel
- The kernel keeps each region in the process's virtual address descriptor (VAD) tree, with the protection it was created with. Pages inside a region can take another protection later.
Every region carries a protection, which says whether it can be read, written or run. These are the four you will meet most, and where each one is normal.
Memory protections
| Protection | What It Means | Normal For |
|---|---|---|
| Execute, write copy (PAGE_ | Code that can run, shared by every process that maps the same file. Windows copies a page for this process only if it writes there. | Image regions, as created |
| Read and write (PAGE_READWRITE) | Data that can change but not run | Heaps and stacks |
| Execute and read (PAGE_ | Code that can run but not change | Code pages inside images, as a live tool shows them |
| Execute, read and write (PAGE_ | Memory that can be written and run | Just-in-time compilers, and a lead anywhere else |
Three Kinds of Memory
Every region is image, mapped or private memory. Image regions hold code from files, mapped regions hold data files and shared memory, and private regions hold the heaps and stacks the program works in.
The picture marks each region with the protection it was created with. A live tool shows each part as it is now, so one region can appear as several rows with other protections.
Image
- spoolsv.exe and its DLLs, each from System32Protection When CreatedExecute, write copy
Mapped
- Data files the spooler readsProtection When CreatedRead only
Private
- Heaps and thread stacksProtection When CreatedRead and write
- One region at 0x21C4F3A0000 that starts with MZ (4D 5A) and maps no fileProtection When CreatedExecute, read and write
What Normal Looks Like
On a healthy machine this part looks like this.
- Code runs from image regions mapped from the executable and its DLLs, created as Execute, write copy.
- Heaps and stacks are private memory that can be read and written but not run.
- Programs with a just-in-time compiler, such as .NET programs and browsers, write code into private memory and run it, which is normal for them.
Why It Matters
Code that never came from a file has to live somewhere in memory. Private memory created to run and be written is where injected and reflectively loaded code usually sits, and no process list shows it.
Normal and Suspicious
A clean machine sits beside a suspicious one, and the pointer marks where to look.
Normal
A clean Windows 11 install
| Address | Type | Protection When Created | Mapped File |
|---|---|---|---|
0x7FF6B0000000 | Image (MEM_ | Execute, write copy (PAGE_ | C:\ |
0x1F5C2A10000 | Private (MEM_ | Read and write (PAGE_ | None (no file) |
Suspicious
ws-042,
| Address | Type | Protection When Created | Mapped File |
|---|---|---|---|
0x7FF6B0000000 | Image (MEM_ | Execute, write copy (PAGE_ | C:\ |
0x21C4F3A0000 | Private (MEM_ | Execute, read and write (PAGE_ | None (no file) |
| Protection When CreatedCreated to run and be written, with no file behind it: code normally runs from image regions a file backs. Remote Thread Injection | |||
What a Malicious Example Shows
Each card is what malicious use of this part looks like, and where it shows.
- A private region created to run and be written, starting with an MZ header or with code.Where it shows: windows.
malware. malfind with Notes MZ header or Function prologue, or Windows. System.VAD filtered to executable memory with no file. - Executable memory holding a DLL that no loader list names and no file backs.Where it shows: A private region starting with an MZ header in windows.
malware. ldrmodules, in no list. - An image region whose code in memory differs from its file on disk.Where it shows: Windows.
Memory. Mem2 Disk live. malfind never judges an image region created as Execute, write copy, so it can miss this.
Live and in Memory
Each question has the tools that answer it on a running machine, and in a memory image, a copy of its memory saved to a file.
In a memory image, windows.vadinfo lists each region with the protection it was created with, and windows.malware.malfind picks out executable regions that can be written or hold written pages. Live, Windows.System.VAD and VMMap show each run of pages with the protection it has now.
Which regions can run code, and is a file behind each?
On a Running Machine
Windows.(documentation, opens in a new tab)Live Machine A Velociraptor artifact. It is a ready-made question Velociraptor asks of running machines, named from general to specific. VAD means virtual address descriptors, the records of memory regions. Each run of pages with its protection now and its mapped file. Filter it to executable memory with no file.System.VAD - VMMap (documentation, opens in a new tab)Live Machine A free Microsoft tool that maps one process's memory, region by region. A process's memory by type, with each region's protection.
In a Memory Image
windows.vadinfo(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. vadinfo means virtual address descriptor information. One row per region with the protection it was created with, private or not, and its file.windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. malfind means find malware. Private regions, and shared ones not created as Execute, write copy, that can run and be written, with a note on how each starts.malware. malfind
Does code in an image region match its file on disk?
On a Running Machine
Windows.(documentation, opens in a new tab)Live Machine A Velociraptor artifact. It is a ready-made question Velociraptor asks of running machines, named from general to specific. Compares each module's code in memory with its file, which malfind never does for image regions.Memory. Mem2 Disk
In a Memory Image
windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. Notes no region at the executable's base address, or one not created as Execute, write copy.malware. hollowprocesses
What does a suspect region hold?
On a Running Machine
Windows.(documentation, opens in a new tab)Live Machine A Velociraptor artifact. It is a ready-made question Velociraptor asks of running machines, named from general to specific. YARA rules over process memory, live. A YARA rule describes a piece of malware by patterns in its bytes.Detection. Yara. Process
In a Memory Image
windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. YARA rules over each process's memory regions.vadyarascan
Reading Malfind Results
windows.malware.malfind lists private regions, and shared regions not created as Execute, write copy, that were created to run and be written, or that can run and hold written pages. Its Notes column reads MZ header, PE header or Function prologue when the first bytes match those patterns. That is a hint about what starts the region, not a verdict.
It judges the protection a region was created with, so a region created writable and made executable later shows a harmless creation protection, and a clean result proves nothing. It skips regions that are entirely paged out, moved from memory to the page file on disk, or zero.
Confirm a hit by reading the threads that start in the region, the handles other processes hold to this one, and the loader lists. Then dump the region, with the incident lead's agreement.
What It Misses
Every way of looking has gaps, so cover these from another source.
- malfind judges the protection a region was created with, so a region made executable later looks harmless to it. It also skips any region that was entirely paged out or zero, so a clean result proves nothing. Live, Windows.System.VAD shows the protection each run of pages has now.
- malfind never judges an image region created as Execute, write copy, so code written over a mapped image needs a check of memory against disk. Windows.Memory.Mem2Disk does that live, as Checking for Injected Code says.
- Executable memory no file backs says nothing of who put the code there or when. Read which processes hold a handle to this one, and the remote thread records, Sysmon event 8 or Defender's DeviceEvents, where those run.
False Positives
These look odd but are normal, so rule them out before you act.
- .NET, Java, browsers and other just-in-time compilers create private executable memory, so compare with a clean machine of the same build first.
- Security products place their own code in the processes they watch, and packed programs unpack themselves in memory.
Red Flags It Explains
Each red flag below is confirmed by reading this part.
- Network Connections That Should Not Exist Read its memory regions. A private region created to run and be written, with no file behind it, is a lead, not proof, since just-in-time compilers make them too.
- Remote Thread Injection Find the region that holds the start with windows.vadinfo, which gives its type, the protection it was created with and its file. Then windows.malware.malfind flags private memory created executable and writable, or executable with a written page. A hit is a lead to dump, and a clean result does not clear it.
- Process Hollowing In a memory image, read the region at the image base with windows.vadinfo, since a genuine image is mapped from its file. Then windows.malware.hollowprocesses reports a region that is missing, created with a protection other than Execute, write copy, or at a base the PEB and the kernel disagree on.
Checks That Use It
These checks read this part as one of their steps.
Hunt It
The same hunt is written for four tools, so pick the one you use. Defender XDR and Sentinel queries are in KQL, which you paste into Advanced hunting in the Defender portal or into Sentinel's Logs page. Splunk queries are in SPL, for its Search page, and Velociraptor queries are in VQL, which you run as a hunt.
Needs Defender for Endpoint. Microsoft's table page lists no action types, so confirm these with DeviceEvents | distinct ActionType, and expect them to be recorded selectively. Debuggers and security products touch other processes' memory too, so a rare pair is a lead to read in memory, not proof.
// Memory allocated, or its protection changed, by one process in another, rarest pairs firstDeviceEvents| where Timestamp > ago( 30d)| where ActionType in ( "NtAllocateVirtualMemoryRemoteApiCall", "MemoryRemoteProtect")| where ProcessId != InitiatingProcessId| summarize Devices = dcount( DeviceId), Events = count( ), Actions = make_set( ActionType), FirstSeen = min( Timestamp), LastSeen = max( Timestamp) by InitiatingProcessFileName, InitiatingProcessFolderPath, FileName| order by Devices asc, Events ascSources
- Microsoft Learn: MEMORY_BASIC_INFORMATION structure (opens in a new tab)
- Microsoft Learn: virtual address space (opens in a new tab)
- Microsoft Learn: memory protection (opens in a new tab)
- Microsoft Learn: memory protection constants (opens in a new tab)
- Microsoft Learn: !vprot debugger command (opens in a new tab)
- Microsoft Learn: !vad debugger command (opens in a new tab)
- Microsoft Learn: Data Execution Prevention (opens in a new tab)
- Microsoft Learn: exploit protection reference (opens in a new tab)
- Microsoft Learn: x64 function prologue and epilogue (opens in a new tab)
- Microsoft Sysinternals: VMMap (opens in a new tab)
- Microsoft Sysinternals: Sysmon (opens in a new tab)
- Microsoft Learn: DeviceEvents table (opens in a new tab)
- Volatility 3: windows.vadinfo (opens in a new tab)
- Volatility 3: windows.malware.malfind (opens in a new tab)
- Velociraptor: Windows.System.VAD (opens in a new tab)
MITRE ATT&CK is a free public catalogue of the techniques attackers use. Each technique has an ID, such as T1055 for Process Injection, so defenders can name the same behaviour the same way. Reading this part helps find these techniques.
- T1055 (opens in a new tab) Process Injection
- T1055.002 (opens in a new tab) Process Injection: Portable Executable Injection
- T1620 (opens in a new tab) Reflective Code Loading
- T1055.001 (opens in a new tab) Process Injection: Dynamic-link Library Injection