Исследовательская заметка N.11 · 2026-10-16

Сколько ещё стоит каждый обход.

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

ВРЕМЯ ЧТЕНИЯ ≈ 8 МИН АВТОР КОМАНДА AXIS

Прямые и непрямые сисколлы

Что покупает: дверь в обход юзермодных хуков. EDR инструментируют документированный API в kernel32/ntdll; прямой вызов ядра — с номером сисколла, извлечённым из байтов самой ntdll в рантайме, — проходит мимо инструментации целиком. Счётчик: три показания. Лотерея SSN: номера сисколлов смещаются между сборками Windows, так что любая техника, предполагающая вместо извлечения, — мина замедленного действия; трюки с сортировкой ломаются, как только хук переупорядочит таблицу. Ядро хранит квитанцию: вызов всё равно случается — провайдеры телеметрии потоков видят его независимо от инициатора. И проблема адреса: инструкция syscall, исполненная из вашего собственного модуля, оставляет указатель инструкции там, где его не бывает у легитимных путей вызова. Именно из-за этого последнего показания стабы C-агента непрямые: стаб загружает номер, а затем прыгает внутрь собственной инструкции syscall в ntdll, так что и указатель, и адрес возврата живут там, где ядро их ожидает. Слой над стабом всё ещё рассказывает историю — и это следующая аренда.

ЮЗЕРМОД ЯДРО A — ДОКУМЕНТИРОВАННЫЙ ПУТЬ · С ХУКОМ ваш вызов kernel32 ntdll · стаб сисколла ХУК УСТАНОВЛЕН ЗДЕСЬ гейт syscall ядро счётчик: аргументы и стек прочитаны до входа B — НЕПРЯМОЙ ПУТЬ · В ОБХОД ваш вызов стаб агента SSN прочитан из байтов ntdll jmp ntdll · инструкция syscall RIP и адрес возврата живут здесь ядро юзермодные хуки остаются на месте — просто не на пути
Рис. A — два пути, одно ядроДокументированный путь читается; непрямой идёт в обход

Разхукивание

Что покупает: исходные байты обратно — восстановление ntdll.text с диска, так что патчи сенсора исчезают. Счётчик: восстановление — самый громкий жест в этом списке. Свежий приватный маппинг ntdll — старейший маркер жанра; движки, проверяющие свои хуки по расписанию, замечают их исчезновение; а в момент ремонта вы сами проставили временную метку своего страха для всякого коррелирующего. Наша доктрина — обходить, а не ремонтировать: непрямым сисколлам не нужно, чтобы хуки исчезли, — и относиться к патчингу как к гейтуемому решению: когда фингерпринт говорит, что присутствует kernel-уровневый EDR (шесть из шестнадцати отслеживаемых семейств), путь удалённого патчинга пропускается целиком, а не пытается и объясняется.

Подмена стека вызовов

Что покупает: обход стека читает скучную историю. Аудиты стартов потоков и ETW-обходы стека, идущие по кадровым указателям, видят возвраты в подписанные системные библиотеки — ntdll, kernel32, kernelbase, — вместо цепочки, оканчивающейся в анонимной памяти. Как мы это гоняем: однобайтовые ret-гаджеты, найденные примерно на двух третях глубины исполняемой секции каждой библиотеки — достаточно глубоко, чтобы выглядеть возвратом из середины настоящей функции, а не из пролога, — сплетённые в синтетическую цепочку кадров вокруг вызова. Счётчик: камуфляж обманывает идущих по кадровым указателям, но не размотку по таблицам исключений — обходы .pdata не следуют вашей нарисованной цепочке, а исключение, поднятое сквозь сфабрикованный кадр, не находит настоящего обработчика; процесс умирает, сжимая собственную историю. Поэтому мы тратим её там, где пауза длинна — ожидание сна, — а не на каждый вызов. Подмена, сквозь которую нельзя позволить себе упасть, — подмена одноразовая, осознанная.

Обфускация сна

