На проверке Fitness-функции для evasive malware: как формализовать 'успех' обхода детекта в эволюционных алгоритмах

Тёмная лаборатория red-team, изогнутый монитор с графиком эволюционного алгоритма: точки мутаций PE-файлов, зелёная линия Парето-фронта, надписи DETECTION SCORE и GEN 47 на амбре и бирюзе. На з...


На третьей итерации собственного фреймворка для эволюционного тестирования ML-детекторов обнаружилась занятная штука: подавляющее большинство мутированных PE-сэмплов прошли статический классификатор на базе EMBER, но потеряли функциональность - reverse shell не устанавливался, DLL не загружалась, payload падал при декодировании. Проблема оказалась не в генетических операторах, не в представлении хромосомы и не в размере популяции. Проблема была в fitness-функции: она учитывала только detection score и полностью игнорировала functional correctness. По сути, GA научился ломать бинарь так, что AV его не узнавал - потому что узнавать было нечего.

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

Бизнес-логика: зачем атакующему эволюционный обход​

Создание нового malware с нуля - дорого и медленно. Переиспользование существующих бинарей с минимальными мутациями - дешевле на порядок. Как отмечают исследователи фреймворка GAME из CrySyS Lab, «building a whole new malware is difficult and expensive, so reusing existing malware - even at the binary level - while evading detection is a much cheaper path to success». Генетический алгоритм автоматизирует этот процесс: вместо ручного перебора трансформаций эволюция находит комбинации мутаций, которые одновременно обходят детект и сохраняют вредоносный функционал.

По данным IBM X-Force Threat Intelligence Index 2025, infostealers составили 32% всего обнаруженного malware - самый распространённый тип. Каждый из этих сэмплов проходит через цепочку детекторов: статические сигнатуры, эвристики, поведенческий анализ в sandbox. Для defensive research эволюционный подход ценнее ручного крафтинга: GA покрывает пространство мутаций, которое человек не способен перебрать вручную. По данным исследования AIMED, автоматическая генерация adversarial-сэмплов через эволюцию сокращает время достижения обхода до 50% по сравнению с классическим случайным подходом. Разница ощутимая - особенно когда sandbox-минуты стоят реальных денег.

Что именно оценивает fitness-функция при эволюции evasive malware​

Fitness-функция - численная оценка «качества» каждой особи (мутированного сэмпла) в популяции. В классическом GA она определяет, какие хромосомы переходят в следующее поколение, а какие отсеиваются. При эволюции evasive malware fitness-функция должна ответить на три вопроса одновременно:
  1. Обходит ли сэмпл детектор? - основной оптимизационный сигнал.
  2. Работает ли сэмпл? - мутации не должны ломать исходный функционал.
  3. Не создал ли сэмпл побочных артефактов? - раздувание файла, аномальная энтропия, нетипичная структура секций.
Простейшая fitness f(x) = 1 - detection_score отвечает только на первый вопрос. Именно поэтому наивные реализации быстро генерируют «обходящие» сэмплы, которые на деле - сломанные бинари. Ни атакующему, ни защитнику от них толку нет.

Detection score - основной оптимизируемый сигнал​

Detection score - ответ детектора на мутированный сэмпл. В зависимости от модели доступа к целевому детектору, сигнал принимает разные формы:

Бинарная метка (malicious / benign) - минимум информации, чистый black-box. Именно так работает GAME: «It only needs the final prediction label, so it works in a black-box setting» (CrySyS Lab). Годится, когда доступ к детектору ограничен (rate-limited API, облачный AV). Проблема - бинарный сигнал создаёт плоский fitness-ландшафт, где большинство особей получают одинаковую оценку. Гладкого градиента нет, сходимость медленная.

Вероятность (0.0–1.0) - детектор возвращает confidence score. Даёт более богатый градиент: fitness плавно растёт при снижении confidence, а не скачком при переходе через порог. Работает с ML-детекторами вроде MalConv или моделей на признаках EMBER. С сигнатурными движками, которые дают только да/нет - бесполезно.

