Вредоносный код, который никогда не касается диска, всё равно вынужден взаимодействовать с механизмами операционной системы. Windows управляет памятью через строго определённые структуры данных, и любое выделение, изменение прав или создание потока фиксируется ядром. Именно эти следы и становятся основой для детекта.
Процессор x86-64 выполняет инструкции только из страниц памяти, помеченных как executable. Чтобы код начал работать, кто-то должен:
Каждый из этих шагов проходит через системные вызовы ядра Windows:
Полиморфные кодировщики — например, shikata_ga_nai из Metasploit — меняют байтовое представление шеллкода при каждой генерации, используют динамический ключ и не привязаны к конкретным регистрам. Это усложняет сигнатурный поиск по содержимому памяти, но не отменяет структурных артефактов: регион всё равно выделен, права всё равно изменены, поток всё равно создан.
Каждый процесс Windows содержит дерево VAD-узлов, описывающих все выделенные регионы виртуальной памяти. Каждый узел хранит базовый адрес, размер, тип выделения (private, mapped, image) и флаги защиты.
Для защитника интересны регионы с комбинацией
Когда регион памяти не привязан к файлу на диске (нет mapping к PE-образу), это означает, что содержимое было записано программно. В VAD-дереве такой регион помечен как
Каждый поток в Windows описывается структурой ETHREAD, связанной с EPROCESS. Защитные решения проверяют:
Таблица дескрипторов процесса содержит все открытые объекты: файлы, ключи реестра, другие процессы, мьютексы, события. Аномальные записи — например, дескриптор на процесс
Снятие полного дампа памяти процесса или всей системы позволяет провести офлайн-анализ. Инструменты вроде Volatility Framework парсят структуры ядра Windows и извлекают:
Преимущество: анализ не зависит от того, активен ли вредоносный код в момент исследования. Недостаток: дамп фиксирует состояние на одно конкретное мгновение и не показывает динамику.
EDR-решения (Endpoint Detection and Response) подписываются на callback-механизмы ядра Windows:
Эти callback-и позволяют фиксировать события в момент их возникновения и коррелировать их: например, последовательность «выделение RWX-региона → запись данных → создание потока с стартовым адресом в этом регионе» формирует цепочку, характерную для shellcode-инъекции.
ETW предоставляет структурированный поток событий ядра и пользовательских провайдеров. Провайдеры вроде
Как уже упоминалось, кодировщики вроде shikata_ga_nai генерируют уникальное байтовое представление при каждом запуске. Метаморфные движки идут дальше: перестраивают граф потока управления, вставляют мусорные инструкции, меняют порядок базовых блоков. Сигнатурный поиск по содержимому памяти против таких техник неэффективен.
Эти техники подменяют содержимое легитимного процесса после его создания. VAD-дерево при этом может выглядеть нормально (backing image присутствует), но фактическое содержимое страниц отличается от оригинального PE-файла на диске. Детект требует сравнения in-memory образа с on-disk файлом — по хешу или побайтово.
Вызов системных функций напрямую через
Продвинутые образцы пытаются удалить хуки EDR из ntdll.dll или kernel32.dll, перезаписывая первые байты функций оригинальными инструкциями. Это обнаруживается по сравнению текущего содержимого модулей в памяти с их образом на диске или с известными эталонными хешами.
При анализе дампа памяти или событий EDR стоит обращать внимание на следующие паттерны:
Memory-based детект не является серебряной пулей, но он закрывает слепую зону, которую оставляет файловый антивирус: код, который существует только в оперативной памяти и никогда не появляется на диске в исполняемом виде. Комбинация структурного анализа VAD, поведенческой корреляции событий и сравнения in-memory образов с дисковыми файлами даёт защитнику устойчивое покрытие против большинства техник fileless-атак.
Почему полностью невидимое выполнение невозможно
Процессор x86-64 выполняет инструкции только из страниц памяти, помеченных как executable. Чтобы код начал работать, кто-то должен:
- Выделить регион виртуальной памяти (или перенаправить существующий).
- Записать в него машинные инструкции.
- Установить флаг исполнения (PAGE_EXECUTE или производные).
- Передать управление на этот адрес.
Каждый из этих шагов проходит через системные вызовы ядра Windows:
NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory, NtCreateThreadEx и их аналоги. Ядро ведёт учёт всех операций в структурах EPROCESS и VAD (Virtual Address Descriptor). Даже если вредоносный код использует прямой системный вызов через syscall/sysenter, минуя user-mode API, ядро всё равно обновляет внутренние таблицы.Полиморфные кодировщики — например, shikata_ga_nai из Metasploit — меняют байтовое представление шеллкода при каждой генерации, используют динамический ключ и не привязаны к конкретным регистрам. Это усложняет сигнатурный поиск по содержимому памяти, но не отменяет структурных артефактов: регион всё равно выделен, права всё равно изменены, поток всё равно создан.
Ключевые артефакты в памяти процесса
VAD-дерево и аномалии прав доступа
Каждый процесс Windows содержит дерево VAD-узлов, описывающих все выделенные регионы виртуальной памяти. Каждый узел хранит базовый адрес, размер, тип выделения (private, mapped, image) и флаги защиты.
Для защитника интересны регионы с комбинацией
PAGE_EXECUTE_READWRITE (RWX). Легитимное ПО крайне редко создаёт такие страницы: компиляторы генерируют код в секциях .text с правами PAGE_EXECUTE_READ, а данные размещают в .data/.bss без права исполнения. RWX-регион в private-памяти процесса — сильный индикатор того, что код был записан и подготовлен к исполнению непосредственно в памяти.Отсутствие backing image
Когда регион памяти не привязан к файлу на диске (нет mapping к PE-образу), это означает, что содержимое было записано программно. В VAD-дереве такой регион помечен как
PrivateMemory = 1. Если при этом регион имеет права исполнения и содержит валидные машинные инструкции — это классический признак shellcode или reflective loading.Аномалии в цепочке потоков
Каждый поток в Windows описывается структурой ETHREAD, связанной с EPROCESS. Защитные решения проверяют:
- Стартовый адрес потока: указывает ли он на регион без backing image или на адрес внутри другого модуля (признак thread hijacking).
- Стек потока: если стек потока не соответствует ожидаемому диапазону для данного модуля, это может указывать на stack pivot или выполнение из нестандартного места.
- Parent process: поток создан процессом, который не является ожидаемым родителем (например,
svchost.exeпорождаетcmd.exeс аргументами загрузки PowerShell).
Handle table и межпроцессное взаимодействие
Таблица дескрипторов процесса содержит все открытые объекты: файлы, ключи реестра, другие процессы, мьютексы, события. Аномальные записи — например, дескриптор на процесс
lsass.exe с правами PROCESS_VM_READ у нестандартного процесса — указывают на попытку чтения чужой памяти (credential dumping).Подходы к анализу
Статический дамп памяти
Снятие полного дампа памяти процесса или всей системы позволяет провести офлайн-анализ. Инструменты вроде Volatility Framework парсят структуры ядра Windows и извлекают:
- Список процессов с их VAD-деревьями.
- Сетевые соединения на момент снятия дампа.
- Загруженные DLL и их хеши.
- Содержимое командных строк процессов.
- Дескрипторы и мьютексы.
Преимущество: анализ не зависит от того, активен ли вредоносный код в момент исследования. Недостаток: дамп фиксирует состояние на одно конкретное мгновение и не показывает динамику.
Поведенческий мониторинг в реальном времени
EDR-решения (Endpoint Detection and Response) подписываются на callback-механизмы ядра Windows:
PsSetCreateProcessNotifyRoutine— уведомление о создании/завершении процессов.
PsSetCreateThreadNotifyRoutine— уведомление о создании потоков.
PsSetLoadImageNotifyRoutine— уведомление о загрузке образов (DLL, драйверов).
ObRegisterCallbacks— фильтрация операций с объектами (открытие процессов, чтение памяти).
Эти callback-и позволяют фиксировать события в момент их возникновения и коррелировать их: например, последовательность «выделение RWX-региона → запись данных → создание потока с стартовым адресом в этом регионе» формирует цепочку, характерную для shellcode-инъекции.
ETW (Event Tracing for Windows)
ETW предоставляет структурированный поток событий ядра и пользовательских провайдеров. Провайдеры вроде
Microsoft-Windows-Kernel-Memory и Microsoft-Windows-Kernel-Process генерируют события выделения памяти, изменения прав и создания потоков. Аналитик может подписаться на эти события и строить детекты на основе последовательностей, не полагаясь на сигнатуры.Что усложняет детект
Полиморфизм и метаморфизм
Как уже упоминалось, кодировщики вроде shikata_ga_nai генерируют уникальное байтовое представление при каждом запуске. Метаморфные движки идут дальше: перестраивают граф потока управления, вставляют мусорные инструкции, меняют порядок базовых блоков. Сигнатурный поиск по содержимому памяти против таких техник неэффективен.
Process hollowing и process doppelgänging
Эти техники подменяют содержимое легитимного процесса после его создания. VAD-дерево при этом может выглядеть нормально (backing image присутствует), но фактическое содержимое страниц отличается от оригинального PE-файла на диске. Детект требует сравнения in-memory образа с on-disk файлом — по хешу или побайтово.
Direct system calls
Вызов системных функций напрямую через
syscall обходит user-mode хуки, которые устанавливают некоторые защитные продукты. Однако kernel-mode callback-и и ETW продолжают работать, поскольку они находятся ниже уровня системных вызовов.Unhooking и обход EDR
Продвинутые образцы пытаются удалить хуки EDR из ntdll.dll или kernel32.dll, перезаписывая первые байты функций оригинальными инструкциями. Это обнаруживается по сравнению текущего содержимого модулей в памяти с их образом на диске или с известными эталонными хешами.
Практические индикаторы для защитника
При анализе дампа памяти или событий EDR стоит обращать внимание на следующие паттерны:
| Индикатор | Что проверяется | Типичная причина |
|---|---|---|
| RWX-регион в private memory | VAD-дерево, флаги защиты | Shellcode, reflective DLL loading |
| Поток со стартовым адресом вне любого модуля | ETHREAD.StartAddress vs. список загруженных образов | Shellcode-инъекция |
| Процесс с VAD, не совпадающим с файлом на диске | Сравнение хешей in-memory и on-disk | Process hollowing |
| Дескриптор на lsass.exe с VM_READ у нестандартного процесса | Handle table, права доступа | Credential dumping |
| Последовательность Allocate → Write → Protect → CreateThread | Корреляция событий ETW/EDR | Классическая цепочка инъекции |
| Командная строка процесса содержит base64 или encoded-параметры | Парсинг командной строки из EPROCESS | Obfuscated PowerShell, certutil abuse |
Ограничения memory-based детекта
- Время жизни артефактов. Если вредоносный код освобождает память после выполнения (например, shellcode стирает себя после создания persistence), дамп, снятый позже, может не содержать улик.
- Шифрование в памяти. Некоторые образцы хранят payload в зашифрованном виде и расшифровывают только на время исполнения, после чего затирают расшифрованную копию.
- Объём данных. Полный дамп памяти 64-битной системы с 32 ГБ RAM — это файл размером в десятки гигабайт. Офлайн-анализ требует времени и ресурсов.
- Ложные срабатывания. JIT-компиляторы (Java, .NET, JavaScript-движки браузеров) легитимно создают RWX-регионы. Детект должен учитывать контекст процесса.
Memory-based детект не является серебряной пулей, но он закрывает слепую зону, которую оставляет файловый антивирус: код, который существует только в оперативной памяти и никогда не появляется на диске в исполняемом виде. Комбинация структурного анализа VAD, поведенческой корреляции событий и сравнения in-memory образов с дисковыми файлами даёт защитнику устойчивое покрытие против большинства техник fileless-атак.