Что покупает: пауза — окно сканера: маяк большую часть жизни спит, а сканеры памяти приходят именно во сне. Наш спит зашифрованным и недосягаемым: зарегистрированные регионы (секреты конфига, ключи сессии — до 64) XOR-ятся 8-байтовым ключом, выведенным из счётчика тиков и стековой энтропии, затем переводятся в PAGE_NOACCESS; само ожидание — одноразовый таймер из очереди таймеров, а на x64 блокирующее ожидание идёт под подменённым стеком из предыдущего раздела; при пробуждении всё восстанавливается, а ключ затирается. Счётчик: регион PAGE_NOACCESS внутри процесса — ровно та форма, которую сканеры памяти ищут: сканер читает то, чем страница была, так что аудит VAD ловит силуэт, даже когда байты в безопасности. Очереди таймеров перечислимы; поток, припаркованный на приватном событии, имеет видимую цепочку ожидания. Шифрование защищает содержимое. Оно не может сделать невидимой форму спрятанного секрета — этот слой сдаётся в аренду и защитнику.

Рис. B — пауза в бронеСодержимое в безопасности; форма видна — цена учтена

Порча ETW

Что покупает: поток событий замолкает — полностью покрыто в заметке N.09: транзиентные стабы return-clean на пути записи, на команду, с очисткой кэша политики ниже движка. Счётчик: тишина сама по себе сигнал. Хост, переставший испускать события, которые всегда испускал, — правило корреляции с пульсом; порча хороша ровно настолько, насколько всё остальное, происходящее на проводе в то же время. Это повторяющийся урок всей полки: слой, который вы ослепили, покрыт, а слой, следящий за слепотой, — нет.

ТехникаПокупаетПоказание счётчика
Прямые сисколлыМимо юзермодных хуковЛотерея SSN; телеметрия ядра; RIP в чужом модуле
Непрямые сисколлы+ адрес возврата в ntdllСтек над стабом всё ещё повествует
РазхукиваниеИсходные байты восстановленыМаркер свежего маппинга; проверки хуков; самоотметившийся страх
Подмена стекаСкучные обходы стекаРазногласие с unwind-таблицами; фатально при исключении
Обфускация снаЗашифрованная, нетронутая паузаСилуэт NOACCESS; перечислимые очереди таймеров
Порча ETWТихий поток событийКорреляция тишины-как-сигнала
Рис. C — прейскурантКаждая строка — аренда, не пропуск

Почему импланты с одним трюком быстро стареют

Детект-инжиниринг не оценивает трюки; он коррелирует слои. Имплант, делающий только прямые сисколлы, вручает защитнику правило обхода стека. Тот, что только разхукивает, вручает правило целостности маппингов. Каждый одиночный трюк покрывает один слой и своим существованием рекламирует, какие слои его автор забыл. Вот почему наша лаборатория оценивает композиции, а не техники: ревизия выходит потому, что весь стек — гейтинг по фингерпринту, непрямые вызовы, нарисованные стеки для длинных пауз, зашифрованные сны, ворота на команду, — выживает матрицу вместе, а не потому, что кто-то из участников умен. Трюк — статья расхода. Композиция — продукт.

Обход — не пропуск. Это аренда — и счётчик крутится в слое, который вы забыли.

Угол защитника

Каждая строка прейскуранта — правило, ждущее вас. Аудируйте указатели инструкций при входе в сисколл — syscall, исполненный вне ntdll, — приговор. Следите за свежими приватными маппингами ntdll.dll и за хуками, исчезающими по расписанию. Сравнивайте цепочки кадровых указателей с размотками .pdata, где телеметрия позволяет; разногласие и есть детект. Охотьтесь на приватные регионы PAGE_NOACCESS и перечисляйте очереди таймеров на важных хостах. И коррелируйте тишину: остановившийся поток событий и есть событие. Мы предпочитаем быть пойманными этими правилами, чем услышанными из-за прилагательных — стройте их.

Следующая заметка: Как бы мы охотились на себя. Детект-инжиниринг против C2 в памяти, с другой стороны — память по форме, периодичность по статистике, таблица каналов, которую все забывают, и пробелы, которые мы оставляем сознательно.