Снижение вероятности детекта вредоносного кода: какие защитные слои компенсируют обход отдельных сигналов

Почему один слой защиты не работает​


Любой механизм детектирования — сигнатурный, эвристический, поведенческий — имеет конечное пространство признаков, на которые он опирается. Атакующий, понимая эти признаки, может модифицировать артефакты так, чтобы конкретный слой их не увидел. Это не теоретическая проблема: техники снижения детекта (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, которые никогда не вызываются. Цель — «размыть» профиль импортов, чтобы статический анализатор не выделил подозрительные функции на фоне шума.

Принцип работы:

  1. Генерируется случайное значение во время компиляции (на основе __TIME__).
  2. Создаётся условие, которое никогда не выполнится (например, if (*A > 350), где *A гарантированно меньше 255).
  3. Внутри недостижимой ветки перечисляются безобидные WinAPI: MessageBoxA, GetLastError, IsWindowVisible и т. д.
  4. Компилятор включает эти функции в IAT, но оптимизатор не удаляет их, потому что формально они «используются».

Подмена стандартных функций (memset, printf)​


Другой приём — переопределение стандартных библиотечных функций. Например, собственная реализация memset через #pragma intrinsic(memset) и #pragma function(memset) заставляет компилятор использовать пользовательскую версию вместо CRT. Это позволяет избежать импорта msvcrt.dll и связанных с ним сигнатур.

Аналогично, макрос PRINTA заменяет printf на цепочку HeapAllocwsprintfAWriteConsoleAHeapFree, убирая из импортов printf и puts.

Что закрывает маскировку API​


СлойЧто закрывает
Динамический анализ вызовов (ETW, kernel callbacks)Фиксирует реальные системные вызовы независимо от того, как они обёрнуты в пользовательском режиме
Sysmon / EDR-правилаОтслеживают не только имена функций, но и паттерны: последовательность аллокация → запись → создание потока
Kernel-mode мониторингPsSetCreateProcessNotifyRoutine, ObRegisterCallbacks и аналогичные механизмы видят операции на уровне ядра, минуя пользовательские обёртки

Обход через «невозможные» условия и мёртвый код​


Вставка кода, который никогда не выполнится, преследует две цели:

  1. Увеличить размер файла до порога, выше которого некоторые песочницы прекращают анализ (например, файлы > 100 МБ часто пропускаются).
  2. Запутать статический анализ. Дизассемблер и декомпилятор тратят время на разбор мёртвых веток, а сигнатурные движки вынуждены обрабатывать больший объём данных.

Пример из 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

Практическая рамка для защитника​


При построении или аудите системы детектирования полезно проверять покрытие по трём осям:

  1. Статическая ось. Покрывает ли решение файлы с модифицированными секциями, аномальной энтропией, нестандартными импортами? Есть ли YARA-правила на структурные паттерны (расшифровщик + jump)?
  2. Динамическая ось. Запускается ли файл в песочнице с достаточным тайм-аутом? Фиксируются ли системные вызовы через ETW или kernel callbacks, а не только через API-hooking в пользовательском режиме?
  3. Поведенческая ось на хосте. Видит ли EDR последовательности операций (аллокация → запись → создание удалённого потока), а не только отдельные вызовы? Есть ли правила на аномальные регионы памяти (RWX, PE-заголовок в данных)?

Если хотя бы одна из осей не покрыта, соответствующий класс техник обхода пройдёт незамеченным. Многослойность — не избыточность, а необходимость, продиктованная тем, что каждый отдельный сигнал имеет конечное и известное атакующему пространство обхода.

Источники​


 
Назад
Верх Низ