Мультидетекторный вектор - VirusTotal API возвращает количество движков, пометивших файл. Fitness = 1 - (detections / total_engines). Самый информативный, но и самый медленный вариант: бесплатный ключ VirusTotal ограничен 4 запросами в минуту. Для популяции в 100 особей один цикл оценки занимает 25 минут - без учёта network latency. Считайте сами, сколько это стоит на 50 поколениях.

Многоступенчатый pipeline - раздельная оценка на статическом AV и поведенческом EDR. ShellForge использует именно этот подход: «multistage fitness evaluation pipeline that integrates both static and behavioral signals for defensive robustness assessment». Это наиболее полная модель: современные endpoint-решения комбинируют сигнатуры, эвристику и поведенческую телеметрию (ETW, kernel callbacks). Без доступа к sandbox-инфраструктуре - не взлетит, каждый поведенческий анализ стоит минуты реального времени.

Functional correctness - сэмпл обязан работать​

Мутация, которая ломает payload, бессмысленна - даже если она обходит все детекторы мира. Проверка functional correctness - второй обязательный компонент fitness-функции.

Подходы к верификации функциональности выстраиваются в спектр от быстрых и ненадёжных до медленных и точных:

Статическая проверка формата - валидация PE/ELF-заголовков, проверка целостности секций, корректность entry point. Выполняется за миллисекунды, но не гарантирует исполнимость: бинарь может быть валидным по формату и при этом падать при запуске. Красивый фантик, а внутри - мусор.

Динамическое исполнение в sandbox - запуск сэмпла в Cuckoo Sandbox или аналоге и проверка ожидаемого поведения (установка соединения, запись файла, вызов определённого API). Надёжно, но медленно: каждый вызов - секунды до минут. ShellForge решает это через каскадный pipeline: сначала статическая проверка (быстрый отсев), затем sandbox только для прошедших первый этап.

Proxy-метрики - проверка контрольных сумм критических участков кода, верификация декодирования payload без полного запуска. Компромисс между скоростью и надёжностью. На практике я использую CRC32 на shellcode-участке хромосомы: если мутация затронула декодер или payload напрямую - fitness обнуляется до sandbox-проверки. Дёшево и сердито.

В контексте Android-малвари (framework GEAAD) функциональность формулируется ещё жёстче: «The generated malware must be executable, and any modifications introduced in Android malware applications for evasion should not compromise the original attack». Любое изменение opcode distribution должно сохранять исполнимость APK.

Побочные эффекты мутаций: размер, энтропия, структурные аномалии​

Третий компонент fitness-функции - минимизация «шума», который мутации вносят в сэмпл. Файл, раздутый на 300% с аномальной энтропией, вызовет подозрение у любого аналитика, даже если формально проходит автоматический детект. Аналитик не дурак - он увидит секцию .rdata размером в полмегабайта и полезет смотреть руками.

GAME явно включает этот аспект: fitness «measures not only whether it evades detection, but also how well it keeps the attacker's practical goals in mind, such as minimal file size growth» (CrySyS Lab).

Типичные штрафные метрики:

МетрикаЧто отслеживаетЛогика штрафа
Delta размера файлаПрирост в байтах/процентахШтраф при превышении порога (обычно 10-15%)
Энтропия секцийАномальная энтропия (>7.5 для PE)Штраф при отклонении от baseline
Количество секцийДобавление новых секций в PE/ELFДискретный штраф за каждую добавленную секцию
Соотношение кода и данныхНепропорциональный рост .data/.rdataШтраф при отклонении от исходного ratio

Скалярная свёртка vs Pareto-оптимизация​

Имея три группы метрик (detection, functionality, artifacts), нужно решить: сворачивать их в одно число или оптимизировать как вектор.

Скалярная свёртка (weighted sum) - подход большинства опубликованных фреймворков:
Python:
# пример для демонстрации концепции
def fitness(sample, detector, sandbox, orig_size):
    det_score = 1.0 - detector.predict_proba(sample)
    func_ok = 1.0 if sandbox.verify(sample) else 0.0
    size_pen = max(0, (len(sample) - orig_size) / orig_size - 0.1)
    return w1 * det_score + w2 * func_ok - w3 * size_pen
ShellForge использует скалярную свёртку с «Total Fitness Score», объединяющим статические и поведенческие сигналы в одну величину. Плюс - простота интеграции с любым GA-движком (DEAP, pymoo, custom). Минус - выбор весов w1, w2, w3 субъективен и критически влияет на результат. Перекос в сторону detection score воспроизводит проблему «сломанных, но необнаруженных» сэмплов. Перекос в сторону functional correctness замедляет эволюцию обхода до неприемлемых сроков. На одном из экспериментов я выставил w2=0.8 - GA за 200 поколений ни разу не обошёл даже EMBER-классификатор, зато все сэмплы работали безупречно. Толку от этого - ноль.

Pareto-оптимизация (NSGA-II, MOEA/D) - альтернатива для исследовательских пайплайнов. Вместо одного числа оперируем вектором целей: [detection_evasion, functional_correctness, artifact_minimization]. Результат - множество Парето-оптимальных решений, каждое из которых представляет свой компромисс. Исследователь выбирает точку на фронте в зависимости от задачи: для defensive testing важнее покрытие разных стратегий обхода, для adversarial training - максимальный evasion rate при гарантированной функциональности.

Когда какой подход:
  • Скалярная свёртка - для production-пайплайнов с фиксированными приоритетами. Быстрее, проще в отладке, достаточна когда задача «максимизировать обход при жёстком constraint на функциональность».
  • Pareto - для исследовательского поиска, когда нужно понять ландшафт компромиссов. Медленнее, требует больше ресурсов и визуализации фронта Парето, но даёт принципиально больше информации о слабых местах детектора.

Модель доступа к детектору: black-box, grey-box, white-box​

Тип fitness-функции определяется тем, какой информацией о детекторе располагает исследователь.

Black-box. Доступна только финальная метка или confidence score. Никакой информации о внутренней архитектуре модели. GAME работает в этом режиме. Это самый реалистичный сценарий - в реальном мире атакующий не видит веса модели CrowdStrike Falcon или Microsoft Defender ATP. Обратная сторона - медленная сходимость: GA вынужден «нащупывать» направление без градиента. Сотни поколений вслепую.

Grey-box. Частичная информация: известен тип модели (Random Forest, gradient boosting), набор признаков (opcode frequencies, API calls), но не конкретные веса. GEAAD (DOpGAN) работает в grey-box режиме: зная, что детектор использует opcode distribution features, фреймворк целенаправленно мутирует именно эти признаки через OFOA-алгоритм (Opcode Frequency Optimal Adjustment). Fitness становится направленной - вместо случайного перебора оптимизация идёт в пространстве известных признаков. Десятки поколений вместо сотен.

White-box. Полный доступ к модели: архитектура, веса, функция потерь. Позволяет использовать gradient-based атаки (FGSM, PGD) для вычисления «идеальной» мутации за один шаг. Генетический алгоритм здесь, как правило, избыточен - градиентные методы быстрее. Но white-box доступ к production-детектору - исключение, а не правило.

Модель доступаСигнал от детектораСкорость сходимости GAРеалистичность сценария
Black-boxМетка или confidenceНизкая (сотни поколений)Высокая
Grey-boxТип модели + набор признаковСредняя (десятки поколений)Средняя
White-boxПолная модель + весаGA не нужен (градиент)Низкая

Практические паттерны из исследований​

ShellForge - многоступенчатый fitness pipeline​

ShellForge формализует эволюцию reverse-shell вариантов как «constrained optimization problem under detection feedback». Ключевая идея - каскадный fitness: сначала дешёвые проверки, потом дорогие.

Этапы оценки каждой хромосомы:
  1. Syntactic validation - проверка корректности мутированного payload. Мгновенная, отсеивает значительную часть популяции.
  2. Static AV scan - оценка сигнатурными движками. Быстрая (секунды). Формирует первую компоненту fitness.
  3. Behavioral EDR analysis - запуск в sandbox с EDR-телеметрией. Медленная (минуты). Только для сэмплов, прошедших первые два этапа.
  4. Total Fitness Score - взвешенная сумма результатов всех стадий.
Каскадная архитектура экономит 60-70% вычислительного бюджета: sandbox-вызовы происходят только для «перспективных» кандидатов. Для GA с популяцией 100 особей и 50 поколений разница между наивным подходом (5000 sandbox-вызовов) и каскадным (1500-2000) - дни против часов.

GA-операторы ShellForge: tournament selection, uniform crossover, random mutation - классический набор. Вся специфика evasion-задачи сконцентрирована в fitness-функции, а не в операторах. И это правильно - не надо усложнять там, где не надо.

GAME - implicit preferences через fitness​

GAME (CrySyS Lab, 2026) использует принципиально другой подход к проектированию fitness: вместо явных весов для каждого компонента, предпочтения атакующего «вшиваются» в саму структуру оценки через набор примитивов модификации. Исследователи формулируют: «we only make reasonable assumptions about the set of modification primitives available to the attacker and model preferences of the attacker implicitly in the fitness function».

Три примитива модификации для ELF-бинарей:
  • Append data - добавление данных в конец файла. Минимальный impact на структуру, но легко обнаруживается эвристиками.
  • Padding overwrite - перезапись неиспользуемых padding-областей. Не увеличивает размер файла. Чистая работа.
  • Segment injection - внедрение нового сегмента. Максимально агрессивная мутация.
По результатам экспериментов GAME на SIMBIoTA-ML (Random Forest classifier, ARM malware), «strategies using padding overwrite and segment injection were often the strongest, since they produce evading malware samples with hard to detect structural anomalies». Сходимость - к ~32 поколению. GAME работает в чистом black-box - детектору не передаётся ничего, кроме бинаря, и от него ожидается только метка. Минимум допущений - максимум реалистичности.

Типичные ошибки при проектировании fitness-функции​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Ошибка 5: отсутствие baseline сравнения. Без сравнения с random mutation невозможно доказать, что GA находит что-то нетривиальное. Если random перебор даёт 40% evasion rate, а GA - 42%, сложная fitness-функция не оправдывает свою вычислительную стоимость. Baseline - обязательная часть любого эксперимента с эволюционным обходом. Без него вы не знаете, работает ли ваш GA или просто дорого бросает монетку.



Проектирование fitness-функции для adversarial malware research - это на 80% инженерия ограничений и на 20% оптимизация целевой метрики. Каждый раз, когда начинаешь с «оптимизируем detection score, а потом добавим constraints», заканчивается переписыванием пайплайна с нуля. Проверено.

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

Проблема большинства опубликованных фреймворков - они проектируются «от атаки» (обойти детект) вместо «от измерения» (понять, где именно детектор деградирует). Это не семантическая разница: она определяет, генерирует ли fitness-функция знание - какие классы трансформаций вскрывают слепые пятна - или мусор: тысячи сломанных бинарей с нулевым detection score.

Второй вывод: многоступенчатый fitness - не архитектурный изыск, а условие выживания эксперимента. Sandbox-вызовы стоят реальных денег (облако) или реального времени (локальный Cuckoo), и каскадный отсев - единственный способ вписать эволюцию в разумный бюджет.

И третье. GA в adversarial ML работает не потому, что он «умнее» gradient-based методов, а потому что не требует дифференцируемости fitness-ландшафта. Когда детектор - чёрный ящик с бинарным ответом, градиент просто невозможен. Ближайший год-два покажут, займут ли LLM-based mutation стратегии эту нишу - по данным CrowdStrike, вредоносное использование GenAI для социальной инженерии удвоилось за 2024 год, и переход от генерации фишинговых писем к генерации evasive payloads выглядит неизбежным. Но какой бы мутационный движок ни использовался - GA, LLM или гибрид - без корректно спроектированной fitness-функции он будет генерировать шум, а не знание.

Попробуйте собрать минимальный пайплайн: EMBER-классификатор + три примитива мутаций из GAME + CRC32-проверка на shellcode-участке. Если после 50 поколений evasion rate не превышает random baseline - проблема в fitness, а не в операторах.
 
Мы в соцсетях:

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

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab