1770321094003.webp


Если ты сюда попал, ты уже находишься в одном из двух состояний.

Состояние первое: Ты устал. Устал от бесконечного цикла «скачал PoC с GitHub, запустил, получил детект, пошёл искать следующий PoC». Устал от чувства, что ты работаешь со средствами, механизм которых понимаешь лишь на 10%, а остальные 90% - магия, которая перестаёт работать после очередного патча. Ты подозреваешь, что реальное знание лежит глубже, за стенами реверс-инжиниринга и чтения сырых структур в отладчике. И ты прав.

Состояние второе: Тебе интересно. Не просто «хочу запустить эксплойт», а интересно как оно работает на самом деле. Ты смотришь на EDR не как на чёрный ящик, а как на сложную систему, у которой есть входы, выходы и, что критически важно, слепые зоны. Ты понимаешь, что побеждает не тот, у кого самый крутой 0-day, а тот, кто лучше всех понимает ландшафт. И ландшафт современной Windows - это ландшафт телеметрии, событий и протоколирования. Его столица - ETW.

Этот материал - для тебя в обоих случаях. Это учебник по таксономии тишины. Мы будем разбирать один из самых фундаментальных механизмов операционной системы - Event Tracing for Windows - не с точки "G" администратора или аналитика SOC, а с точки зрения архитектора, который хочет спроектировать внутри этого механизма комнату, где его действия не будут слышны.

В опенсурсе по этой теме есть только осколки:
  • Слайды с конференций, где за 40 минут спикер пролетает от API к кейлоггингу, оставляя тебя с десятком вопросов «как?».
  • Посты в блогах, рекламирующие «новый крутой bypass», который на поверку оказывается вариацией патча EtwEventWrite и работает только против Sysmon в лаборатории 2018 года.
  • Документация Microsoft на 500 страниц, написанная для разработчиков легитимного софта, где умышленно или нет опущены критические внутренние детали.
  • Обсуждения на всяких хабрах, где люди делятся обрывочными фактами: «нашел структуру ETW_REG_ENTRY», «патчил EtwpEventTracingSlot», но без контекста, без методики, без понимания последствий.
Здесь будет полный цикл. Мы начнём с основ: что такое ETW, зачем Microsoft его создавала, какую проблему он решает (и создаёт для нас). Мы погрузимся в архитектуру: контроллеры, провайдеры, консьюмеры, сессии. Мы пройдём по стеку вызовов: от твоего вызова CreateRemoteThread в коде до момента, когда бит информации об этом событии попадает в сокет, ведущий к серверу EDR.

Затем мы перейдём к практической деструкции этого потока. Мы рассмотрим методы не по принципу «от простого к сложному», а по принципу «от внешнего к внутреннему», от user-mode к kernel-mode, от грубого к тонкому. Каждый метод будет сопровождаться:
  • Техническим объяснением (какая структура/функция является целевой).
  • Контекстом уязвимости (почему это вообще возможно? Это фича или баг?).
  • Практической реализацией (код на C/C++, ассемблер, иногда - готовые инструменты).
  • Анализом живучести (насколько это устойчиво к обновлениям, PatchGuard, эвристикам EDR).
  • Рисками и детекцией (как может быть обнаружено, какие артефакты оставляет).
Мы не будем бояться говорить, когда что-то не работает. Мы не будем скрывать сложности.

(0.1) ETW как проблема: Шум как неизбежность, Тишина как аномалия

Чтобы понять, как прятаться, нужно понять, от чего. Современная операционная система - это не статичный код. Это живой организм, который постоянно говорит. Он бормочет себе под нос о каждом своём действии: «создал дескриптор», «отобразил секцию», «отправил пакет». Долгое время этот монолог был приватным. Его можно было услышать, только подсунув отладчик или драйвер прямо в мозг системы - рискованно, нестабильно, заметно.

ETW - это решение Microsoft превратить этот внутренний монолог в публичную трансляцию. Они сказали: «Давайте сделаем это официально, структурированно, эффективно». И они сделали. Теперь у системы есть организованный, высокопроизводительный механизм, чтобы вещать о каждом своём шаге. Это гениально с инженерной точки зрения. Это кошмар с точки зрения конфиденциальности и контроля.

Потому что теперь любой желающий, у кого есть права и понимание, может подписаться на эту трансляцию. Антивирусы, EDR, SIEM-системы - они и есть главные подписчики. Они сидят на критических каналах (Microsoft-Windows-Kernel-Process, Microsoft-Windows-Threat-Intelligence) и жадно поглощают поток событий. Их анализ - это не анализ одного подозрительного вызова. Это корреляция сотен тысяч событий в секунду, поиск паттернов, аномалий, цепочек.

Ключевой сдвиг парадигмы: Раньше мы боролись с хуками. EDR перехватывал вызов NtCreateThreadEx и проверял его параметры. Мы ответили прямыми syscall'ами, обходом хуков. EDR переместился глубже - в ядро, на уровень событий. Теперь неважно, как ты вызвал функцию - через ntdll, через прямой syscall, через Hell's Gate. Сам факт того, что поток был создан, порождает событие в ядре. И это событие утечёт по ETW.

