Прямые и непрямые сисколлы
Что покупает: дверь в обход юзермодных хуков. EDR инструментируют документированный API в kernel32/ntdll; прямой вызов ядра — с номером сисколла, извлечённым из байтов самой ntdll в рантайме, — проходит мимо инструментации целиком. Счётчик: три показания. Лотерея SSN: номера сисколлов смещаются между сборками Windows, так что любая техника, предполагающая вместо извлечения, — мина замедленного действия; трюки с сортировкой ломаются, как только хук переупорядочит таблицу. Ядро хранит квитанцию: вызов всё равно случается — провайдеры телеметрии потоков видят его независимо от инициатора. И проблема адреса: инструкция syscall, исполненная из вашего собственного модуля, оставляет указатель инструкции там, где его не бывает у легитимных путей вызова. Именно из-за этого последнего показания стабы C-агента непрямые: стаб загружает номер, а затем прыгает внутрь собственной инструкции syscall в ntdll, так что и указатель, и адрес возврата живут там, где ядро их ожидает. Слой над стабом всё ещё рассказывает историю — и это следующая аренда.
Разхукивание
Что покупает: исходные байты обратно — восстановление ntdll.text с диска, так что патчи сенсора исчезают. Счётчик: восстановление — самый громкий жест в этом списке. Свежий приватный маппинг ntdll — старейший маркер жанра; движки, проверяющие свои хуки по расписанию, замечают их исчезновение; а в момент ремонта вы сами проставили временную метку своего страха для всякого коррелирующего. Наша доктрина — обходить, а не ремонтировать: непрямым сисколлам не нужно, чтобы хуки исчезли, — и относиться к патчингу как к гейтуемому решению: когда фингерпринт говорит, что присутствует kernel-уровневый EDR (шесть из шестнадцати отслеживаемых семейств), путь удалённого патчинга пропускается целиком, а не пытается и объясняется.
Подмена стека вызовов
Что покупает: обход стека читает скучную историю. Аудиты стартов потоков и ETW-обходы стека, идущие по кадровым указателям, видят возвраты в подписанные системные библиотеки — ntdll, kernel32, kernelbase, — вместо цепочки, оканчивающейся в анонимной памяти. Как мы это гоняем: однобайтовые ret-гаджеты, найденные примерно на двух третях глубины исполняемой секции каждой библиотеки — достаточно глубоко, чтобы выглядеть возвратом из середины настоящей функции, а не из пролога, — сплетённые в синтетическую цепочку кадров вокруг вызова. Счётчик: камуфляж обманывает идущих по кадровым указателям, но не размотку по таблицам исключений — обходы .pdata не следуют вашей нарисованной цепочке, а исключение, поднятое сквозь сфабрикованный кадр, не находит настоящего обработчика; процесс умирает, сжимая собственную историю. Поэтому мы тратим её там, где пауза длинна — ожидание сна, — а не на каждый вызов. Подмена, сквозь которую нельзя позволить себе упасть, — подмена одноразовая, осознанная.
Обфускация сна
Что покупает: пауза — окно сканера: маяк большую часть жизни спит, а сканеры памяти приходят именно во сне. Наш спит зашифрованным и недосягаемым: зарегистрированные регионы (секреты конфига, ключи сессии — до 64) XOR-ятся 8-байтовым ключом, выведенным из счётчика тиков и стековой энтропии, затем переводятся в PAGE_NOACCESS; само ожидание — одноразовый таймер из очереди таймеров, а на x64 блокирующее ожидание идёт под подменённым стеком из предыдущего раздела; при пробуждении всё восстанавливается, а ключ затирается. Счётчик: регион PAGE_NOACCESS внутри процесса — ровно та форма, которую сканеры памяти ищут: сканер читает то, чем страница была, так что аудит VAD ловит силуэт, даже когда байты в безопасности. Очереди таймеров перечислимы; поток, припаркованный на приватном событии, имеет видимую цепочку ожидания. Шифрование защищает содержимое. Оно не может сделать невидимой форму спрятанного секрета — этот слой сдаётся в аренду и защитнику.
Порча ETW
Что покупает: поток событий замолкает — полностью покрыто в заметке N.09: транзиентные стабы return-clean на пути записи, на команду, с очисткой кэша политики ниже движка. Счётчик: тишина сама по себе сигнал. Хост, переставший испускать события, которые всегда испускал, — правило корреляции с пульсом; порча хороша ровно настолько, насколько всё остальное, происходящее на проводе в то же время. Это повторяющийся урок всей полки: слой, который вы ослепили, покрыт, а слой, следящий за слепотой, — нет.
| Техника | Покупает | Показание счётчика |
|---|---|---|
| Прямые сисколлы | Мимо юзермодных хуков | Лотерея SSN; телеметрия ядра; RIP в чужом модуле |
| Непрямые сисколлы | + адрес возврата в ntdll | Стек над стабом всё ещё повествует |
| Разхукивание | Исходные байты восстановлены | Маркер свежего маппинга; проверки хуков; самоотметившийся страх |
| Подмена стека | Скучные обходы стека | Разногласие с unwind-таблицами; фатально при исключении |
| Обфускация сна | Зашифрованная, нетронутая пауза | Силуэт NOACCESS; перечислимые очереди таймеров |
| Порча ETW | Тихий поток событий | Корреляция тишины-как-сигнала |
Почему импланты с одним трюком быстро стареют
Детект-инжиниринг не оценивает трюки; он коррелирует слои. Имплант, делающий только прямые сисколлы, вручает защитнику правило обхода стека. Тот, что только разхукивает, вручает правило целостности маппингов. Каждый одиночный трюк покрывает один слой и своим существованием рекламирует, какие слои его автор забыл. Вот почему наша лаборатория оценивает композиции, а не техники: ревизия выходит потому, что весь стек — гейтинг по фингерпринту, непрямые вызовы, нарисованные стеки для длинных пауз, зашифрованные сны, ворота на команду, — выживает матрицу вместе, а не потому, что кто-то из участников умен. Трюк — статья расхода. Композиция — продукт.
Обход — не пропуск. Это аренда — и счётчик крутится в слое, который вы забыли.
Угол защитника
Каждая строка прейскуранта — правило, ждущее вас. Аудируйте указатели инструкций при входе в сисколл — syscall, исполненный вне ntdll, — приговор. Следите за свежими приватными маппингами ntdll.dll и за хуками, исчезающими по расписанию. Сравнивайте цепочки кадровых указателей с размотками .pdata, где телеметрия позволяет; разногласие и есть детект. Охотьтесь на приватные регионы PAGE_NOACCESS и перечисляйте очереди таймеров на важных хостах. И коррелируйте тишину: остановившийся поток событий и есть событие. Мы предпочитаем быть пойманными этими правилами, чем услышанными из-за прилагательных — стройте их.
Следующая заметка: Как бы мы охотились на себя. Детект-инжиниринг против C2 в памяти, с другой стороны — память по форме, периодичность по статистике, таблица каналов, которую все забывают, и пробелы, которые мы оставляем сознательно.