Почему один слой защиты не работает
Любой механизм детектирования — сигнатурный, эвристический, поведенческий — имеет конечное пространство признаков, на которые он опирается. Атакующий, понимая эти признаки, может модифицировать артефакты так, чтобы конкретный слой их не увидел. Это не теоретическая проблема: техники снижения детекта (detection evasion) систематизированы в MITRE ATT&CK и регулярно наблюдаются в реальных кампаниях.
Ответ на вопрос «что компенсирует обход одного сигнала» — многослойная модель защиты, где каждый слой покрывает слепые зоны соседнего. Ниже разобраны основные классы техник обхода и соответствующие им компенсирующие механизмы.
Сигнатурный детект и его обход
Сигнатурный антивирус сравнивает байтовую последовательность файла с базой известных шаблонов. Обход достигается любым изменением этих байтов без потери функциональности:
- Перестановка секций PE-файла. Порядок секций в заголовке не влияет на загрузку, но меняет хеш и байтовый паттерн.
- Шифрование/кодирование полезной нагрузки. Тело шифруется, а в точке входа добавляется небольшой расшифровщик. Сигнатура на оригинальный код перестаёт совпадать.
- Модификация точки входа. Вставка мусорных инструкций (junk code) или перенос точки входа в другую секцию.
- Полиморфизм. Каждая копия файла содержит уникальный расшифровщик и ключ, что делает статическую сигнатуру бесполезной.
Что закрывает эту зону
| Слой | Что закрывает |
|---|---|
| Эвристический анализ | Обнаруживает подозрительные конструкции (расшифровщик + jump в данные) независимо от конкретных байтов |
| Статический анализ структуры PE | Аномалии энтропии секций, нестандартные флаги секций, подозрительные импорты |
| Sandbox / динамический анализ | Запускает файл в изолированной среде и фиксирует поведение, даже если сигнатура не сработала |
Снижение энтропии как техника обхода
Один из индикаторов, по которым статические анализаторы выделяют подозрительные файлы, — высокая энтропия секции. Зашифрованный или сжатый код даёт энтропию, близкую к 8 битам на байт, что резко отличается от обычного компилированного кода (обычно 5–6,5).
Атакующие снижают энтропию за счёт:
- Разбиения зашифрованных данных на блоки с добавлением нулевых или повторяющихся байтов.
- Использования алгоритмов сжатия с низким коэффициентом вместо шифрования.
- Вставки «мёртвого» кода или данных с предсказуемым распределением.
Для оценки энтропии используется формула Шеннона:
Python:
import math
def calc_entropy(buffer):
if isinstance(buffer, str):
buffer = buffer.encode()
entropy = 0
for x in range(256):
p = float(buffer.count(bytes([x]))) / len(buffer)
if p > 0:
entropy += -p * math.log(p, 2)
return entropy
Если энтропия секции ниже порога, эвристический анализатор может не пометить файл как упакованный.
Что компенсирует обход энтропии
- Поведенческий анализ в рантайме. Независимо от энтропии на диске, при расшифровке в памяти код начинает выполнять характерные действия: аллокация памяти с правами исполнения, инъекция в процесс, обращение к чувствительным API.
- Memory scanning. EDR-решения сканируют выделенные регионы памяти процесса на наличие известных паттернов или аномальных структур (например, PE-заголовок в регионе, выделенном через
VirtualAlloc).
- Контроль аномалий энтропии в памяти. Некоторые EDR фиксируют регионы с высокой энтропией, которые были выделены с флагом
PAGE_EXECUTE_READWRITE.
Обход поведенческого анализа через API-маскировку
Поведенческий детект отслеживает вызовы чувствительных WinAPI:
VirtualAllocEx, WriteProcessMemory, CreateRemoteThread, NtUnmapViewOfSection и подобные. Атакующие маскируют эти вызовы:IAT Camouflage
Техника заключается в добавлении в таблицу импортов (IAT) большого количества безобидных WinAPI, которые никогда не вызываются. Цель — «размыть» профиль импортов, чтобы статический анализатор не выделил подозрительные функции на фоне шума.
Принцип работы:
- Генерируется случайное значение во время компиляции (на основе
__TIME__).
- Создаётся условие, которое никогда не выполнится (например,
if (*A > 350), где*Aгарантированно меньше 255).
- Внутри недостижимой ветки перечисляются безобидные WinAPI:
MessageBoxA,GetLastError,IsWindowVisibleи т. д.
- Компилятор включает эти функции в IAT, но оптимизатор не удаляет их, потому что формально они «используются».
Подмена стандартных функций (memset, printf)
Другой приём — переопределение стандартных библиотечных функций. Например, собственная реализация
memset через #pragma intrinsic(memset) и #pragma function(memset) заставляет компилятор использовать пользовательскую версию вместо CRT. Это позволяет избежать импорта msvcrt.dll и связанных с ним сигнатур.Аналогично, макрос
PRINTA заменяет printf на цепочку HeapAlloc → wsprintfA → WriteConsoleA → HeapFree, убирая из импортов printf и puts.Что закрывает маскировку API
| Слой | Что закрывает |
|---|---|
| Динамический анализ вызовов (ETW, kernel callbacks) | Фиксирует реальные системные вызовы независимо от того, как они обёрнуты в пользовательском режиме |
| Sysmon / EDR-правила | Отслеживают не только имена функций, но и паттерны: последовательность аллокация → запись → создание потока |
| Kernel-mode мониторинг | PsSetCreateProcessNotifyRoutine, ObRegisterCallbacks и аналогичные механизмы видят операции на уровне ядра, минуя пользовательские обёртки |
Обход через «невозможные» условия и мёртвый код
Вставка кода, который никогда не выполнится, преследует две цели:
- Увеличить размер файла до порога, выше которого некоторые песочницы прекращают анализ (например, файлы > 100 МБ часто пропускаются).
- Запутать статический анализ. Дизассемблер и декомпилятор тратят время на разбор мёртвых веток, а сигнатурные движки вынуждены обрабатывать больший объём данных.
Пример из Evidence: условие
if (z > 5) при z = 4 никогда не истинно, но компилятор включает тело ветки в бинарный файл, если не может доказать недостижимость на этапе оптимизации.Что компенсирует мёртвый код и раздувание
- Символическое выполнение. Продвинутые анализаторы способны доказывать недостижимость веток и исключать их из анализа.
- Тайм-ауты и приоритизация в песочницах. Современные sandbox-решения не анализируют весь файл линейно, а фокусируются на путях выполнения, достижимых от точки входа.
- Контроль размера файла на шлюзе. Почтовые и веб-шлюзы могут блокировать или помещать в карантин файлы аномально большого размера.
Шифрование ключей и brute-force дешифровка
Одна из техник защиты полезной нагрузки — шифрование ключа, которым зашифрован основной код. В Evidence показан пример: ключ генерируется случайно, шифруется XOR с однобайтовым ключом
b, а при дешифровке b восстанавливается перебором (всего 256 вариантов) с использованием «байта-подсказки» (HintByte).Для статического анализатора это означает, что в файле нет ни открытого ключа, ни открытой полезной нагрузки. Однако защита слабая: перебор 256 значений тривиален.
Что компенсирует шифрование ключей
- Эмуляция в песочнице. Sandbox выполняет расшифровщик и получает доступ к расшифрованному коду в памяти.
- Контроль аномальных паттернов XOR. Последовательности вида
xor [reg], imm8в цикле с инкрементом — характерный признак распаковки/расшифровки.
- YARA-правила на структуру. Правила могут детектировать сам паттерн «зашифрованный блок + короткий расшифровщик», не зная конкретного ключа.
Обход через ожидание и тайминги
Техника
WaitForSingleObject((HANDLE)-1, INFINITE) — бесконечное ожидание без реального объекта. Некоторые песочницы имеют ограниченный тайм-аут анализа (30–120 секунд). Если вредоносный код «спит» дольше этого тайм-аута, песочница фиксирует только безобидное поведение и отпускает файл.Более продвинутые варианты:
- Проверка времени работы системы (
GetTickCount). Если система запущена менее N минут — вероятно, это песочница.
- Подсчёт количества запущенных процессов, объёма оперативной памяти, наличия специфических артефактов виртуализации.
Что компенсирует тайминговые обходы
- Увеличенные тайм-ауты песочниц. Современные решения анализируют файлы до 10–15 минут или используют ускорение времени (time acceleration).
- Детект анти-песочничных техник. Проверки
GetTickCount,NtQuerySystemInformationс определёнными классами, запросы к WMI — всё это фиксируется как подозрительное поведение.
- Поведенческий анализ на конечной точке (EDR). Даже если песочница не дождалась, EDR на хосте увидит вредоносные действия, когда они наконец произойдут.
Модель многослойной защиты: как слои дополняют друг друга
Ни один из перечисленных обходов не является универсальным, потому что каждый слой защиты оперирует разными артефактами:
| Уровень атаки | Что обходит | Что компенсирует |
|---|---|---|
| Модификация байтов | Сигнатуры | Эвристика, YARA, sandbox |
| Шифрование нагрузки | Сигнатуры + эвристика | Поведенческий анализ, memory scanning |
| Снижение энтропии | Статический анализ | Динамический анализ, EDR |
| IAT Camouflage | Статический анализ импортов | Динамический мониторинг вызовов, ETW |
| Мёртвый код / раздувание | Песочницы с тайм-аутом | Символическое выполнение, контроль на шлюзе |
| Тайминги и анти-песочница | Песочницы | Увеличенные тайм-ауты, EDR на хосте |
| Kernel-mode руткиты | Пользовательский мониторинг | Secure Boot, HVCI, kernel-mode EDR |
Практическая рамка для защитника
При построении или аудите системы детектирования полезно проверять покрытие по трём осям:
- Статическая ось. Покрывает ли решение файлы с модифицированными секциями, аномальной энтропией, нестандартными импортами? Есть ли YARA-правила на структурные паттерны (расшифровщик + jump)?
- Динамическая ось. Запускается ли файл в песочнице с достаточным тайм-аутом? Фиксируются ли системные вызовы через ETW или kernel callbacks, а не только через API-hooking в пользовательском режиме?
- Поведенческая ось на хосте. Видит ли EDR последовательности операций (аллокация → запись → создание удалённого потока), а не только отдельные вызовы? Есть ли правила на аномальные регионы памяти (RWX, PE-заголовок в данных)?
Если хотя бы одна из осей не покрыта, соответствующий класс техник обхода пройдёт незамеченным. Многослойность — не избыточность, а необходимость, продиктованная тем, что каждый отдельный сигнал имеет конечное и известное атакующему пространство обхода.
