Windows Memory Management

Virtual Memory & Paging

Programs cannot touch RAM directly, so each program is given virtual memory
Example: when a program says "I want what is at 400 Main Street", Windows checks its translation table and knows where that really is in RAM. The program never knows where its data physically lives, and it does not need to.

Page state

Each state, with its constant, an analogy and whether it can be used:
  • Free (MEM_FREE): An empty parking space with no owner. No: touching it raises an access violation
  • Reserved (MEM_RESERVE): A space with your name on it, but no car. Not yet: nobody else can take it
  • Committed (MEM_COMMIT): A space with the car parked. Yes, if the permissions allow it
c
#include <Windows.h>
#include <stdio.h>

int main() {
    DWORD dwProcessId = 34760;
    HANDLE hProcess = OpenProcess(
        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,  // Permissions we request: query info + read memory
        FALSE,                                        // Do child processes inherit this handle? No
        dwProcessId                                   // PID of the target process
    );

    if (hProcess == NULL) {
        DWORD dwError = GetLastError();
        if (dwError == ERROR_ACCESS_DENIED) {
            printf("[!] No permissions on PID %lu\n", dwProcessId);
        }
        else if (dwError == ERROR_INVALID_PARAMETER) {
            printf("[!] PID %lu does not exist\n", dwProcessId);
        }
        else
            printf("[!] OpenProcess failed with error: %lu\n", dwError);

        return -1;
    }

    MEMORY_BASIC_INFORMATION Mbi = { 0 };
    PBYTE pAddress = NULL;
    // Same as VirtualQuery, but "Ex" = on another process (that is why it takes the handle)
    if (VirtualQueryEx(hProcess, pAddress, &Mbi, sizeof(Mbi))) {
        // Only show committed regions (the ones backed by real memory)
        if (Mbi.State == MEM_COMMIT) {
            printf("0x%p  %10zu bytes  %s  %s  Protect: 0x%08lX\n",
                Mbi.BaseAddress,
                Mbi.RegionSize,
                StateText(Mbi.State),
                TypeText(Mbi.Type),
                Mbi.Protect);
        }

        // Jump to the end of this region = start of the next one
        pAddress = (PBYTE)Mbi.BaseAddress + Mbi.RegionSize;
    }

    CloseHandle(hProcess);

    return 0;
}

Protection

  • PAGE_NOACCESS: Nothing: any access raises an error
  • PAGE_READONLY (R): Read only
  • PAGE_READWRITE (RW): Read and write (normal data)
  • PAGE_EXECUTE_READ (RX): Read and execute (normal code)
  • PAGE_EXECUTE_READWRITE (RWX): Everything
  • PAGE_GUARD (modifier): One-shot alarm

Type

  • MEM_PRIVATE: memory that belongs only to that process. It includes what you reserve with VirtualAlloc, the heap and the stack.
  • MEM_MAPPED: comes from a mapped section that is not an executable, for example with CreateFileMapping + MapViewOfFile. It can be shared between processes. The module comes back to it later with Mapping Injection.
  • MEM_IMAGE: comes from a loaded .exe or .dll. Each PE section is mapped with its own permissions: .text (the code) as RX, the data sections as RW, etc.

Windows protections

DEP (Data Execution Prevention): Windows does not allow code to run in areas marked as data only; if an attacker runs code in an RW area, the program crashes
ASLR (Address Space Layout Randomization): Every time a program starts, Windows places things at different addresses. It is as if the street numbers changed every day. Many attacks need to know exactly where something is in memory; with ASLR that address changes, so the attacker cannot target it without finding it out first.

Memory lifecycle

  • 1. Request memory: malloc, HeapAlloc, LocalAlloc, VirtualAlloc
  • 2. Use it: memcpy. That memory may contain garbage unless you get it zeroed
  • 3. Give it back when you no longer need it: malloc with free, HeapAlloc with HeapFree

Allocating memory

c
// Method 1 - Using malloc()
PVOID pAddress = malloc(100);

// Method 2 - Using HeapAlloc()
PVOID pAddress = HeapAlloc(GetProcessHeap(), 0, 100);

// Method 3 - Using LocalAlloc()
PVOID pAddress = LocalAlloc(LPTR, 100);

Writing to memory

c
PVOID pAddress  = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 100);

CHAR* cString   = "MalDev Academy Is The Best";

memcpy(pAddress, cString, strlen(cString));

Freeing memory

  • malloc → free function.
  • HeapAlloc → HeapFree function.
  • LocalAlloc → LocalFree function.