Любая инъекция кода в чужой процесс на Windows проходит через строго определённые механизмы управления памятью и планирования потоков. Ядро фиксирует каждое выделение региона, каждое изменение прав страницы и каждое создание потока в структурах данных, доступных для наблюдения. Для защитника это означает: полностью бесследная инъекция невозможна в принципе — вопрос лишь в том, на каком уровне и с какой задержкой эти следы будут обнаружены.
Типичный сценарий инъекции shellcode в чужой процесс состоит из последовательности вызовов WinAPI, каждый из которых оставляет наблюдаемый артефакт:
Каждый из этих шагов порождает артефакты, которые защитник может наблюдать на уровне ядра, через ETW-провайдеры или через EDR-хуки.
Windows отслеживает все выделенные регионы виртуальной памяти через структуру VAD (Virtual Address Descriptor). Каждый вызов
EDR-системы и инструменты вроде Volatility могут перечислить VAD-дерево и найти регионы без backing file с подозрительными правами.
Ключевой индикатор — смена защиты страниц на
Переход
Если защитник получает дамп памяти процесса, shellcode-регион можно идентифицировать по содержимому:
При создании потока через
Поток, чей
Если процесс A создаёт поток в процессе B, и при этом B не является дочерним по отношению к A, не является системным сервисом и не входит в известный паттерн (например,
В некоторых реализациях атакующий создаёт поток в приостановленном состоянии (
EDR-системы, подписанные на
Даже если каждый вызов по отдельности выглядит безобидно, их последовательность в коротком временном окне из одного процесса-источника к одному процессу-цели формирует паттерн:
Корреляция этих событий по
Несколько встроенных провайдеров Windows генерируют события, релевантные для детекта инъекции:
При расследовании подозрительного процесса аналитик может проверить:
Атакующие используют техники, усложняющие детект:
Для каждого из этих обходов существуют соответствующие методы детекта, но они требуют более глубокой интеграции с ядром или использования специализированных ETW-провайдеров.
Что происходит при классической инъекции
Типичный сценарий инъекции shellcode в чужой процесс состоит из последовательности вызовов WinAPI, каждый из которых оставляет наблюдаемый артефакт:
- Перечисление процессов —
CreateToolhelp32Snapshotс флагомTH32CS_SNAPPROCESS, затем итерация черезProcess32First/Process32Next.
- Открытие целевого процесса —
OpenProcessс правамиPROCESS_ALL_ACCESSили комбинациейPROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD.
- Выделение памяти —
VirtualAllocExс флагамиMEM_COMMIT | MEM_RESERVEи начальными правамиPAGE_READWRITE.
- Запись payload —
WriteProcessMemoryкопирует shellcode в выделенный регион.
- Смена прав страницы —
VirtualProtectExпереводит регион вPAGE_EXECUTE_READWRITE(RWX).
- Создание удалённого потока —
CreateRemoteThreadс адресом shellcode в качестве стартовой рутины.
Каждый из этих шагов порождает артефакты, которые защитник может наблюдать на уровне ядра, через ETW-провайдеры или через EDR-хуки.
Артефакты в виртуальной памяти процесса
VAD-дерево и новый регион
Windows отслеживает все выделенные регионы виртуальной памяти через структуру VAD (Virtual Address Descriptor). Каждый вызов
VirtualAllocEx создаёт новый узел в VAD-дереве целевого процесса. Для защитника это означает:- Появление региона с типом
MEM_PRIVATE(не привязан к файлу на диске, не является mapped image).
- Размер региона обычно мал — от нескольких сотен байт до нескольких килобайт, что нетипично для легитимных выделений.
- Регион не имеет backing file: в
MEMORY_BASIC_INFORMATIONполеTypeравноMEM_PRIVATE, аAllocationBaseуказывает на сам регион.
EDR-системы и инструменты вроде Volatility могут перечислить VAD-дерево и найти регионы без backing file с подозрительными правами.
Права страниц: переход в RWX
Ключевой индикатор — смена защиты страниц на
PAGE_EXECUTE_READWRITE. Легитимное ПО крайне редко использует RWX-регионы:- Компиляторы и линковщики разделяют секции:
.text— RX,.data— RW,.rdata— R.
- JIT-компиляторы (CLR, V8) выделяют память как RW, записывают код, затем переключают в RX — но не оставляют RWX.
- Регион, который одновременно доступен на запись и исполнение, — сильный сигнал аномалии.
Переход
PAGE_READWRITE → PAGE_EXECUTE_READWRITE через VirtualProtectEx фиксируется ядром. EDR-системы, работающие на уровне kernel-mode драйвера, могут перехватывать этот переход через хуки на NtProtectVirtualMemory или через подписку на соответствующие ETW-события. Конкретный набор доступных ETW-провайдеров для отслеживания изменений прав памяти зависит от версии Windows и уровня привилегий подписчика.Содержимое региона
Если защитник получает дамп памяти процесса, shellcode-регион можно идентифицировать по содержимому:
- Высокая энтропия (если payload зашифрован или упакован).
- Паттерны, характерные для position-independent кода: отсутствие абсолютных адресов, использование
call $+5/popдля получения EIP/RIP, обращение к PEB черезgs:[0x60](x64) илиfs:[0x30](x86).
- Отсутствие валидных заголовков PE в начале региона.
Артефакты в потоках выполнения
Аномальный адрес старта потока
При создании потока через
CreateRemoteThread ядро заполняет структуру ETHREAD, включая поле StartAddress. Для легитимных потоков процесса этот адрес обычно указывает:- На код внутри загруженного модуля (
.exeили.dll), зарегистрированного в PEB.
- На известную системную функцию (
RtlUserThreadStart,BaseThreadInitThunk).
Поток, чей
StartAddress указывает на регион MEM_PRIVATE без backing file, — прямой индикатор инъекции. EDR-системы проверяют это при каждом событии создания потока.Контекст создания потока
CreateRemoteThread создаёт поток из контекста другого процесса. Ядро фиксирует:ClientIdпроцесса-создателя.
ClientIdцелевого процесса.
- Дескриптор, через который выполнен вызов.
Если процесс A создаёт поток в процессе B, и при этом B не является дочерним по отношению к A, не является системным сервисом и не входит в известный паттерн (например,
csrss.exe, svchost.exe), это вызывает подозрение.Состояние потока до и после инъекции
В некоторых реализациях атакующий создаёт поток в приостановленном состоянии (
CREATE_SUSPENDED), чтобы модифицировать контекст через SetThreadContext перед возобновлением. В этом случае артефактом служит:- Поток, созданный с флагом
CREATE_SUSPENDED.
- Последующий вызов
SetThreadContext, меняющийRIP/EIPна адрес вне загруженных модулей.
- Вызов
ResumeThread.
Артефакты на уровне дескрипторов
Подозрительные права доступа
OpenProcess с PROCESS_ALL_ACCESS (0x1FFFFF) — редкость для легитимного ПО. Чаще встречаются комбинации:| Права | Типичный легитимный сценарий |
|---|---|
PROCESS_QUERY_INFORMATION | Мониторинг, диспетчер задач |
PROCESS_VM_READ | Отладчик, profiler |
PROCESS_VM_WRITE + PROCESS_VM_OPERATION + PROCESS_CREATE_THREAD | Инъекция |
EDR-системы, подписанные на
ObRegisterCallbacks, видят каждый запрос на открытие дескриптора процесса и могут оценить запрошенные права.Последовательность операций
Даже если каждый вызов по отдельности выглядит безобидно, их последовательность в коротком временном окне из одного процесса-источника к одному процессу-цели формирует паттерн:
Код:
OpenProcess → VirtualAllocEx → WriteProcessMemory → VirtualProtectEx → CreateRemoteThread
Корреляция этих событий по
ProcessId источника и цели, по временной метке и по адресному пространству — основа поведенческого детекта.Что видит защитник через ETW и kernel callbacks
ETW-провайдеры
Несколько встроенных провайдеров Windows генерируют события, релевантные для детекта инъекции:
- Microsoft-Windows-Kernel-Process — события создания потоков, включая
StartAddress.
- Microsoft-Windows-Threat-Intelligence (PPL-protected) — расширенные события для EDR-вендоров, включая
VirtualAllocEx,WriteProcessMemory,CreateRemoteThreadс контекстом вызывающего процесса. Доступ к этому провайдеру ограничен защитой PPL (Protected Process Light), что делает его недоступным для пользовательских утилит без соответствующей подписи.
Kernel callbacks
PsSetCreateThreadNotifyRoutine/PsSetCreateThreadNotifyRoutineEx— уведомление при создании потока, включая удалённые потоки. Позволяет EDR-драйверу проверитьStartAddressнового потока и сопоставить его с загруженными модулями целевого процесса.
ObRegisterCallbacks— фильтрация запросов на открытие дескрипторов процессов и потоков. Позволяет оценить запрошенные права доступа до того, как дескриптор будет выдан.
PsSetLoadImageNotifyRoutine— если shellcode пытается загрузить DLL черезLoadLibraryв качестве payload, это событие также фиксируется.
Практическая проверка для аналитика
При расследовании подозрительного процесса аналитик может проверить:
- VAD-дерево — через WinDbg (
!vad), Volatility (vadinfo,malfind) или Sysinternals VMMap. Искать регионыMEM_PRIVATEс правами RWX.
- Потоки процесса — через Process Explorer, WinDbg (
!thread) или API. ПроверятьStartAddressкаждого потока: если он не попадает в диапазон загруженных модулей, это аномалия.
- Дескрипторы — через Process Explorer (вкладка Handles) или
NtQuerySystemInformation. Искать дескрипторы процессов с подозрительными правами.
- ETW-трассировка — собрать трассировку через
logmanилиxperfс включённым провайдером Kernel-Process и проанализировать события создания потоков.
Ограничения и обходы, о которых нужно знать
Атакующие используют техники, усложняющие детект:
- Разделение этапов:
VirtualAllocExиWriteProcessMemoryвыполняются из одного процесса, аCreateRemoteThread— из другого.
- Использование легитимных потоков: вместо создания нового потока атакующий приостанавливает существующий, меняет его контекст через
SetThreadContextи возобновляет. Новый поток не создаётся, ноStartAddressвETHREADне меняется — меняется только регистр RIP в сохранённом контексте.
- APC-инъекция:
QueueUserAPCк существующему потоку в alertable состоянии. Не требуетCreateRemoteThread, но требует, чтобы целевой поток находился в alertable wait.
- Memory-mapped секции: вместо
VirtualAllocEx+WriteProcessMemoryиспользуетсяNtCreateSection+NtMapViewOfSectionс правамиPAGE_EXECUTE_READWRITE. VAD-запись будет иметь типMEM_MAPPED, но backing file может быть в секции\KnownDllsили в pagefile.
Для каждого из этих обходов существуют соответствующие методы детекта, но они требуют более глубокой интеграции с ядром или использования специализированных ETW-провайдеров.
Итоговая карта артефактов
| Этап инъекции | Артефакт | Где наблюдать |
|---|---|---|
| Перечисление процессов | Snapshot handle, итерация | ETW Kernel-Process, EDR behavioral |
| OpenProcess | Дескриптор с PROCESS_ALL_ACCESS | ObRegisterCallbacks, Process Explorer |
| VirtualAllocEx | VAD-узел MEM_PRIVATE | VAD-дерево, Volatility malfind |
| WriteProcessMemory | Содержимое региона без backing file | Дамп памяти, YARA in-memory scan |
| VirtualProtectEx → RWX | Смена прав страницы | EDR kernel hook, ETW (зависит от версии) |
| CreateRemoteThread | Поток с StartAddress вне модулей | PsSetCreateThreadNotifyRoutine, Process Explorer |