Таким образом, наша задача трансформировалась. Раньше мы старались выглядеть легитимно для хука (передавать правильные параметры). Теперь мы должны не выглядеть вообще. Мы должны влиять не на результат вызова функции, а на побочный эффект - её логгирование.

Отсюда рождается главная этическая (в рамках исследования) и техническая дилемма: Тишина в системе, построенной на шуме, сама по себе является самым громким звуком.

Если EDR видит, что за последние 5 минут в системе не произошло ни одного события от ядерных провайдеров, в то время как пользователь активно работает, - это краснейший из красных флагов. Поэтому наша цель - не глушить ETW полностью (это крик). Наша цель - избирательный шепот. Мы хотим, чтобы наши специфические, редкие, критичные действия растворялись в фоновом гуле миллионов легитимных событий. Мы хотим не отключить микрофон у всей системы, а аккуратно подрезать провода, ведущие к микрофону в той конкретной комнате, где мы говорим.

(0.2) Что тебе понадобится: Инструментарий психопата

Мы будем работать с реальным кодом, часто низкоуровневым. Ты не сможешь просто читать. Тебе придётся делать. Поэтому приготовь:
  1. Изолированная лаборатория: Виртуальная машина (VMware Workstation/Player, VirtualBox) с Windows 10/11. Обязательно отключи интернет и общие папки на хост. То, что мы будем делать, может привести к крашам системы (BSOD) и повреждению ОС. Используй снапшоты.
  2. Среды разработки: Visual Studio 2019+ с компонентами для разработки драйверов (Windows Driver Kit - WDK). Для некоторых экспериментов подойдёт и MinGW.
  3. Отладчики: WinDbg Preview (обязательно с настройкой символов). Для user-mode можно и x64dbg, но для ядра - только WinDbg. Учись им пользоваться. Это наш микроскоп.
  4. Системные утилиты: Sysinternals Suite (особенно Process Monitor, Process Explorer). logman.exe (встроен в Windows). Windows Performance Recorder (WPR).
  5. Исходники и бинарники для изучения: Исходный код TamperETW, SilkETW. Драйверы-примеры из WDK. Дизассемблер (Ghidra, IDA Free - если есть).
  6. Правильный настрой: Готовность к тому, что 80% времени уйдёт на отладку, поиск смещений, анализ дампов и чтение документации. 20% - на момент катарсиса, когда твой код наконец делает систему слепой к твоему действию. Это нормально. Это и есть работа.
В этом руководстве будет сквозная мысль: мы не воюем с системой. Мы её изучаем. Мы находимся по ту сторону баррикад от коммерческих EDR, но мы используем те же методы - глубокий анализ, реверс-инжиниринг, понимание архитектуры. Наша цель - знание. А знание, как известно, сила.

Мы будем относиться к коду Windows с уважением, даже когда мы его разбираем. Это сложная, местами гениальная система. Наш подход - не «взломать и сломать», а «понять и перенаправить». Ирония будет в адрес абсурдности гонки вооружений, а не в адрес разработчиков, которые, в конце концов, тоже пытаются делать свою работу.

Это длинный путь. Но в конце ты не будешь тем, кто запускает чужие скрипты. Ты будешь тем, кто понимает, почему они работают (или не работают). Ты сможешь адаптироваться, когда Microsoft выпустит очередной патч. Ты сможешь создать свой, уникальный метод сокрытия, основанный на твоём понимании системы.

Всё начинается с первого шага. С понимания, что тишина - это не пустота. Это дизайн.

(1) ETW: Большой Брат внутри твоего кода, но с API

Прежде чем ломать, надо понять. А ETW - это гениальная, чудовищно сложная и одновременно элегантная система. Представь себе гигантскую диспетчерскую внутри Windows. Тысячи датчиков (провайдеров) по всей системе - в ядре, в драйверах, в службах, в user-mode процессах - постоянно кричат о том, что происходит: «создан поток!», «открыт файл!», «вызван такой-то системный вызов!», «выделена память!». Эти крики - события (events). Их можно слушать, записывать, фильтровать.

Архитектура в трех абзацах:
  1. Провайдеры (Providers): Источники событий. У каждого есть GUID. Бывают kernel-mode (огромное количество событий ядра) и user-mode (например, .NET CLR, PowerShell, или твоё собственное приложение). Провайдер регистрируется в системе и говорит: «Я готов шуметь».
  2. Контроллеры (Controllers): Сущности, которые говорят провайдерам: «Начинай шуметь» или «Заткнись». И решают, куда эти события пойдут. Самый известный контроллер - сессия ETW с именем NT Kernel Logger. Но их может быть много.
  3. Консьюмеры (Consumers): Сущности, которые подписываются на события от контроллеров и что-то с ними делают: пишут в файл .etl, отправляют в SIEM, или, что нам критически важно, передают в драйвер EDR/антивируса. EDR - это супер-продвинутый консьюмер.
Система эффективна, потому что пока никто не слушает, провайдер молчит (ну, почти). Как только появляется консьюмер (например, запускается EDR), контроллер активирует провайдеров, и поток событий хлынет. События минимально аллоцируют память, используют буферизацию - это не printf на каждый чих. Это промышленный масштаб слежки.

Почему это проблема для нас? Потому что современные EDR (не тупые сигнатурные антивирусы) сидят именно здесь. Они не хукют тысячу функций в user-mode. Они регистрируются как консьюмеры на ключевые провайдеры ядра (Microsoft-Windows-Threat-Intelligence, Microsoft-Windows-Kernel-Process, и т.д.) и получают сырой, неотфильтрованный поток системной телеметрии. Твой CreateRemoteThread или NtMapViewOfSection светится там как ёлка. Раньше обходили хуки в ntdll.dll. Теперь обходят ETW. Это эволюция.

Практический инструмент №1: logman и Windows Performance Recorder (WPR)
Прежде чем воевать, надо увидеть врага. Открой консоль от админа.
  • logman query providers - вывалит тебе гигантский список всех зарегистрированных провайдеров в системе. Попробуй найти Microsoft-Windows-PowerShell или Microsoft-Windows-Kernel-Process.
  • logman start -n MyTrace -p Microsoft-Windows-Kernel-Process (process,thread,image) -o c:\trace.etl -ets - запустит сессию, слушающую события процессов, потоков и образов от ядерного провайдера, и пишущую в файл.
  • Запусти любой процесс, потом logman stop -n MyTrace -ets. Файл trace.etl можно открыть в Event Viewer (Открыть сохранённый журнал...) или в более продвинутом Windows Performance Analyzer. Ты увидишь события. Это база.
(2) User-Mode ETW и классическое «отключение»: почему это детский сад

Самый частый и самый наивный совет в «хацкерских» блогах: «запатчь EtwEventWrite или ntdll!EtwEventWriteFull». Давай разберёмся, что это и почему это уже не работает для серьёзных EDR.

В user-mode, когда код хочет сгенерировать событие (например, PowerShell хочет залогировать использование скрипта), он в конечном итоге вызывает функцию, обычно EtwEventWrite (или её варианты). Эта функция живет в ntdll.dll. Идея проста: найти в памяти эту функцию и заменить её начало на инструкцию ret (возврат) или mov eax, 0 (успех). События перестают уходить.

Код, который ты везде видел:

C++:
#include <Windows.h>
#include <iostream>

void PatchEtwEventWrite() {
// Находим функцию в памяти
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
if (!ntdll) return;

void* pFunction = GetProcAddress(ntdll, "EtwEventWrite");
if (!pFunction) return;

// Меняем защиту страницы на R/W
DWORD oldProtect;
VirtualProtect(pFunction, 1, PAGE_EXECUTE_READWRITE, &oldProtect);

// Патчим на ret (0xC3) для x86. Для x64 код сложнее.
#ifdef _WIN64
// Например, mov eax, 0; ret (b8 00 00 00 00 c3)
unsigned char patch[] = { 0xB8, 0x00, 0x00, 0x00, 0x00, 0xC3 };
memcpy(pFunction, patch, sizeof(patch));
#else
unsigned char patch = 0xC3;
memcpy(pFunction, &patch, 1);
#endif

VirtualProtect(pFunction, 1, oldProtect, &oldProtect);
std::cout << "[+] EtwEventWrite patched.\n";
}

Почему это детский сад?
  1. EDR это видят. Продвинутые EDR давно мониторят целостность критических функций в своих процессах (да, они внедряются в твой процесс). Рандомный ret в начале EtwEventWrite - красная лампочка.
  2. Это слишком грубо. Ты отключаешь ВСЕ user-mode события от ВСЕХ провайдеров для ВСЕХ консьюмеров. Это аномалия. Это как отключить микрофон у всего зала, чтобы не слышали твой разговор. Подозрительно.
  3. Не затрагивает Kernel-Mode. Самое важное! EDR в первую очередь слушает kernel-mode провайдеры. Твои манипуляции в user-mode на них не влияют. События о создании процесса, загрузке драйвера, вызове системных функций продолжают течь рекой. Ты спрятал ложку, но кухня продолжает греметь кастрюлями.
Вывод: Патчинг EtwEventWrite - это техника из 2018 года. Она может сработать против каких-то простых логгеров или для скрытия конкретных действий в user-mode (например, логирования .NET), но как полноценный метод сокрытия - мёртв. Но понимать её надо, это база.

(3) Настоящая игра: Kernel-Mode ETW и структура ETW_REG_ENTRY

Чтобы влиять на события ядра, нужно лезть глубже. В ядре Windows есть глобальная структура, которая управляет регистрацией провайдеров. Через нее можно манипулировать тем, какие провайдеры «включены» и для каких сессий (контроллеров) они шлют события.

