Информация Сбор поведенческой телеметрии через технологию ETW-Ti.

sashka123555

Участник
Автор темы
53
61
Заливаю с моего ТГК .Привет. Данный пост я пишу для своего ТГ-канала. Я решил оформить тему в виде статьи на Teletype , так как лимит символов в Telegram немного ограничивает - приходилось делить посты на 2-3 части, что выглядело не очень аккуратно. Возможно, в будущем я куплю Premium.

О чем статья, мистер?​

Сегодня в статье я бы хотел обсудить технологию ETW-Ti. Она в корне отличается от классического использования ETW. Когда малварь обходит обычный ETW, она просто патчит пару байт в библиотеке ntdll.dll внутри своего процесса в пространстве пользователя, чтобы перестать слать отчеты. С ETW-Ti такой фокус физически не прокатит. Этот источник телеметрии зашит глубоко в само мясо ядра Windows. События генерирует не сам процесс, а операционная система, когда этот процесс выполняет опасные действия. Соответственно, из пользовательского пространства дотянуться до этого логгера крайне трудно.

Кстати. Про ETW я вкратце проходился в своей статье на Хабре, кому интересно - можете почитать. Начнем!

Для начала поговорим про стандартный ETW.​

ETW представляет собой встроенную в Windows инфраструктуру трассировки событий. Различные компоненты операционной системы и приложения могут публиковать информацию о происходящих событиях, а другие компоненты, в том числе средства мониторинга и защиты, могут организовать сеанс сбора этих данных и получать интересующую их телеметрию, собственно антивирусу не обязательно самостоятельно внедряться в каждый процесс или устанавливать отдельный перехватчик для каждой интересующей его операции - в некоторых случаях необходимая информация уже может предоставляться самой операционной системой через соответствующих поставщиков событий.
1792166835999.png
Архитектура ETW включает в себя 4 элемента:
  1. Поставщик событий - компонент который создает события ETW. Им может быть, как сама операционная система, так и отдельное приложение или драйвер. Например, компонент системы может сообщить о создании процесса, загрузке образа или другом интересующем событии, задача протокола это сформировать событие и передать его инфраструктуре ETW;
  2. Сеанс трассировки - механизм, через который происходит непосредственный сбор событий. Он связывает поставщиков с системой, которая эти события получает. При запуске сеанса определяется, какие поставщики будут активны и какие события от них необходимо собирать. Полученные данные могут временно находиться в буферах, а затем передаваться потребителю или записываться в журнал;
  3. Потребитель - компонент, который получает события из сеанса трассировки и обрабатывает их. На данном этапе собранная информация начинает использоваться по назначению, например, антивирусный процесс может получить событие о создани нового процесса, извлечь из него идентификатор процесса, путь к исполняемому файлу и другие данные, после чего передать информацию своему механизму анализа;
  4. Контроллер - компонент отвечающий за управление сеансом трассировки. Через него можно запустить или остановить сеанс, добавить или отключить поставщиков, а также изменить параметры сбора событий.

ETW-Ti.​

ETW-Ti(ETW Threat Intelligence) - это технология которая разработана Microsoft для качественных EDR-решений. Она является источником телеметрии, сама же технология работает глубоко внутри ядра Windows и создана для того , чтобы отслеживать выполнение подозрительных действий в системе - вроде аллокация виртуальной памяти, изменение прав защиты памяти, маппинг файла в память и т.д.

Если вы взгляните на старые антивирусы или антивирусы, которые разрабатывают не очень опытные в данной сфере программисты , то заметите, что им довольно нравится использовать DLL-библиотеки для реализации IAT/EAT-хуков. Впрочем, я не говорю, что этот способ мертв. Например, IAT-хуки можно реализовать непосредственно внутри браузера, перехватывая такие функции, как GetMessage или PeekMessage, и при необходимости изменять обрабатываемые сообщения. Например, это можно использовать, например, для вмешательства в обработку нажатий клавиш и подмены данных, которые получает приложение(а-ля защита от кейлоггера). Но если речь идет об антивирусе, который в 2026 году в большей степени полагается на поведенческий анализ и корреляцию событий, то строить мониторинг исключительно на таких хуках, по мнению автора, довольно сомнительная идея. Ведь есть столь прекрасная технология, как ETW-Ti. Начнем углубляться.

Так , мы знаем, что данный источник телеметрии позволяет отслеживать определенные события. Но какие именно события нас интересуют в первую очередь? В целом, ETW-Ti предоставляет довольно интересный набор данных, связанных с операциями над виртуальной памятью, потоками и другими действиями внутри системы , самое интересное здесь то, что подобные операции могут встречаться как в обычной работе программ, так и при реализации различных техник внедрения кода в чужой процесс. Соответственно, для защитного ПО сама по себе отдельная операция не всегда является чем-то подозрительным, однако в совокупности с другими событиями она уже может представлять определенный интерес. Разберем самые интересные из них(события) -
  1. 0x01 - KERNEL_THREATINT_TASK_ALLOCATE_VIRTUAL_MEMORY | NtAllocateVirtualMemory - выделение виртуальной памяти локально ИЛИ удаленно.
  2. 0x02 - KERNEL_THREATINT_TASK_PROTECT_VIRTUAL_MEMORY | NtProtectVirtualMemory - изменение прав защиты памяти.
  3. 0x03 - KERNEL_THREATINT_TASK_MAPVIEW | NtMapViewOfSection - отображение секции(файла) в память процесса.
  4. 0x04 - KERNEL_THREATINT_TASK_QUEUEUSERAPC | NtQueueUserAPC - постановка асинхронного вызова процедуры APC в поток другого процесса.
  5. 0x05 - KERNEL_THREATINT_TASK_SETCONTEXTTHREAD | NtSetContextThread - жесткое изменение контекста потока. Ядро генерирует это событие, когда кто-то пытается переписать регистры процессора(типа RIP/EIP или отладочные DR0-DR7) в чужом потоке.
  6. 0x06 - KERNEL_THREATINT_TASK_GETCONTEXTTHREAD | NtGetContextThread - чтение контекста потока.
  7. 0x07 - KERNEL_THREATINT_TASK_READVM | NtReadVirtualMemory - чтение памяти другого процесса.
  8. 0x08 - KERNEL_THREATINT_TASK_WRITEVM | NtWriteVirtualMemory - прямая запись в память чужого процесса.
  9. 0x09 - KERNEL_THREATINT_TASK_SUSPENDRESUMETHREAD | NtSuspendThread / NtResumeThread - заморозка или отморозка конкретного потока.
  10. 0x0A - KERNEL_THREATINT_TASK_SUSPENDRESUMEPROCESS | NtSuspendProcess / NtResumeProcess - приостановка работы всего процесса сразу.
В Windows при вызове условной ReadProcessMemory последовательность вызовов в итоге уходит глубоко внутрь ядра. В типичном случае пользовательская функция проходит через системные библиотеки и приводит к вызову NtReadVirtualMemory из ntdll.dll , которая передает управление ядру через syscall-вызов. Уже внутри ядра выполняются необходимые проверки доступа к целевому процессу , а его дескриптор разрешается в соответствующий объект процесса. Дальше управление переходит к внутренним функциям ядра, которые отвечают за чтение виртуальной памяти. На исследуемой сборке Windows в этом пути можно встретить MiReadWriteVirtualMemory, однако конкретную последовательность вызовов стоит проверять отдельно.
1792166520718.png
Непосредственно внутри функции MiReadWriteVirtualMemory и реализуется сбор телеметрии через специальную внутреннюю структуру в ядре, которая называется EtwpThreatIntelProvider. Когда MiReadWriteVirtualMemory делает свою работу, она параллельно вызывает функцию логирования - EtwTiLogReadWriteVm. Та собирает всю необходимую информацию(кто читал, кого читал, сколько байт и какой пользовательский call-стэк при этом был) и пытается передать это добро дальше.
1792166556638.png
Непосредственно внутри EtwTiLogReadWriteVm вызывается системная функция ядра EtwpEventWriteFull. Она смотрит на этот самый EtwpThreatIntelProvider и проверяет -
  1. Если провайдер активен и к нему подключен EDR(сессия трассировки запущена), то все собранные данные успешно улетают в системные буферы ETW, откуда их потом забирает антивирус.
  2. Если сессии нет - вызов просто завершается ведь телеметрию просто некому передавать.
1792166579239.png

