Windows Memory Management

Virtual Memory & Paging

Los programas no pueden tocar memoria RAM directamente, por eso se le da cada programa una memoria virtual
Ejemplo: Cuando un programa dice "quiero lo que hay en la calle 400", Windows mira su tabla de traducción y sabe que en realidad eso está en tal sitio de la RAM. El programa nunca sabe dónde está físicamente su información, ni le hace falta.

Estado de Página

Cada estado, con su constante, una analogía y si se puede usar:
  • Libre (MEM_FREE): Plaza vacía y sin dueño. No: tocarla da access violation
  • Reservada (MEM_RESERVE): Plaza con tu nombre, pero sin coche. No todavía: nadie más puede cogerla
  • Comprometida (MEM_COMMIT): Plaza con el coche aparcado. Sí, si los permisos lo dejan
c
#include <Windows.h>
#include <stdio.h>

int main() {
    DWORD dwProcesoId = 34760;
    HANDLE hProceso = OpenProcess(
        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,  // Permisos que pedimos: consultar info + leer memoria
        FALSE,                                        // ¿Heredan este handle los procesos hijos? No
        dwProcesoId                                   // PID del proceso objetivo
    );

    if (hProceso == NULL) {
        DWORD dwError = GetLastError();
        if (dwError == ERROR_ACCESS_DENIED) {
            printf("[!] Sin permisos sobre el PID %lu\n", dwProcesoId);
        }
        else if (dwError == ERROR_INVALID_PARAMETER) {
            printf("[!] El PID %lu no existe\n", dwProcesoId);
        }
        else
            printf("[!] OpenProcess fallo con error: %lu\n", dwError);

        return -1;
    }

    MEMORY_BASIC_INFORMATION Mbi = { 0 };
    PBYTE pDireccion = NULL;
    // Igual que VirtualQuery, pero con "Ex" = sobre otro proceso (por eso recibe el handle)
    if (VirtualQueryEx(hProceso, pDireccion, &Mbi, sizeof(Mbi))) {
        // Solo mostramos las regiones comprometidas (las que tienen memoria de verdad)
        if (Mbi.State == MEM_COMMIT) {
            printf("0x%p  %10zu bytes  %s  %s  Protect: 0x%08lX\n",
                Mbi.BaseAddress,
                Mbi.RegionSize,
                EstadoTexto(Mbi.State),
                TipoTexto(Mbi.Type),
                Mbi.Protect);
        }

        // Saltamos al final de esta región = inicio de la siguiente
        pDireccion = (PBYTE)Mbi.BaseAddress + Mbi.RegionSize;
    }

    CloseHandle(hProceso);

    return 0;
}

Protección

  • PAGE_NOACCESS: Nada: cualquier acceso da error
  • PAGE_READONLY (R): Solo leer
  • PAGE_READWRITE (RW): Leer y escribir (datos normales)
  • PAGE_EXECUTE_READ (RX): Leer y ejecutar (código normal)
  • PAGE_EXECUTE_READWRITE (RWX): Todo
  • PAGE_GUARD (modificador): Alarma de un solo uso

Tipo

  • MEM_PRIVATE: memoria solo de ese proceso. Incluye lo que reservas con VirtualAlloc, el heap y la pila.
  • MEM_MAPPED: viene de una sección mapeada que no es un ejecutable, por ejemplo con CreateFileMapping + MapViewOfFile. Puede compartirse entre procesos. El módulo lo retoma más adelante con Mapping Injection.
  • MEM_IMAGE: viene de un .exe o .dll cargado. Cada sección del PE se mapea con sus permisos: .text (el código) como RX, las de datos como RW, etc.

Protecciones de Windows

DEP (Data Execution Prevention): Windows no deja ejecutar código en zonas marcadas solo para datos, si un atacante ejecuta código en zona RW se cuelga el programa
ASLR (Address Space Layout Randomization): Cada vez que aranca un programa. Windows coloca las cosas en direcciones distintas. Es como si cada día cambiaran los números de las calles. Muchos ataques necesitan saber exactamente dónde está algo en memoria; con ASLR esa dirección cambia, así que el atacante no puede apuntar a ella sin antes averiguarla.

Ciclo de vida de la memoria

  • 1. Pedir memoria malloc, HeapAlloc, LocalAlloc, VirtualAlloc
  • 2. Usarla memcpy, esa memoria puede venir con basura a menos que te la den limpia a ceros
  • 3. Devolverla cuando no la necesita la liberas malloc con free, HeapAlloc con HeapFree

Guardar Memoria

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);

Escribir en memoria

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

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

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

Liberar Memoria

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