Исследователи (большой респект таким ребятам как @\xpn\, @EthicalChaos) ковырялись в ядре и нашли эту структуру. В разных версиях Windows она немного меняется, но суть одна: есть глобальный список (EtwpRegEntryList или EtwpGroupList) зарегистрированных провайдеров. Каждая запись (ETW_REG_ENTRY или _ETW_REG_ENTRY) содержит критически важные поля:
  • Guid - идентификатор провайдера.
  • ProviderEnableInfo - массив структур, каждая из которых соответствует возможной сессии ETW (их ограниченное число, например, 64). В этой структуре есть поле EnableLevel (уровень детализации) и EnableFlags (маска того, какие события включены).
  • Если EnableLevel == 0 для данной сессии - провайдер для этой сессии молчит.
Стратегия: Найти в памяти ядра эту глобальную структуру, пробежаться по списку провайдеров, найти те, что интересны EDR (например, Microsoft-Windows-Threat-Intelligence), и для всех сессий (или конкретно для сессии, используемой EDR) выставить EnableLevel = 0. Это не удаляет провайдер, не патчит код. Это просто говорит системе: «для этого слушателя не генерируй события». Более тонко и менее заметно.

Проблема:
  1. Отсутствие документации. Эти структуры - ntoskrnl.exe private. Нет официального API. Мы работаем с reverse engineering.
  2. Сигнатуры. Смещения полей меняются от сборки к сборке. Нужен способ надежно их находить.
  3. Целостность ядра (PatchGuard). Прямая запись в неэкспортируемые структуры ядра из драйвера вызовет PAGE_FAULT_IN_NONPAGED_AREA или триггернет PatchGuard, что приведёт к BSOD. Нужно быть хитрее.
(4) Практический инструмент №2: Исследование с помощью WinDbg и поиск сигнатур

Прежде чем писать код, нужно понять ландшафт. Запустим WinDbg отладку ядра (локальную или через KD). Нам нужно найти, например, список провайдеров.

Код:
lkd> x nt!EtwpRegEntryList
lkd> x nt!*RegEntry*
lkd> dq nt!EtwpRegEntryList L1  // смотрим значение указателя
lkd> dl [значение]  // смотрим список

Мы можем найти сигнатуру для поиска ETW_REG_ENTRY в памяти. Часто это начинается с GUID провайдера (16 байт). Зная GUID известного провайдера (его можно получить через logman или из реестра HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\&lt;GUID&gt;), можно его заюзать для поиска.

Но ручное ковыряние в WinDbg - для разового исследования. Нам нужен код. Один из методов - найти экспортируемую функцию ядра, которая работает с этими структурами, например, EtwRegister или EtwEnableCallback, и дизассемблировать её, чтобы найти инструкции, обращающиеся к глобальному списку. Это уже серьёзный реверс.

(5) Метод: Патчинг памяти ядра через уязвимости драйверов (CVE-XXXX-XXXX)

Предупреждение: Этот раздел носит исключительно образовательный характер. Использование уязвимостей нулевого дня или публичных эксплойтов для модификации ядра незаконно и неэтично в неподконтрольной среде. Мы говорим о защите собственных систем или работе в рамках authorized red-teaming.

Поскольку PatchGuard охраняет критические структуры ядра, «классический» драйвер с прямыми записями не пройдёт. Но что, если найти другой драйвер, который уже имеет право записи в ядро, но содержит уязвимость, позволяющую нам выполнить произвольную запись?

История знает примеры: CVE-2019-16098 (драйвер hw.sys), CVE-2021-21551 (драйвер dbutil_2_3.sys). Идея: используя уязвимость в легитимном, подписанном драйвере, получить примитив записи по произвольному адресу ядра (write-what-where). Затем использовать этот примитив для обнуления полей EnableLevel в найденных структурах ETW_REG_ENTRY.

Примерный алгоритм (псевдокод):
  1. Установить уязвимый драйвер, получить хендл к его устройству.
  2. Эксплуатировать уязвимость для получения write-what-where.
  3. Найти в памяти ядра ETW_REG_ENTRY для целевого провайдера. Это можно сделать, просканировав память ядра на предмет его GUID. Для этого нужен ещё один примитив - чтение ядра (read-what-where), который часто идёт в паре с записью, или использовать другие методы (например, через \\.\PhysicalMemory в старых системах, или снова уязвимостью).
  4. Рассчитать адрес поля EnableLevel для нужной сессии (например, сессия EDR часто имеет ID = 1 или 2, это нужно исследовать).
  5. Записать 0 по этому адресу.
  6. Повторить для всех интересующих провайдеров (Kernel-Process, Kernel-Thread, Threat-Intelligence, Kernel-Network и т.д.).
Это сложный, но эффективный метод. EDR, работающий в пользовательском режиме, не увидит этих изменений. Агент в режиме ядра может обнаружить аномалии, если он также следит за целостностью ETW структур, но это нетривиально.

(6) Метод: Подмена GUID провайдера (Provider Hijacking)

Более тонкий и, возможно, более устойчивый метод. Вместо отключения провайдера, мы можем его подменить.

Идея: Многие EDR и системные мониторы включают провайдеры по их GUID. Что если зарегистрировать свой собственный user-mode провайдер с точно таким же GUID, как у системного Microsoft-Windows-Threat-Intelligence? Согласно логике ETW, когда консьюмер (EDR) включает провайдер по GUID, он включает ВСЕ зарегистрированные провайдеры с этим GUID. Наш user-mode провайдер получит callback (EtwEnableCallback), но системный kernel-mode провайдер - тоже. Однако, если наш провайдер зарегистрирован после системного, мы можем повлиять на процесс.

Более интересная атака: В callback'е включения (EtwEnableCallback) мы можем, в теории, изменить параметры (EnableLevel, EnableFlags), которые передаются дальше. Или вообще вернуть ошибку. Но это требует глубокого понимания порядка вызовов.

Практический инструмент №3: Создание своего провайдера
Вот основа для регистрации user-mode провайдера. Это легальный код, используемый для диагностики.

C++:
#include <windows.h>
#include <evntprov.h>
#include <stdio.h>

// GUID, который мы хотим подменить. ВЗЯТЬ ИЗ СИСТЕМНОГО ПРОВАЙДЕРА!
// {GUID для Microsoft-Windows-Threat-Intelligence}
DEFINE_GUID(/* Вставить сюда реальный GUID */,
    0xxxxxxxx, 0xxxx, 0xxxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx);

REGHANDLE hProvider = NULL;

void WINAPI EtwEnableCallback(
    _In_ LPCGUID SourceId,
    _In_ ULONG IsEnabled,
    _In_ UCHAR Level,
    _In_ ULONGLONG MatchAnyKeyword,
    _In_ ULONGLONG MatchAllKeyword,
    _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
    _In_opt_ PVOID CallbackContext
) {
    printf("[+] EtwEnableCallback called! IsEnabled: %d, Level: %d\n", IsEnabled, Level);
    // Здесь можно сделать что-то хитрое.
    // Например, если Level > 0, можно попробовать предотвратить включение
    // настоящего провайдера (сложно).
    // Или просто логировать факт включения EDR.
}

int main() {
    ULONG status = EventRegister(&/*GUID*/, EtwEnableCallback, NULL, &hProvider);
    if (status != ERROR_SUCCESS) {
        printf("[-] EventRegister failed: %lu\n", status);
        return 1;
    }
    printf("[+] Fake provider registered with system ETW GUID.\n");

    // Сидим и ждём, пока EDR не включит нас
    getchar();

    EventUnregister(hProvider);
    return 0;
}

Запусти такой провайдер от имени SYSTEM или от пользователя, под которым работает агент EDR. Если GUID совпадёт, и EDR включает провайдер, твой callback вызовется. Это уже огромная информация. Дальше можно экспериментировать: что будет, если из callback'а вызвать ExitProcess? Или подвесить поток? Сломается ли подписка EDR? Это область для исследования.

(7) Метод: Манипуляции с сессиями ETW (EtwpEventTracingSlot)

В ядре есть глобальный массив EtwpEventTracingSlot (или подобный), который содержит указатели на структуры контекста для каждой возможной сессии ETW (их, напомню, ~64). Каждая сессия (ETW_SESSION) содержит настройки: куда пишут события, какие провайдеры включены и т.д.

Если мы можем найти этот массив и обнулить, например, указатель для сессии, которую использует EDR, то система перестанет направлять события в эту сессию. Или можем модифицировать LoggerContext сессии, чтобы сбросить флаги активности.

Это ещё более низкоуровневая и опасная операция, чем манипуляция с ETW_REG_ENTRY, так как затрагивает непосредственно механизм маршрутизации событий. PatchGuard здесь особенно бдителен.

(8) Обход на уровне компиляции и загрузки: «Серые» бинари

Отойдём от прямого насилия над ядром. Есть более элегантный путь: предотвратить генерацию событий в принципе.

Многие события генерируются не глубоко в ядре, а в user-mode библиотеках, когда они вызывают системные вызовы. Например, kernelbase.dll и ntdll.dll содержат обёртки, которые перед вызовом NtCreateThreadEx могут генерировать ETW-событие, если соответствующий провайдер включен.

Что если скомпилировать или загрузить свою копию ntdll.dll, в которой эти вызовы в ETW вырезаны? Или модифицировать её на диске перед загрузкой?

Техника 1: Статическая линковка с минимальными runtime-библиотеками. Если пишешь свой Loader или инструмент на чистом C, можно избежать линковки с ntdll.dll вообще? Нет, системные вызовы всё равно нужны. Но можно вызывать их напрямую через syscall, минуя обёртки в ntdll. Это уже реально. Современные шеллкод-лоадеры (например, использующие технику "Hell's Gate" или "Dynamic Syscall") именно это и делают: они находят номер системного вызова (SSN) напрямую и вызывают его через инструкцию syscall, полностью обходя ntdll.dll. А раз обошли ntdll - обошли и её встроенные вызовы EtwEventWrite.

Если вы хотите узнать больше о том, как обходить телеметрию стека и использовать обратные вызовы для скрытия действий в системе, ознакомьтесь с этим материалом о применении ASM в обходе защитных механизмов. - тык

Практический инструмент №4: Динамический вызов syscall на C (x64)

C:
#include <windows.h>
#include <stdint.h>

// Примитивная функция для поиска SSN по сигнатуре в ntdll (опущена для краткости)
DWORD FindSSNForSyscall(const char* syscallName);

// Пример для NtCreateThreadEx (условный)
void MyNtCreateThreadEx(...) {
    DWORD ssn = FindSSNForSyscall("NtCreateThreadEx");
    __asm {
        mov r10, rcx
        mov eax, ssn  // SSN в eax
        syscall
        ret
    }
}

Таким образом, твой код не вызывает NtCreateThreadEx из ntdll, а делает syscall сам. Соответственно, любой код логирования внутри ntdll!NtCreateThreadEx (включая ETW) пропускается. Это один из самых эффективных user-mode методов. Но EDR адаптируются: они начинают ставить хуки на саму инструкцию syscall в ядре, через KiSystemCall64Shadow и механизм MSR (Model Specific Registers). Это уже следующая эшелонированная битва.

(9) Инструментарий реального мира: SilkETW, TamperETW и другие

