Pages

In the context of memory management, a page refers to a fixed-size block of memory, typically 4KB in size, used by the operating system to manage virtual memory.

Pages

The name "page" is derived from the analogy of dividing memory into fixed-size pages, much like a book is divided into pages. Each page has a specific size, typically measured in kilobytes or megabytes, and serves as the smallest unit of memory that can be allocated or managed by the operating system.

Each process has an associated page table. The page table is primarily a data structure managed by the kernel.

Each process has its own page table that is maintained by the kernel and associated with the process. The process can reference and access its own page table, but it does not directly manage or modify the page table itself.

Normal pages are generally 4KB in size. There are two other types of memory pages in the x86 64bit Windows operating system: large and huge:

The commonly used page sizes in x86 64bit are:

  • Normal Pages: The default page size is 4 KB (4096 bytes).

  • Large Pages: Large pages are larger than normal pages and are generally 2MB.

  • Huge Pages: Huge pages are even larger than large pages and can have a size of 1GB. This has been supported since Windows 10 and Server 2016.

It's important to note that the availability and usage of large and huge pages may vary depending on the specific operating system, hardware configuration, and software requirements.

Page Protection

Windows provides a range of page protection options that can be set on virtual memory pages to control the access permissions and behavior of those pages. These protections are enforced by the Windows memory management subsystem to ensure the integrity and security of the system's memory.

The commonly used types of page protection:

  • PAGE_READONLY : The memory page is read-only, meaning it can be read but not modified. Any attempt to write to the page results in a protection violation.

  • PAGE_READWRITE : The memory page allows both read and write operations. It can be read from and written to without any restrictions.

  • PAGE_EXECUTE_READ: The memory page is executable, allowing the processor to fetch and execute instructions from that page. It can also be read from, but write operations are not allowed.

  • PAGE_EXECUTE_READWRITE: The memory page permits all operations, including reading, writing, and executing instructions. It provides full access to the page for both code execution and data modification.

  • PAGE_NOACCESS: The memory page is inaccessible, preventing any read, write, or execute operations. Attempting to access the page results in a protection violation.

  • PAGE_WRITECOPY: Copy on Write, write access will caue a private copy to be set for the caller.

  • PAGE_EXECUTE_WRITECOPY: as above but with execute support.

  • PAGE_GUARD: Used for dynamically growing a thread's stack.

These protections are enforced by the Windows memory management subsystem to ensure the integrity and security of the system's memory.

It is possible to write to process memory, and it is possible to write to another processes memory provided we have the appropriate security access token. The WriteProcessMemory Win32 API is commonly used for this.

Memory Mapping

In modern computing, data mapping in memory plays a crucial role in managing the efficient execution and storage of various types of data. When a program runs, it needs to access and manipulate different data sets.

In general, a variety of data can be mapped in a processes virtual memory, including:

  • Executable files (such as executable images or PE files) - These are loaded into memory and mapped for execution. This includes DLLs that are loaded into memory and mapped for execution of shared code.

  • Heap memory - This includes both the native heap and managed heap in the context of managed languages like C# or Java.

  • Files - Memory mapping files allows direct access to their contents as if they were part of the process's memory space.

  • Page tables - These are data structures used by the operating system to manage the virtual memory mappings for a process.

  • Thread stacks - Each thread in a process has its own stack space, which is used to store local variables, function calls, and other thread-specific data.

Page States

Memory states can be categorised into three main types: free, committed, and reserved.

Free memory refers to unallocated memory that is available for allocation to processes or applications when requested.

Committed memory is memory that has been allocated and is actively being used by a process. It is backed by physical memory or, in some cases, the paging file. Committed memory is guaranteed to be accessible and page faults are handled transparently.

Reserved memory, on the other hand, is memory that has been reserved for future use but is not yet committed. It allows processes to claim a specific range of memory addresses without immediately consuming physical resources. If we attempt to access these pages an access violation is triggered.

It is common for VirtualAlloc to be used to reserve and commit memory at the same time (using the MEM_RESERVE | MEM_COMMIT flags, and this is fine for small allocations.

It is important to understand that we may want to reserve large areas of memory where we might not need all of it initially and can commit it later in the program:

Reserve and Commit snippet
// reserve memory for a later commitment
char* buffer = (char*)VirtualAlloc(nullptr, 0x40000000, 
    MEM_RESERVE, PAGE_READWRITE);

// create a small buffer
char text[] = "My text";

// commit part of the memory for the small buffer
VirtualAlloc(buffer, sizeof(text), MEM_COMMIT, PAGE_READWRITE);

// copy the small buffer in to the committed memory
strcpy_s(buffer, sizeof(text), text);

This allows us to efficiently reserve memory beyond physical memory restrictions until we need it.

Page Faults

A page fault is an exception that occurs when a program or process attempts to access a page of virtual memory that is currently not in physical memory (RAM) and needs to be brought in from secondary storage (such as a hard disk).

It typically happens when the operating system needs to allocate more memory for a process or when a process tries to access memory that has been paged out to disk. The operating system handles page faults by loading the required page into memory and updating the page tables to reflect the new mapping, allowing the program to continue its execution seamlessly.

NULL Page

The term "NULL page" typically refers to a single memory page with the address 0. This page is commonly used in operating systems to handle null pointer references or as a guard page for memory protection.

In older versions of Windows this could potentially be used to exploit null pointer dereferencing vulnerabilities.

The C language defines the NULL macro as a void pointer to 0:

#define NULL ((void *)0)

Consider the following code:

Null Pointer Dereference
MyPointer = NULL;
// ...
MyPointer->Callback();

The issue occurs when the pointer is assigned a value of NULL or 0, but later executes code by invoking the Callback() function within the pointer structure (without checking for NULL). By exploiting the ability to write to the "NULL page," it becomes possible to inject shellcode and direct the pointer to it at the offset corresponding to the Callback() function in the structure.

In Windows 8 onwards the operating system reserves the first page of the virtual address space (address 0) and intentionally leaves it unmapped. Any attempt to dereference a null pointer, which would typically result in accessing invalid memory, will trigger an access violation exception.

This section introduced the basics of memory. The upcoming sections will describe different types of memory in more detail.

Last updated