Introduction

This module introduces the fundamentals of Windows memory management from a programming perspective. Understanding how Windows represents, protects, allocates, and releases memory is important when writing native Windows programs, debugging process behavior, analyzing malware, and understanding how security products observe suspicious memory activity.

Virtual Memory & Paging

Modern operating systems do not allow normal user-mode processes to directly access physical memory, meaning RAM. Instead, each process works with its own virtual address space. The operating system and processor translate virtual addresses into physical memory locations when needed.

This design provides several important benefits:

Virtual memory relies on paging, where memory is divided into fixed-size blocks called pages. On most Windows systems, normal pages are 4 KB, although other page sizes can exist depending on the processor and configuration. Instead of managing memory as one large continuous block, Windows manages memory page by page, which makes it easier to track what is committed, what is currently resident in RAM, what belongs to a file mapping, and what can be moved to disk.

Paging is used because physical memory is limited. A process may reserve or commit large ranges of virtual memory, but that does not always mean every byte is immediately backed by physical RAM. Windows can keep frequently used pages in RAM and move less-used pages elsewhere, such as the paging file, when memory pressure increases. This allows the system to support more processes and larger virtual address spaces than would be possible if every allocation had to remain permanently in RAM.

The exact page size and allocation granularity can be retrieved using GetSystemInfo, which fills a SYSTEM_INFO structure. For example:

SYSTEM_INFO SysInfo = { 0 };

GetSystemInfo(&SysInfo);

printf("[i] Page Size: %lu bytes\n", SysInfo.dwPageSize);
printf("[i] Allocation Granularity: %lu bytes\n", SysInfo.dwAllocationGranularity);

Furthermore, the image below from the Windows Internals 7th edition - part 1 book shows how separate process virtual address spaces can map to physical pages in RAM, shared physical pages, or disk-backed pages.

Virtual-Mem

Virtual Address Space

Each process has its own virtual address space. Two different processes can use the same virtual address, but those addresses do not necessarily point to the same physical memory. For example, Process A and Process B may both have memory at 0x0000000000400000, but these are separate virtual addresses inside separate process address spaces.

A process can inspect its own address space using APIs such as VirtualQuery. This API receives an address and returns information about the memory region that contains it, such as the base address, region size, page state, page protection, and memory type. The returned information is stored in a MEMORY_BASIC_INFORMATION structure. For example:

MEMORY_BASIC_INFORMATION Mbi = { 0 };

if (VirtualQuery(pAddress, &Mbi, sizeof(Mbi)) == 0)
{
    printf("[!] VirtualQuery Failed With Error: %lu\n", GetLastError());
    return -1;
}

printf("[i] Base Address : 0x%p\n", Mbi.BaseAddress);
printf("[i] Region Size  : %zu\n", Mbi.RegionSize);
printf("[i] State        : 0x%0.8X\n", Mbi.State);
printf("[i] Protection   : 0x%0.8X\n", Mbi.Protect);
printf("[i] Type         : 0x%0.8X\n", Mbi.Type);