​
C++:
if ( KeGetCurrentThread()->PreviousMode == 1 )
  {
    if ( a4 == 16 )
      JUMPOUT(0x14093B816LL);
    if ( EtwProviderEnabled(RegHandle: RegHandle, Level: 0, Keyword: 0xC0000u) != 0 )
    {
      if ( a2 == a3 )
      {
        v10 = 0;
        v11 = (const EVENT_DESCRIPTOR *)&RtlSystemOverrideInformation;
      }
      else
      {
        v10 = EtwProviderEnabled(RegHandle: RegHandle, Level: 0, Keyword: 0x200000000uLL);
        v11 = &PfSnEvt_GetReadLists_Stop;
      }
      v12 = v11;
Непосредственно такие вызовы осуществляются везде, откуда ETW-Ti собирает телеметрию, также посмотрим внутрь функции MiMapViewOfSectionExCommon и заметим абсолютно тоже самое:
1792166609227.png
Как видно на скриншоте, в самом конце успешного маппинга секции ядро точно так же вызывает внутренний логгер(выделен красным). На этом со сбором телеметрии закончу. Теперь поговорим касаемо того, как вообще происходит использование ETW-Ti и непосредственно его обход.

Регистрация ETW-Ti.​

Вышел я подмечал, что данная технология используется в частности антивирусным защитным ПО, которому важно обеспечивать сбор данных поведенческим способом. Microsoft не выдают документацию данной технологии налево и направо, вследствие, чтобы получить документацию - компания должна пройти жесткий аудит, доказать, что разрабатывает именно защитное ПО(EDR/антивирус), и заключить с Microsoft специальное соглашение, также пользовательский сервис должен обладать статусом защиты AM-PPL. Впрочем, мутная и сложная тема.

Сэр, а может, теперь про обход?​

Теперь про сам обход. Сразу скажу, что из пользовательского пространства обойти данную технологию не выйдет(т.е без драйвера), разработчик в любом случае под рукой должен иметь либо свой с нуля разработанный драйвер , который маппится в пространство ядра через индивидуальный маппер(аналог kdmapper-а), либо, реализовать этот обход через коммуникацию с BYOVD-драйвером через простые IOCTL-запросы.

Если выбирать вариант с маппером своего драйвера, то все получается в разы проще. От уязвимого драйвера-донора нам нужен самый минимум - условно, просто примитив для выделения и маппинга памяти, чтобы один раз подсунуть и запустить наш собственный код в ядре. А вот во втором случае(пытаться сделать все чисто через IOCTL чужого драйвера, без маппинга своего) начнется геморрой. Там должно совпасть слишком много вещей - нужно найти живой драйвер со специфическими примитивами произвольного чтения-записи произвольной памяти.

Теперь про сам обход. Для начала нужно понять, как ядро администрирует текущие сессии трассировки, в результате чего мы натыкаемся на недокументированную структуру WMI_LOGGER_CONTEXT. Каждая активная сессия описывается данной структурой внутри ядра, манипулируя ее полями, атакующий драйвер может перенастроить правила игры. Внутри данной структуры есть интересные поля, например -
  1. LoggerMode - данное поле описывает флаги конфигурации сессии. Например, если сбросить биты отвечающие за доставку логов в реальном времени, защитное ПО просто перестанет оперативно получать события.
  2. AcceptNewEvents - Булево поле. Тоже можно к чертям выставить на 0, в результате чего, получаем то, что получаем - новые сессии больше не принимаются.
Теперь поговорим про вложенную структуру TRACE_ENABLE_INFO. Внутри ядра зарегистрированные провайдеры(через ETW_GUID_ENTRY) связаны с конкретными сессиями логгеров. И состояние каждого такого подключения описывается структурой TRACE_ENABLE_INFO. Она содержит базовые настройки фильтрации - уровень логирования(Level) и маски (MatchAnyKeyword / MatchAllKeyword)
C++:
typedef struct _TRACE_ENABLE_INFO {
    ULONG     IsEnabled;
    UCHAR     Level;
    UCHAR     Reserved1;
    USHORT    LoggerId;
    ULONG     EnableProperty;
    ULONG     Reserved2;
    ULONGLONG MatchAnyKeyword;
    ULONGLONG MatchAllKeyword;
} TRACE_ENABLE_INFO, *PTRACE_ENABLE_INFO;
1792166682206.png
Внутри вы можете наблюдать поле IsEnabled. Когда ядро вызывает функцию EtwProviderEnabled под капотом оно идет в данную структуру и проверяет, равен ли флаг данного поля единице или нет, если нет - весь механизм сбора телеметрии сворачивается до того, как начнет упаковывать данные операции с памятью и т.д.

Пару слов под конец.​

Данная статья вышла довольно маленькой. Все таки пост для ТГ-канала и я решил не описывать каждую функцию, каждую структуру максимально подробно. На Teletype я решил написать статью потому-что Павел Дуров слишком жадный со своими символами... Надеюсь читателю данный пост был полезен. Ожидаю фидбек!
 
  • Нравится
Реакции: вайега52 и romacaddy