Не все должны писать всё с нуля. Сообщество создало инструменты, которые абстрагируют часть сложностей.
  • SilkETW (GitHub - mandiant/SilkETW): Утилита от FireEye (Mandiant) для работы с ETW как для атак, так и для защиты. Позволяет легко собирать события, создавать потребителей и, что важно, фильтровать их в реальном времени. С точки зрения атакующего, можно использовать её движок для подписки на события и их подавления (dropping) перед тем, как они достигнут EDR. Это работает, если запустить SilkETW с правильными привилегиями ДО запуска EDR, чтобы захватить подписку.
  • TamperETW (https://github.com/cyberark/TamperETW): Инструмент, который делает именно то, о чём мы говорили в разделе (2) - патчит EtwEventWrite и другие функции в памяти. Просто, грубо, иногда работает. Исходный код - отличный пример для изучения.
  • GadgetToJScript и подобные: Используют модификацию ETW среди других техник для скрытия выполнения скриптов внутри процессов.
Использование таких инструментов в операциях повышает скорость, но снижает скрытность. Сигнатуры этих утилит давно в EDR. Кастомный код, написанный по мотивам их методов, предпочтительнее.

(10) Детекция и контрмеры EDR: как они могут нас найти

Наш разговор был бы нечестным, если бы мы не посмотрели на ситуацию глазами защитника. Как EDR может обнаружить наши манипуляции?
  1. Детекция PatchGuard Crash. Если EDR видит, что система неожиданно падает с PG-виолей (что может случиться при грубом патче ядра), это косвенная улика.
  2. Проверка целостности критичных функций. EDR может периодически читать и проверять хэш EtwEventWrite и других функций в своей user-mode памяти.
  3. Внешний наблюдатель. EDR может запустить минимальный консьюмер в отдельном, максимально защищённом процессе, который будет получать события и сравнивать их с тем, что получает основной агент. Если поток событий различается - что-то не так.
  4. Анализ поведения. Если в системе происходят явно вредоносные действия (инжект шеллкода, манипуляции с LSASS), но от ETW приходят только тишина - это само по себе аномалия. EDR может переключиться на другие источники данных (например, минифильтры файловой системы, сетевые драйверы) и принять решение на основе косвенных признаков.
  5. Защита самого EDR. EDR может регистрировать свой провайдер с уникальным, случайно сгенерированным GUID для внутренней связи между компонентами. Подменить такой GUID сложно.
  6. Хуки в ядре. EDR может установить callback'и на EtwRegister, EtwUnregister, EtwEnableCallback для мониторинга попыток вмешательства в ETW.
Вывод: Нет неуязвимой техники. Есть баланс между скрытностью, сложностью реализации и устойчивостью к обновлениям. Часто лучший подход - комбинация: syscall + минимальные манипуляции с user-mode ETW для конкретных провайдеров (например, .NET) + работа из легитимных, но забытых процессов (например, msbuild.exe), которые сами по себе генерируют мало подозрительных событий.

(11) Лаборатория и эксперименты: предлагаемый план

Теория без практики мертва. Вот что ты можешь сделать на своей isolated VM (обязательно изолированной!):
  1. Базовый мониторинг. Установи Sysmon, настрой его на логирование всего. Запусти простой шеллкод-лоадер. Увидишь кучу событий (ProcessCreate, CreateRemoteThread, etc).
  2. Примени грубый патч. Используй TamperETW или свой мини-патчер для EtwEventWrite. Повтори эксперимент. Увидишь, что события от Sysmon (которые идут через ETW) могут исчезнуть или измениться. Но события от EDR (если он установлен) скорее всего останутся.
  3. Изучи kernel-mode. Подними простой драйвер (используя пример из Windows Driver Kit), который только читает память. Попробуй найти GUID известного провайдера в памяти. Не пиши!
  4. Эксперимент с подменой GUID. Зарегистрируй свой провайдер с GUID от PowerShell. Запусти PowerShell. Видишь ли ты callback в своей программе?
  5. Освой syscall. Напиши простейший загрузчик, который вызывает NtAllocateVirtualMemory, NtWriteVirtualMemory, NtCreateThreadEx напрямую через syscall. Сравни события с классическим вызовом через CreateRemoteThread.
Финал как начало: Что мы на самом деле сделали?

Если ты дошёл до этого места - ты уже не тот, кто начал читать. Ты проделал путь от поверхностного понимания ETW как «чего-то, что логирует» до глубокого, архитектурного видения его как центральной нервной системы телеметрии Windows. Мы не просто изучили методы обхода. Мы провели полноценное инженерное расследование, разобрав механизм на компоненты, чтобы понять, где и как приложить усилие для достижения контролируемой тишины.

Давай оглянемся на пройденное:
  • Мы начали с "думать" - осознали, что современная ОС есть машина для производства шума, а наша задача - стать мастером акустики в этой машине.
  • Мы погрузились в архитектуру ETW - от пользовательских провайдеров до ядерных сессий, от вызовов EtwEventWrite до структур ETW_REG_ENTRY и EtwpEventTracingSlot.
  • Мы прошли весь спектр методов - от грубого патчинга в user-mode, который является детским садом, до рискованных манипуляций с ядерными структурами, которые требуют понимания каждого байта.
  • Мы обсудили альтернативные пути - обход генерации событий на этапе компиляции и загрузки через прямые syscall'ы, что является, пожалуй, самым элегантным и устойчивым подходом.
  • Мы разобрали инструментарий - от системных утилит вроде logman до исследовательских проектов вроде SilkETW и TamperETW.
  • И, наконец, мы посмотрели на проблему глазами защиты - как EDR могут детектировать наши манипуляции и почему идеальная тишина невозможна и даже нежелательна.
Это был не просто список техник. Это был курс выживания в среде тотальной наблюдаемости. И теперь, когда у тебя есть карта местности, пришло время обсудить, как по ней двигаться.

Если вам интересна тема обхода EDR с использованием прямых системных вызовов, рекомендую к прочтению материал о direct syscalls, который глубоко раскрывает этот метод обхода и показывает его практическое применение.

Синтез методов

Главный вывод из всего вышеперечисленного - не существует серебряной пули. Каждый метод существует в контексте компромиссов:
  1. Уровень воздействия (User-mode vs Kernel-mode): Чем глубже, тем эффективнее, но и опаснее, и заметнее. Патч EtwEventWrite может быть детектирован EDR в user-mode, но обнуление EnableLevel в ядре - может вызвать PatchGuard или быть замечено другим драйвером EDR.
  2. Уровень шума (Полное отключение vs Избирательная фильтрация): Глушение всего потока событий - это флаг аномалии. Точечное подавление только событий, связанных с твоими конкретными действиями (например, манипуляциями с потоками или памятью) - искусство.
  3. Сложность реализации (Готовые инструменты vs Кастомный код): Использование SilkETW или TamperETW быстро даёт результат, но оставляет известные сигнатуры. Кастомный код, написанный по мотивам их методов, требует времени, но меньше шумит.
Правило минимальной достаточности: Используй самый простой и наименее заметный метод, который решает конкретную задачу в конкретном контексте.
  • Если тебе нужно скрыть запуск .NET-ассемблера в памяти, возможно, достаточно патча EtwEventWrite для провайдера .NET CLR (но не всех провайдеров!).
  • Если ты делает инжект шеллкода в легитимный процесс, сосредоточься на прямых syscall'ах для ключевых функций (NtAllocateVirtualMemory, NtWriteVirtualMemory, NtCreateThreadEx) и обнулении EnableLevel для Microsoft-Windows-Threat-Intelligence только на время операции.
  • Если ты проводишь red-team операцию с долгим присутствием в системе, возможно, стоит вообще не трогать ETW глобально, а вместо этого максимально мимикрировать под легитимную активность, генерируя фоновый шум (например, совершая легитимные действия в системе), чтобы твои редкие вредоносные действия терялись в нём.
Комбинирование методов (например, прямые syscall'ы + точечное отключение ядерного провайдера для сессии EDR) часто даёт синергетический эффект, но и увеличивает поверхность атаки для детектирования.

Когда знание становится опасным

Мы много говорили о техниках, но важно очертить красные линии.
  1. Использование уязвимостей нулевого дня (0-day): В статье мы упоминали эксплуатацию уязвимых драйверов как метод записи в ядро. В реальной операции использование неизвестных уязвимостей (0-day) против систем, на которые у тебя нет явного разрешения на тестирование, - это не исследование, это киберпреступление. Существует этический framework (например, ответственное раскрытие уязвимостей), который должен соблюдаться.
  2. Работа на production-системах: Все эксперименты должны проводиться в изолированных лабораториях. Патчинг ядерных структур, даже образовательный, может привести к нестабильности системы, потере данных и срабатыванию систем безопасности, что может иметь юридические последствия.
  3. Цель - понимание, а не разрушение: Знания, изложенные здесь, должны быть направлены на улучшение безопасности: тестирование собственных систем, разработку более эффективных защитных механизмов, понимание векторов атак для их блокирования. Использование их для несанкционированного доступа, шпионажа или саботажа - это путь в никуда.
Это понимание мотивов, методов и мышления всех сторон: и атакующего, и защищающегося. Настоящий специалист по безопасности должен уметь думать как обе стороны, чтобы эффективно защищать.

Будущее ETW и эволюция контроля: Куда движется ветер

Технологии не стоят на месте. Microsoft активно развивает средства изоляции и контроля целостности, которые напрямую влияют на применимость описанных методов:
  1. Hypervisor-Protected Code Integrity (HVCI) и Kernel Mode Hardware Enforced Stack Protection: Эти технологии делают прямое модифицирование кода ядра и критических структур практически невозможным, даже с примитивом записи. Патчинг ядерных функций или структур может приводить к немедленному краху системы.
  2. Microsoft Pluton и Secure Core PC: Аппаратные технологии, обеспечивающие целостность загрузки и изоляцию критических компонентов. Они могут быть использованы для проверки целостности ядра и его структур в рантайме.
  3. Усиление изоляции пользовательского режима: Технологии вроде Arbitrary Code Guard (ACG) и Code Integrity Guard (CIG) затрудняют выполнение неподписанного кода и модификацию исполняемой памяти, что влияет на методы патчинга user-mode функций.
  4. Развитие самой ETW: Microsoft продолжает добавлять новых провайдеров, углублять детализацию событий. Возможны изменения в архитектуре, которые сделают глобальные списки структур ещё более защищёнными или виртуализированными.
Что это значит для нас? Методы, основанные на прямом изменении ядра, будут становиться всё менее устойчивыми. Будущее, скорее всего, за методами обхода на этапе компиляции/загрузки и злоупотреблением легитимными механизмами (например, более изощрённой подменой провайдеров, использованием официальных API для управления ETW в своих целях). Также будет расти важность социальной инженерии и фишинга как способа получения первоначального доступа без необходимости глубокого технического обхода - потому что если у тебя есть права администратора и возможность отключить защитные механизмы легально, бой с ETW становится не нужен.

(10.4) Итоговый чек-лист исследователя

Прежде чем закрыть эту статью, сверься с этим списком. Если ты можешь уверенно ответить на эти вопросы и выполнить эти пункты - ты усвоил материал.
  • Понимание основ: Можешь объяснить разницу между провайдером, контроллером и консьюмером ETW, не заглядывая в текст?
  • Инструментарий: Умеешь использовать logman для захвата событий конкретного провайдера и анализировать .etl файл в WPA?
  • User-mode обход: Написал (или подробно разобрал) код, который патчит EtwEventWrite только для конкретного модуля (например, clr.dll), а не глобально?
  • Ядерные структуры: Можешь в WinDbg найти ETW_REG_ENTRY для провайдера Microsoft-Windows-Kernel-Process и вручную изменить его EnableLevel для сессии 1 (условно)?
  • Прямые syscall'ы: Реализовал хотя бы один системный вызов (например, NtAllocateVirtualMemory) напрямую через syscall, без обращения к ntdll?
  • Анализ защиты: Можешь назвать хотя бы три способа, которыми EDR может обнаружить манипуляции с ETW, описанные в статье?
  • Этика: Чётко понимаешь разницу между исследованием в изолированной среде и несанкционированным вторжением?
Последние слова

Будь хакером в изначальном смысле слова.
Не вандалом, который ломает из любопытства, а мастером, который понимает механизмы до винтика, чтобы улучшить, починить или защитить. Используй свои навыки, чтобы делать интернет и компьютеры безопаснее для всех. Помогай новичкам, делись знаниями (как это сделал я здесь), но не корми их рыбой - учи их рыбачить.

И помни главный парадокс, который мы открыли: абсолютная тишина невозможна и не нужна. Искусство не в том, чтобы замолчать полностью, а в том, чтобы твой шум ничем не отличался от шума целого мира. Искусство не в том, чтобы уничтожить систему слежки, а в том, чтобы стать для неё невидимым, оставаясь на виду.

Система будет эволюционировать. Защита будет становиться сложнее. Но пока есть любопытные умы, готовые ковыряться в отладчике до рассвета, чтобы понять, как работает мир, - баланс будет сохраняться.
 
Последнее редактирование модератором:
  • Нравится
Реакции: Luxkerr и delifer
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab