Дисклеймер.
Данная статья была написана лишь потому, что у многих людей складывается ложное представление о современном антивирусном ПО. Автор в статье не распространяет какую-либо коммерческую тайну. Вся информация, которую я познал, была изучена путём анализа современных движков.
Введение.
Время, когда антивирусное ПО основывалось лишь на статическом, или сигнатурном, анализе, уже далеко в прошлом. Хотя в 2026 году оба вида анализа по-прежнему используются, они применяются не так активно, как раньше. В далёком прошлом антивирусное ПО могло делать выводы, основываясь лишь на анализе таблицы импорта, что, соответственно, вызывало множество ложных срабатываний. В результате этого появился термин "поведенческий анализ". Его основная идея заключается в том, что антивирусное ПО оценивает не только содержимое конкретного файла, но и действия, которые выполняет запущенный процесс. Система собирает телеметрию: отслеживает создание дочерних процессов, работу с файлами и реестром, загрузку библиотек, сетевую активность, попытки получить доступ к другим процессам и другие события. Каждому наблюдаемому действию или их комбинации может соответствовать определённое правило. Если процесс выполняет операцию, характерную для вредоносного ПО, ему увеличивается условный уровень подозрительности.
Рассмотрим примитивный пример в контексте поведенческого анализа. В системе появляется новый процесс с именем winloggon.exe - уже здесь внимательный наблюдатель заметит проблему, легитимный процесс называется winlogon.exe, а одна лишняя буква в имени для вируса вполне может оказаться попыткой замаскироваться под системный процесс под невнимательность пользователя. После инициализации наш герой совершает первую ошибку: обращается к реестру и прописывает себя в Run.
Ошибка первая. Использование банального реестрового механизма автозапуска. В этот момент защитный драйвер антивируса, заранее зарегистрировавший обработчик реестровых операций через CmRegisterCallbackEx, мгновенно фиксирует попытку записи в ветку HKCU\...\Run. Изменение ключа выполнено, соответсвенно первичное событие телеметрии сформировано. Следующим шагом он загружает прямиком с диска чистую библиотеку.
Ошибка вторая. Антивирусный мини-фильтр файловой системы получает возможность наблюдать операции с файлами в виде IRP. Создание, открытие, запись, переименование - всё это уже находится в поле зрения системы защиты. Если файл появился в подозрительном месте, был создан неизвестным процессом, а затем сразу загружен этим же процессом, у нас появляется ещё одно событие. Но допустим, наш вирус достаточно глуп, чтобы пойти дальше. Он устанавливает соединение с удалённым сервером.
Ошибка третья. После чего в игру вступает антивирусный WFP драйвер - Windows Filtering Platform. Сетевой стек Windows позволяет системе защиты получать информацию о сетевых операциях и принимать решение, разрешать соединение или нет. В результате winloggon.exe, который только что прописал себя в автозагрузку, создал подозрительный файл и теперь пытается установить соединение наружу, оставляет ещё один вполне заметный след.
В результате чего основываясь на сборе телеметрии, начислении баллов, мы получаем не одно определенное событие, а полноценную законченную картину:

Антивирусу уже не обязательно знать, что winloggon.exe является конкретным известным образцом вредоносного ПО. Достаточно увидеть, что процесс ведёт себя крайне подозрительно - создаёт себя в каталоге пользователя, прописывает путь к себе в Run, загружает библиотеки с целью обхода хуков, пытается получить доступ к чужому процессу, устанавливает сетевое соединение и после этого начинает массово изменять пользовательские файлы. Каждое действие само по себе ещё ничего не доказывает. Но когда они происходят в определённой последовательности и принадлежат одному процессу, ситуация становится значительно интереснее.
Возникает логичный вопрос, как работает поведенческий анализ под капотом? В большинстве антивирусных решений это реализовано примерно следующим образом: для упрощённого представления корреляционный движок можно представить как систему весов - отдельным событиям и их комбинациям условно назначается определённый уровень риска. Если суммарный показатель достигает установленного порогового значения, приложение признаётся подозрительным и отправляется в детект. При этом в контексте поведенческого анализа существуют такие понятия, как корреляция событий, сбор телеметрии и её отправка на сервер для дополнительного анализа. Но об этом подробнее далее в статье.
Источники телеметрии.
Источников для сбора информации в Windows существует достаточно много. Современное антивирусное ПО обычно использует несколько компонентов, работающих как в пользовательском режиме, так и в режиме ядра. В состав продукта может входить несколько драйверов, каждый из которых отвечает за собственный источник телеметрии или механизм защиты, соответственно через эти компоненты антивирус получает возможность наблюдать за событиями, происходящими в системе. Драйверов режима ядра, например, может быть как пять, так и всего один. Их разделение на несколько отдельных компонентов может быть обусловлено различными причинами - архитектурой самого продукта, разделением ответственности между компонентами, использованием разных механизмов или необходимостью изолировать отдельные функции защиты друг от друга.
Пользовательский режим.
Источники телеметрии существуют не только в драйверах ядра. Часть информации антивирусное ПО может получать непосредственно из пользовательского режима. Одним из наиболее известных механизмов является AMSI, или Antimalware Scan Interface. Его основная задача заключается в том, чтобы позволить различным приложениям передавать потенциально опасное содержимое антивирусному ПО ещё до его непосредственного выполнения. Например, речь может идти о скриптах или других данных, которые сами по себе ещё не успели выполнить какое-либо действие в системе. Однако подобные механизмы имеют очевидное ограничение: они работают в адресном пространстве пользовательского режима и потенциально могут стать целью вмешательства со стороны уже запущенного вредоносного процесса. Если процесс получил возможность выполнять произвольный код внутри собственного адресного пространства, он теоретически способен попытаться изменить работу отдельных компонентов защиты, включая находящиеся в его процессе загруженные модули. Именно поэтому современное антивирусное ПО не может полагаться исключительно на такие механизмы. Например, есть популярная техника AMSI-bypass.
Но пользовательский режим не ограничивается только AMSI. Как мы знаем, практически у любого антивирусного ПО есть основной пользовательский процесс, который отвечает за управление различными компонентами защиты: взаимодействие с сервером и загрузки компонентов вроде сканера, например, загрузку обновлений и дополнительных компонентов, коммуникацию с драйверами через IOCTL. Собственно, именно в нём также может быть реализована логика работы с ETW и обработки получаемой через него телеметрии. Если говорить упрощённо, ETW представляет собой встроенную в Windows инфраструктуру трассировки событий. Различные компоненты операционной системы и приложения могут публиковать информацию о происходящих событиях, а другие компоненты, в том числе средства мониторинга и защиты, могут организовать сеанс сбора этих данных и получать интересующую их телеметрию, собственно антивирусу не обязательно самостоятельно внедряться в каждый процесс или устанавливать отдельный перехватчик для каждой интересующей его операции: в некоторых случаях необходимая информация уже может предоставляться самой операционной системой через соответствующих поставщиков событий.

Архитектура ETW включает в себя 4 элемента:
Поставщик событий - компонент который создает события ETW. Им может быть, как сама операционная система, так и отдельное приложение или драйвер. Например, компонент системы может сообщить о создании процесса, загрузке образа или другом интересующем событии, задача протокола это сформировать событие и передать его инфраструктуре ETW;
Сеанс трассировки - механизм, через который происходит непосредственный сбор событий. Он связывает поставщиков с системой, которая эти события получает. При запуске сеанса определяется, какие поставщики будут активны и какие события от них необходимо собирать. Полученные данные могут временно находиться в буферах, а затем передаваться потребителю или записываться в журнал;
Потребитель - компонент, который получает события из сеанса трассировки и обрабатывает их. На данном этапе собранная информация начинает использоваться по назначению, например, антивирусный процесс может получить событие о создани нового процесса, извлечь из него идентификатор процесса, путь к исполняемому файлу и другие данные, после чего передать информацию своему механизму анализа;
Контроллер - компонент отвечающий за управление сеансом трассировки. Через него можно запустить или остановить сеанс, добавить или отключить поставщиков, а также изменить параметры сбора событий.
По мнению автора статьи, ETW нельзя назвать обязательным компонентом архитектуры антивирусного ПО. На 2026 год, по моим наблюдениям, некоторые топовые антивирусные продукты вообще не используют ETW как основной источник телеметрии, оставляя значительную часть поведенческого анализа непосредственно в своих драйверах. Собственно, определённая логика в этом есть - в случае драйвера антивирус получает доступ к событиям раньше и может наблюдать за ними независимо от пользовательского процесса.
Также антивирусное ПО может формировать собственную телеметрию. Например, продукт может использовать динамическую библиотеку(DLL), которая загружается в создаваемые процессы и устанавливает внутри них собственные перехватчики. После загрузки в адресное пространство нового процесса библиотека может начать собирать необходимую информацию непосредственно из его контекста и передавать её основному процессу антивируса(exe-шнику). Для этого могут использоваться разные механизмы межпроцессного взаимодействия, например данные можно передавать через именованные каналы(пайпы), которые позволяют организовать обмен информацией между процессами. Другой же вариант - локальный RPC-сервер, через который основной процесс антивируса может принимать сообщения от загруженных библиотек. В зависимости от архитектуры конкретного продукта это может быть как простой обмен отдельными событиями, так и полноценный канал передачи телеметрии.
Что же касается самих ловушек, то они могут быть разными. Например, раньше вирусы использовали технику хэширования строк с именами функций и последующей загрузки их адресов через перебор EAT в PEB. Данная техника на 2026 год уже хорошо известна разработчикам антивирусного ПО, поэтому возьмём её в качестве примера и рассмотрим, как с ней можно бороться. Например, через технику kernel32trap.
Загружаемой библиотеке в результате поставляется несколько задач:
После загрузки в адресное пространство процесса драйвером начать базовый сбор информации;
По локальному RPC-серверу передать базовую собранную телеметрию в основной модуль антивируса;
Установить все необходимые ловушки. Опять же, как пример - kernel32trap;
kernel32trap - это относительно новая техника направленная против динамического резолва функций через PEB. Идея проста заключается в том, вместо того чтобы заранее искать конкретные вызовы API, антивирус устанавливает специальные ловушки в местах, через которые вредоносное ПО получает доступ к функциям. В результате процесс всё ещё может самостоятельно обратиться к PEB, найти kernel32.dll, пройти по её EAT и вычислить адрес нужной функции, однако последующий переход по этому адресу уже проходит через установленную защитой ловушку. В результате чего антивирусному ПО достаточно перехватить сам факт обращения к интересующей функции и получить контекст происходящего, а именно - какой процесс выполнил переход, откуда он пришёл и какие действия выполнялись непосредственно перед этим. Защитная DLL перехватывает сам факт обращения к интересующей функции, вычитывает контекст происходящего (какой поток выполнил переход, из какого региона памяти пришёл вызов) и оценивает легитимность операции.
Например, до того, как защитная DLL-выставила ловушку, оригинальный код в начале условной VirtualAllocвыглядит стандартно:
kernel32.VirtualAlloc: 48 89 5C 24 08 mov qword ptr [rsp+8], rbx ; сохранение регистров 48 8C 44 24 10 mov qword ptr [rsp+10], rbp ; в теневой стек 4C 89 44 24 18 mov qword ptr [rsp+18], r9 57 push rdi
Вредоносное ПО выполняет обычный CALL по этому адресу. Однако после инициализации защитный модуль находит соответствующую точку в памяти, временно снимает защиту от записи с помощью VirtualProtect и заменяет первые 5 байт(размер опкода JMP + 4 байта смещения). В дизассемблере функция мгновенно превращается в ловушку вида:
kernel32.VirtualAlloc: E9 2B 44 0F 00 jmp av_module.hookhandler ; <---- первый 5 байт затираются хуком 4C 89 44 24 18 mov qword ptr [rsp+18], r9 ; оригинальная функция 57 push rdi
Собственно, реализация подобных ловушек может различаться. В одном случае защита может использовать EAT-хук, в другом - перехват непосредственно в начале самой функции. Для простоты я решил показать принцип работы на примере инлайн-хука. В случае же с EAT-хуком защитный DLL-модуль изменяет сам механизм разрешения адреса экспортируемой функции и получает контроль ещё на этапе получения этого адреса, то есть до непосредственного вызова API. Принципиальных различий в плане сбора телеметрии здесь нет. Основное техническое отличие заключается в том, что антивирус дополнительно переводит страницу памяти, содержащую EAT, в состояние PAGE_NOACCESS / PAGE_GUARD и устанавливает собственный обработчик исключений (VEH) для контроля подобных обращений.
В результате чего DLL-библиотека фиксирует вызов, собирает контекст(какой процесс вызвал, сколько памяти просит) и отправляет телеметрию в главный процесс. На изображении это выглядит так:

Также источниками телеметрии также могут выступать статические сканеры, которые, в частности, могут динамически подгружаться с сервера. Получив файл на анализ, такой сканер может проверить его структуру, импорты, секции, строки, цифровую подпись и другие характеристики, после чего передать результаты основному процессу антивируса для дальнейшего анализа.
Режим ядра.
Если же мы спускаемся на уровень драйверов, то у нас появляется гораздо больше возможностей, нежели на уровне пользователя. Здесь антивирус уже может взаимодействовать непосредственно с механизмами ядра и получать информацию о событиях, которые происходят в системе. В зависимости от задач продукта могут использоваться разные типы драйверов: обычный драйвер режима ядра, WFP-драйвер для работы с сетевой активностью, драйвер файлового мини-фильтра для наблюдения за операциями с файлами и другие специализированные компоненты, также и ELAM-драйвер.
У каждого такого драйвера своя область ответственности. Например файловый мини-фильтр берет на себя задачу отслеживания создания, открытия, записи и удаления/переименование файлов. На подобных драйверах работают многие современные антивирусные решения, в частности механизмы защиты от программ-вымогателей. Их задача - обнаруживать массовое изменение или шифрование файлов, характерное для шифровальщиков, и своевременно блокировать дальнейшие операции. А вот например WFP предоставляет возможность наблюдать за сетевыми соединениями и применять к ним определённые правила, а обычный драйвер режима ядра может использовать различные механизмы уведомлений Windows для получения информации о создании процессов, потоков и загрузке образов. В результате несколько разных драйверов могут одновременно поставлять телеметрию в единый механизм анализа, где эти события уже связываются между собой и используются для определения поведения процесса.

Теперь погрузимся в архитектуру каждого типа драйверов и разберём их подробнее. Начнём с обычного драйвера режима ядра и рассмотрим его возможные точки сбора телеметрии, а закончим ELAM-драйвером, который получает возможность участвовать в защите системы уже на этапе загрузки операционной системы. Никто не запрещает объединить несколько функций в одном драйвере. Например, файловый мини-фильтр вполне может одновременно содержать механизмы защиты от эксплойтов. Разделение таких компонентов - исключительно архитектурное решение. В рамках этой статьи автор намеренно рассматривает их отдельно, чтобы читателю было проще понять назначение каждого механизма и принцип его работы.
Начинать всегда трудно. Главное — ввязаться в бой, а там видно будет» (Антон Чехов).
Стандартный драйвер.
Стандартный драйвер может собирать телеметрию из достаточно большого количества источников непосредственно через механизмы ядра. Один из основных способов для этого - так называемые функции обратного вызова. Смысл заключается в том, что драйвер регистрирует свою функцию, а ядро Windows самостоятельно вызывает её при наступлении определённого события. Функции обратного вызова существуют разного рода и предназначены для совершенно разных событий. Например, через ObRegisterCallbacks драйвер может получать уведомления об операциях с дескрипторами процессов и потоков. Через CmRegisterCallbackEx можно наблюдать за операциями с реестром. PsSetCreateProcessNotifyRoutine позволяет получать уведомления о создании и завершении процессов, а PsSetCreateThreadNotifyRoutine о создании потоков. Отдельно существует PsSetLoadImageNotifyRoutine, через который драйвер может получать уведомления о загрузке исполняемых образов, в том числе DLL, в адресное пространство процесса.
В результате один драйвер может зарегистрировать сразу несколько различных функций обратного вызова и получать достаточно подробную картину происходящего в системе. Например, сначала он может увидеть создание нового процесса, затем загрузку в него подозрительной DLL, после чего зафиксировать обращение этого процесса к реестру и попытку получить дескриптор другого процесса. Для самого драйвера это будут отдельные события, но для антивируса они уже могут стать частью одной цепочки поведения(не раз буду эту цепочку в статье объяснять на примере разных драйверов).
Например, для лучшего понимания разберём hmpalert и посмотрим, как подобные функции обратного вызова используются уже в реальной системе защиты. После создания нового процесса защитный компонент получает информацию о соответствующем объекте EPROCESS и может сохранить необходимые сведения о его начальном состоянии. В частности, можно зафиксировать первичный токен процесса и использовать его как точку сравнения для дальнейших проверок.
После создания первого потока состояние процесса можно проверить повторно. Если за это время изменились связанные с безопасностью данные, защита получает дополнительный повод для анализа. Такой подход позволяет обнаруживать подозрительные изменения, которые могут возникнуть в результате эксплуатации уязвимости с целью повышения привилегий. Попытался привести наглядный пример, где такие функции могут использовать. Автор решил, что не будет рассказывать про каждую функцию, потому что слишком уж много статей на Хабре на эту тему.
Коммуникация драйвера и техника Inverted Call.
Как вы уже поняли, большая часть логики защиты заложена непосредственно в драйверы. Но у антивируса всё так же имеется пользовательский процесс, который должен взаимодействовать с основным драйвером. Их взаимодействие происходит путем отправки из пользовательского режима IOCTL-кодов до основного драйвера для выполнения определенного действия со стороны драйвера. Пользовательский процесс открывает дескриптор устройства, и при необходимости отправляет IOCTL-запросы через функцию DeviceIoControl.
Обычный механизм IOCTL работает по принципу запрос-ответ, инициируемому строго из пользовательского режима. Для антивируса этого мало, ведь ядро постоянно фиксирует события (запуск процессов, запись файлов). Поэтому современные EDR прибегают к технике Inverted Call.
Суть техники заключается в том, что пользовательский процесс антивируса заранее отправляет драйверу ящик пустых IOCTL-запросов (обычно через пул потоков и асинхронный ввод-вывод с использованием OVERLAPPED структур или портов завершения ввода-вывода - IOCP). Драйвер не выполняет эти запросы сразу, а ставит их в очередь и оставляет висеть в состоянии ожидания, возвращая статус STATUS_PENDING. Как только в операционной системе происходит критическое событие, например, срабатывает функция обратного вызова, создания процесса PsSetCreateProcessNotifyRoutineEx - драйвер ядра извлекает один висящий в очереди IRP-пакет, заполняет его буфер телеметрией (PID, путь к файлу и т.д) и завершает операцию вызовом IoCompleteRequest. Потом же пользовательский процесс мгновенно просыпается, забирает готовые данные для анализа, а на место обработанного запроса тут же отправляет новый пустой IRP-пакет, данный цикл повторяется непрерывно, обеспечивая передачу логов ядра наверх в реальном времени с минимальными задержками и практически нулевой нагрузкой на процессор. Обычно для подобного существует отдельный поток в пользовательском режиме.

Инициатором передачи данных формально остаётся пользовательский процесс, но саму передачу фактически инициирует ядро, когда появляется новое событие. Именно поэтому механизм и получил название Inverted Call.
Драйвер-мини фильтра.
Файловые системы предоставвляют операции ввода-вывода для доступа к файлам. Windows поддерживает несколько файловых систем, прежде всего это NTFS.
Фильтрация файловой системы - механизм Windows, позволяющий драйверам перехватывать операции, которые выполняются с файлами. Это используется антивирусами, программами резервного копирования, шифрования и многим другим ПО. Раньше для этого использовались так называемые Legacy фильтровые драйверы. Они могли перехватывать обращения к файловой системе, но разрабатывать такие драйверы было сложнее. Поэтому Microsoft со временем представила новую модель - File System Minifilter. Мини-фильтры работают через специальный Filter Manager, который предоставляет драйверу готовую инфраструктуру для перехвата операций. Например, мини-фильтр может получать уведомления о создании, открытии, чтении, записи, переименовании и удалении файлов.
В результате, если какой-нибудь шифровальщик начинает в тупую и последовательно открывать сотни файлов, читать их содержимое и записывать туда уже зашифрованные данные, мини-фильтр может увидеть эти операции и передать информацию дальше в систему защиты. Типичная структура расположения фильтров в стеке файловой системы показана на следующем рисунке ниже:

Каждый мини-фильтр имеет собственную высоту, по которой определяется его положение в стеке фильтров. Чем выше значение высоты, тем выше располагается сам мини-фильтр. За всем этим следит Filter Manager. Он получает IRP так же, как обычный Legacy-фильтр, а затем передаёт их зарегистрированным мини-фильтрам в соответствии с их высотой. Благодаря этому можно определить, в каком порядке разные мини-фильтры будут обрабатывать одну и ту же операцию файловой системы. Иногда структура стека получается немного сложнее. Например, в нём может находиться устаревший фильтр, который разделяет мини-фильтры на две группы: часть располагается выше него, а часть ниже. В таком случае Windows может использовать несколько экземпляров диспетчера фильтров, каждый из которых управляет своей группой мини-фильтров.
Каждый экземпляр диспетчера фильтров называется кадром. На следующем рисунке показан пример стека, в котором используются два таких кадра.
Загрузка и выгрузка драйвера мини-фильтра.
Драйвер мини-фильтра, как и любой другой драйвер, необходимо сначала загрузить. Если загрузка выполняется из пользовательского режима, для этого используется функция FilterLoad. Ей передаётся имя мини-фильтра, по которому система находит соответствующую запись в реестре, обычно в HKLM\System\CurrentControlSet\Services\<ИмяДрайвера>.
Сам пользовательский процесс при этом не загружает код непосредственно в память ядра. FilterLoad выступает пользовательским интерфейсом для диспетчера фильтров, запрос передаётся в драйвер, где уже используется FltLoadFilter. В результате загружается указанный мини-фильтр и подключает его к своей системе фильтрации. Проверить все загруженные в систему мини-фильтры можно через штатную утилиту fltmc.exe. Если выполнить команду fltmc без дополнительных параметров, она выведет список зарегистрированных и загруженных мини-фильтров. Например, вот вывод на моей ОС Windows 11 последней версии:

Основа драйверов мини-фильтра.
Драйвер мини-фильтра файловой системы имеет функцию DriverEntry, как и любой другой драйвер. Драйвер должен зарегистрироваться как мини-фильтр в диспетчере фильтров, задав различные настройки, такие как, какие операции, которые он хочет перехватить. Драйвер сначала заполняет необходимые структуры, в которых указывает свои параметры и обратные вызовы, а затем передаёт их в FltRegisterFilter для регистрации в диспетчере фильтров. Если регистрация прошла успешно, драйвер выполняет оставшуюся инициализацию: создаёт необходимые объекты, подготавливает внутренние структуры и настраивает дополнительные параметры. После этого вызывается FltStartFiltering, который фактически запускает фильтрацию и позволяет мини-фильтру начать получать операции файловой системы.
Обратите внимание, что драйверу нет необходимости устанавливать собственные процедуры отправки (IRP_MJ_READ, IRP_MJ_WRITE и т. д.). Функция регистрации имеет следующий прототип:
NTSTATUS FLTAPI FltRegisterFilter( [in] PDRIVER_OBJECT Driver, [in] const FLT_REGISTRATION *Registration, [out] PFLT_FILTER *RetFilter );
Требуемая структура FLT_REGISTRATION предоставляет всю необходимую информацию для регистрации. Она определяется так:
typedef struct _FLT_REGISTRATION { USHORT Size; USHORT Version; FLT_REGISTRATION_FLAGS Flags; const FLT_CONTEXT_REGISTRATION *ContextRegistration; const FLT_OPERATION_REGISTRATION *OperationRegistration; PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback; PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback; PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback; PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownStartCallback; PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownCompleteCallback; PFLT_GENERATE_FILE_NAME GenerateFileNameCallback; PFLT_NORMALIZE_NAME_COMPONENT NormalizeNameComponentCallback; PFLT_NORMALIZE_CONTEXT_CLEANUP NormalizeContextCleanupCallback; PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback; PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback; PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback; } FLT_REGISTRATION, *PFLT_REGISTRATION;
В этой структуре хранится основная информация о мини-фильтре: его версия, поддерживаемые возможности, используемые контексты, интересующие операции и функции обратного вызова. Я не буду разбирать все, потому что все таки данная статья является введением, нежели полноценной разработкой антивирусного ПО. Из всех полей здесь особенно важны три:
OperationRegistration - главное поле для нашего случая. Оно содержит массив
FLT_OPERATION_REGISTRATION, где указываются операции файловой системы, которые интересуют драйвер, и функции обратного вызова, вызываемые при их выполнении. Например, можно указать, что нас интересуют создание, открытие, запись или удаление файла;FilterUnloadCallback - функция обратного вызова, которая вызывается при попытке выгрузить мини-фильтр. Здесь драйвер может выполнить необходимую очистку и решить, разрешать ли выгрузку;
InstanceSetupCallback - функция обратного вызова вызываемая при подключении мини-фильтра к тому. С его помощью драйвер может определить, нужен ли ему конкретный том, и разрешить или запретить подключение.
Остальные поля отвечают за более специфические возможности мини-фильтра и для базового понимания его работы сейчас не понадобятся. Главное запомнить OperationRegistration - через него определяется, какие операции файловой системы будет видеть драйвер. Собственно драйвер мини-фильтра должен указывать, какие операции ему интересны. Это предоставляется в мини-фильтре.
typedef struct _FLT_OPERATION_REGISTRATION { UCHAR MajorFunction; FLT_OPERATION_REGISTRATION_FLAGS Flags; PFLT_PRE_OPERATION_CALLBACK PreOperation; PFLT_POST_OPERATION_CALLBACK PostOperation; PVOID Reserved1; } FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;
Сама операция идентифицируется основным кодом функции, многие из которых такие же, как и мы встречались в предыдущих главах: IRP_MJ_CREATE, IRP_MJ_READ, IRP_MJ_WRITE и так далее. Тем не менее, есть другие операции, отождествляемые с основной функцией, которые не имеют реальной основной функции. Эта абстракция, предоставляемая диспетчером фильтров, помогает изолировать мини-фильтр.
Для каждой интересующей его мажорной функции (IRP_MJ_CREATE - открытие/создание, IRP_MJ_WRITE - запись, IRP_MJ_SET_INFORMATION - переименование/удаление/изменение метаданных) минифильтр может зарегистрировать два типа обработчиков:
Post-Operation-Callback(после выполнения операции) - вызывается, когда файловая система уже обработала запрос и вернула результат обратно вверх по стеку драйверов. Компонент защиты использует эту точку, чтобы убедиться, что запись прошла успешно, обновить теневые копии файлов (для их последующего восстановления в случае подтверждения атаки) или зафиксировать аномально высокую скорость модификации документов;
Pre-Operation-Callback(до выполнения операции) - вызывается до того, как запрос дойдет до файловой системы, в этот момент компонент защиты проверяет, какой процесс пытается изменить файл, находится ли он в списке доверенных и не характерно ли его поведение для программ-шифровальщиков). Из этого функция обратного вызова антивируса может мгновенно заблокировать операцию, вернув статус
FLT_PREOP_COMPLETEс ошибкойSTATUS_ACCESS_DENIED.
На практике антивирусу совершенно не обязательно перехватывать абсолютно всё. Например, защите от программ-вымогателей в первую очередь интересны операции, связанные с изменением файлов. Мини-фильтр может отслеживать запись, переименование, создание и удаление файлов, а затем передавать полученные данные в пользовательский процесс или собственный модуль анализа. Представим, что процесс начинает открывать большое количество пользовательских документов и практически сразу перезаписывает их содержимое. Для обычного приложения отдельная операция записи совершенно нормальна. Но если один процесс за короткое время изменил сотни или тысячи файлов, ситуация уже выглядит подозрительно. На данном этапе мини-фильтр становится одним из основных источников телеметрии. Он находится непосредственно в стеке файловой системы и может видеть операции с файлами ещё до того, как они превратятся в обычную статистику поведения. На основе этих событий антивирус может определить характер работы процесса и, если поведение соответствует атаке шифровальщика, заблокировать дальнейшие операции.
Например, подобный принцип используется в защитных механизмах класса CryptoGuard, которые входят в состав hmpalert. Возникает логичный вопрос - как отличить обычную легитимную программу, которая активно работает с файлами, от настоящего шфировальщика? Конечно, цифровая подпись и репутация процесса тоже учитываются, но одной проверки недостаточно. Для этого защитные системы могут использовать технику Copy-on-Write (CoW). Когда процесс начинает массово изменять пользовательские файлы, антивирус не обязательно сразу блокирует эти операции. Вместо этого система может создать копию исходных данных и позволить операции продолжиться под контролем защиты. Если последующие действия процесса действительно начинают выглядеть как массовое шифрование, оригинальные данные остаются доступными для восстановления, а дальнейшие операции процесса могут быть заблокированы. В результате чего защита получает возможность наблюдать за реальным поведением процесса, не полагаясь только на его имя, цифровую подпись.

WFP-драйвер.
Если мини-фильтр позволяет антивирусу наблюдать за операциями с файлами, то WFP-драйвер выполняет похожую задачу уже для сетевой активности. Windows Filtering Platform (WFP) предоставляет ядру набор точек, в которых можно получать информацию о сетевых операциях и принимать решение о дальнейшем прохождении трафика. Антивирус может зарегистрировать собственные обработчики для интересующих его точек WFP. Когда процесс пытается установить соединение, система передаёт обработчику информацию об операции. В зависимости от конкретной точки фильтрации это может быть адрес назначения, порт, протокол, направление соединения и другие параметры.
Например, winloggon.exe внезапно устанавливает исходящее соединение с неизвестным внешним сервером. Само по себе это ещё не означает наличие вредоносного ПО. Но если перед этим процесс прописал себя в Run, загрузил подозрительную DLL, получил доступ к другому процессу, а затем начал сетевое взаимодействие, WFP становится ещё одним источником телеметрии для общей картины. При необходимости WFP позволяет не только наблюдать за соединением, но и вмешиваться в его обработку. Фильтр может разрешить операцию, заблокировать её или передать информацию дальше для дополнительного анализа.

С технической же точки зрения. Драйвер защиты регистрирует собственные фильтры и указывает, какие сетевые события его интересуют. Когда сетевой стек доходит до соответствующей точки, WFP вызывает зарегистрированный обработчик и передаёт ему структуру с параметрами текущей операции. В обработчике драйвер может проанализировать соединение и вернуть соответствующее решение. Например, разрешить операцию через FWP_ACTION_PERMIT или заблокировать её с помощью FWP_ACTION_BLOCK. Для сопоставления события с конкретным процессом можно использовать предоставляемые WFP идентификаторы и дополнительные данные о соединении.
ELAM-драйвер.
На этапе загрузки Windows технология ELAM позволяет антивирусу участвовать в проверке сторонних драйверов ещё до их полноценной инициализации. Драйвер антивируса загружается на раннем этапе запуска системы и регистрирует необходимые функции обратного вызова. Для каждого обнаруженного драйвера антивирус может передать системе информацию о его подлинности и репутации, в том числе сведения о цифровой подписи и хэше файла. На основании этих данных Windows принимает решение о том, разрешать ли загрузку конкретного драйвера. Подробнее описал тут.
Чего не хватает?
На момент 2026 года существует ещё одна серьёзная проблема для разработчиков антивирусного ПО, связанная с безопасностью UEFI. Существует целый класс атак, при которых злоумышленник использует уязвимость в DXE-драйвере или другом доверенном компоненте прошивки, чтобы получить выполнение собственного кода ещё до запуска операционной системы. Подобные атаки особенно опасны тем, что обычные средства защиты Windows в этот момент ещё не работают. Проблема усугубляется тем, что наличие цифровой подписи само по себе не гарантирует безопасность UEFI-модуля. Подписанный компонент может содержать программную уязвимость(например, самую тривиальную - NVRAM Buffer Overflow), которую затем можно использовать для обхода механизмов доверенной загрузки или выполнения произвольного кода. Именно поэтому Secure Boot защищает не от всех возможных уязвимостей, а прежде всего от запуска неподписанных или запрещённых компонентов.
А есть ещё одна проблема - SMM-руткиты. На данный момент я в первую очередь замечаю их использование разработчиками читов, однако вполне вероятно, что со временем подобные техники начнут применяться и в обычном вредоносном ПО. Несмотря на крайне низкий уровень исполнения, полностью невидимыми для операционной системы такие решения быть не обязаны. Как правило, SMM-компонент не существует изолированно - ему необходимо каким-либо образом взаимодействовать с остальными компонентами системы, а значит, остаются точки, по которым потенциально можно построить обнаружение. Например, если архитектура предполагает наличие собственного драйвера в операционной системе и пользовательского компонента, между ними неизбежно возникает канал взаимодействия. Это могут быть устройства, запросы ввода-вывода, разделяемая память и другие механизмы. Сам по себе факт наличия такого драйвера, конечно, ещё ничего не доказывает, однако его поведение уже можно анализировать в совокупности с другими событиями телеметрии.
Отдельный интерес представляет функциональность самого SMM-руткита. Если, например, реализован кейлоггер, система защиты может искать связанные с ним признаки уже на уровне пользовательского режима и ядра - необычные обращения к устройствам, подозрительные драйверы, нестандартное взаимодействие между компонентами, изменения конфигурации платформы и другие косвенные признаки. На этом, думаю все, мини-глава такая.
Сбор телеметрии.
Сбор телеметрии и её дальнейшая отправка на бэкенд может осуществляться, например, в формате XML. Антивирусный агент может использовать легковесную стороннюю библиотеку вроде TinyXML-2, чтобы сформировать XML-структуру с описанием найденных угроз, логов поведения и данных о ПК, после чего упаковать её и отправить на сервер.

При этом событие не обязательно сразу отправляется по сети. Сначала оно может попасть во внутреннюю очередь антивирусного агента. Например, драйвер зафиксировал создание нового процесса и передал в пользовательский режим PID, PPID, путь к образу и время создания. Агент получает эти данные, определяет тип события и формирует из них уже нормализованную структуру. После этого событие может передаваться локальному анализатору или сохраняться в очереди телеметрии. Отдельный поток занимается дальнейшей обработкой и отправкой данных на сервер. Такой подход позволяет продолжать сбор информации даже в том случае, если сетевое соединение временно недоступно. Если событий становится много, отправлять каждое из них отдельным сетевым запросом невыгодно. Поэтому агент может накопить определённое количество событий, объединить их в один пакет, сжать и только после этого передать на сервер.
Рассмотрим простой PoC. Допустим, антивирус получил от своего драйвера информацию о создании нового процесса. В реальной системе структура события будет значительно больше, но для примера нам достаточно PID, имени процесса и пути к исполняемому файлу. Пользовательский агент получает эти данные и формирует из них XML-документ, который затем можно передать в транспортный модуль. Демонстрация:
<event type="ProcessCreate" pid="1337"> <process> <name>winloggon.exe</name> <path>C:\Users\User\AppData\winloggon.exe</path> </process> </event>
В коммерческом антивирусе(например, Sophos, который на скрине выше) подобная структура, конечно не ограничается двумя полями. В неё могут входить идентификаторы процесса и родительского процесса, время события, сведения о пользователе, цифровой подписи, хэше файла и другие данные. После формирования структуры она передаётся дальше по цепочке обработки телеметрии.
Но стоит отметить, что в современных высоконагруженных EDR-системах XML не всегда подходит для передачи больших объёмов телеметрии. Текстовый формат содержит много служебных данных и при большом количестве событий начинает заметно увеличивать объём передаваемой информации. Поэтому для массовой передачи телеметрии чаще используются более компактные бинарные форматы сериализации.
Корелляционный движок.
Корреляционный движок хранит в себе основную логику сопоставления и оценки событий, о которой мы говорили выше. На данном этапе отдельные события телеметрии начинают связываться между собой и превращаются в единую цепочку поведения. У каждого антивирусного продукта может быть собственная реализация корреляционного движка, поскольку конкретные правила, веса событий и способы их обработки являются частью архитектуры самого продукта.
Например, отдельное изменение ключа автозагрузки само по себе ещё не говорит о наличии вредоносного ПО. Однако если после этого тот же процесс создаёт исполняемый файл в каталоге пользователя, загружает неизвестную библиотеку, пытается получить доступ к другому процессу и устанавливает сетевое соединение, корреляционный движок уже рассматривает эти события не изолированно, а как связанные между собой действия. Для этого движок может учитывать идентификатор процесса, родительский процесс, время возникновения события, путь к файлу, цифровую подпись, сетевой адрес и другие параметры. В результате несколько событий, полученных от разных источников телеметрии, объединяются в одну поведенческую цепочку. Каждому событию или комбинации событий может назначаться определённый вес, после чего итоговая оценка используется для принятия дальнейшего решения. Одни события могут повышать уровень подозрительности, другие, наоборот, снижать его, а некоторые комбинации событий могут иметь значительно больший вес, чем каждое из них по отдельности. Благодаря этому система способна учитывать контекст происходящего и отличать обычную активность легитимного приложения от последовательности действий, характерной для вредоносного ПО.

Драйверы, ETW, пользовательские модули и другие источники передают ему отдельные события, которые сами по себе могут быть совершенно безобидными. Однако после сопоставления этих событий между собой система получает уже полноценную картину поведения процесса. На этом рассмотрение архитектуры поведенческого анализа можно закончить. Мы прошли путь от отдельных источников телеметрии до их объединения в единую поведенческую цепочку и увидели, какую роль в этом процессе играют пользовательский режим, ядро Windows, файловая система и сетевой стек.
Защита процессов антивируса. Anti-Tampering.
До этого момента мы рассматривали антивирус как наблюдателя, который собирает телеметрию и анализирует происходящие в системе события. Однако возникает очевидная проблема: что произойдёт, если вредоносный процесс попытается атаковать саму систему защиты? Вредоносному ПО совершенно не обязательно обходить каждое правило поведенческого анализа. Иногда значительно проще попытаться отключить или повредить сам антивирус - ззавершить его пользовательский процесс, изменить его конфигурацию, получить дескриптор к защищаемому процессу или попытаться выгрузить его драйвер.
Для противодействия подобным действиям используются определенные механизмы. Anti-Tampering - защиты компонентов антивируса от несанкционированного вмешательства. Например, обычное завершение процесса через TerminateProcess требует наличия соответствующего права на дескриптор процесса. Поэтому защитный драйвер может использовать ObRegisterCallbacks для контроля операций с дескрипторами процессов и потоков. Если сторонний процесс пытается получить опасные права доступа к процессу антивируса, драйвер может изменить доступ или полностью запретить такую операцию. Однако защита не ограничивается только дескрипторами. Антивирусу необходимо контролировать собственные процессы, файлы, службы, конфигурацию и драйверы. Поэтому Anti-Tampering обычно представляет собой совокупность нескольких механизмов, работающих на разных уровнях системы.
На 2026 же год хакеры в обход это используют BYOVD-драйверы, заставляя драйвер убивать процессы антивирусов. Поэтому, довольно большое количество антивирусов нацелены на детектирование этих же самых BYOVD-драйверов.
Облачная защита.
Локальный анализ не всегда позволяет быстро определить, является ли конкретный файл или поведение вредоносным. Особенно это актуально для новых образцов, которые ещё не успели попасть в локальные базы сигнатур. Поэтому современные антивирусные решения могут дополнять локальные механизмы облачным анализом.
Облачный анализ - это передача данных о подозрительном объекте или поведении с конечного устройства на удалённую серверную инфраструктуру для дополнительного анализа и вынесения вердикта. Например, Microsoft Defender взаимодействует с облачными службами Microsoft, включая Microsoft Active Protection Service (MAPS). Облачная защита дополняет локальный анализ и позволяет использовать информацию, полученную от других систем, для более быстрого выявления новых и неизвестных угроз.
Логика проста. Антивирус получает подозрительный файл или набор событий телеметрии, выполняет локальный анализ и, если имеющейся информации недостаточно для принятия решения, передаёт необходимые данные в облачную инфраструктуру. Серверная сторона может сопоставить полученную информацию с уже известными образцами, репутацией файла, результатами анализа других систем и дополнительными признаками. Главное преимущество такого подхода заключается в скорости распространения информации. Если новый образец был обнаружен на одной системе и получил соответствующий вердикт, информация о нём может использоваться для защиты других систем без необходимости ждать очередного традиционного обновления локальной базы. При этом облачная защита не означает, что каждый файл полностью отправляется на сервер. Конкретный набор передаваемых данных зависит от продукта, типа события и используемого механизма анализа. В некоторых случаях достаточно метаданных, хэша или других признаков, а для более сложного анализа могут передаваться дополнительные данные.
И еще немного про DLL-модули.
Для чего ещё могут использоваться специальные DLL-модули? Например, для защиты браузеров от банковских троянов, кейлоггеров и других вредоносных компонентов, пытающихся перехватывать пользовательский ввод.
Антивирус может внедрять собственный DLL-модуль в адресное пространство браузера и устанавливать необходимые перехватчики. В простейшем случае речь может идти о контроле функций, связанных с обработкой оконных сообщений и пользовательского ввода, например GetMessage, PeekMessage, а также других механизмов Win32, через которые приложение получает информацию о событиях интерфейса. При этом защитному модулю важно не просто зафиксировать сам факт вызова функции, а определить контекст происходящего: какой поток получил сообщение, какому окну оно предназначено, какой процесс является источником события и какие действия выполнялись непосредственно перед этим. Полученная информация передаётся основному компоненту защиты и может использоваться для обнаружения подозрительного поведения. Например, если неизвестный модуль пытается перехватывать события клавиатуры внутри браузера, одновременно обращается к памяти процесса, устанавливает собственные хуки и взаимодействует с сетевым соединением, совокупность таких событий уже может рассматриваться как подозрительная.
Заключение.
Надеюсь, мне удалось изложить в этой статье всё самое важное. Разумеется, многие механизмы были рассмотрены достаточно поверхностно, поскольку эта статья посвящена именно архитектуре современного антивирусного ПО, а не его непосредственной разработке. Если статья получит хороший отклик, возможно, я продолжу эту тему отдельным циклом материалов уже с практической стороны - разработка драйверов, сбор телеметрии, реализация корреляционного движка и другие компоненты антивирусной защиты. Хотя, подозреваю, для большинства читателей это уже будет куда менее увлекательное чтение.
Книги, документации, полезная информация кому это интересно.
Также оставляю набор интересных книг для тех, кто хочет углубиться в тематику антивирусного ПО.
Внутреннее устройство Windows(Windows Internals) / Руссинович, Ионеску;
Программирование драйверов Windows (Windows Kernel Programming) / Павел Йосифович;
Практический анализ вредоносных программ (Practical Malware Analysis) - Майкл Сикорский, Эндрю Хониг;
Реверс-инжиниринг. Практическое руководство (Practical Reverse Engineering) - Брюс Данг, Александр Газилов, Чен Ву, Питер Чин;
Искусство исследования вредоносного ПО (The Art of Memory Forensics) - Майкл Халперин, Джейсон Л. Фармер и др.
Всем пока.
Комментарии (3)

MasterMentor
20.08.2026 05:03Это всё хорошо. Но луче статью писать так: ребята, вот ссылка на репозиторий с дровами, защищающими от антивирусов (список) ваш экзешник. И вот эксплоит для их установки в систему. Всё поддерживается в рабочем состоянии, исключительно в целях распространения знаний. Спасибо за внимание!
А так какое-то пустое теоретизирование (при всём уважении).
ruomserg
Как хорошо что у меня Linux - а не вот это вот всё...
pfemidi
Из интернетов:
Скачал вирусов себе на линух.
Распаковал.
Поставил под root.
Не завелись. Два часа гуглил, оказалось, вместо /usr/local/bin вирусы стали в папку /usr/bin на которую у юзера malware нет прав на запись, поэтому вирус не может создать файл процесса. Нашел на китайском сайте патченый .configure и .make, пересобрал, переустановил.
Вирус заявил, что ему необходима библиотека cmalw-lib-2.0. Оказалось cmalw-lib-2.0 идет под CentOS, а под убунту ее не было. Гуглил два часа, нашел инструкцию как собрать .deb пакет либы из исходников. Собрал, поставил, вирус радостно запустился, пискнул в спикер и сделал core dump.
Час чтения syslog показал, что вирус думал, что у меня ext4 и вызывал ее api для шифрования диска. В btrfs это api deprecated поэтому линукс, заметив это непотребство, перевел раздел в рид-онли.
В сердцах открыл исходники вируса, grep'нул bitcoin кошелек, отправил туда $5 из жалости и пошел спать...