Статья Как обманывают ML-классификаторы малвари

1773361950623.webp

Статические ML-классификаторы давно заняли своё место в AV, EDR и sandbox-решениях. Они помогают быстро оценивать PE-файлы до запуска, опираясь на признаки вроде структуры секций, импортов, строк, энтропии, заголовков и агрегированных наборов признаков вроде EMBER. Это даёт защитнику масштабируемость и скорость, но вместе с этим открывает новую поверхность атаки.

Проблема в том, что такая модель принимает решение не по смыслу поведения образца, а по его признаковому представлению. Для классификатора вредоносный файл - это не намерение, а набор корреляций, связанных с malicious или benign классом. Если этими корреляциями можно управлять, не ломая сам payload, появляется пространство для adversarial attacks - модификаций, которые сдвигают вердикт модели в нужную сторону при сохранении функциональности файла.

На этом и строится вся практическая сложность темы. Атаковать признаки в feature space сравнительно удобно: модель видна как функция, decision boundary можно исследовать, а градиентные методы вроде FGSM или PGD быстро показывают, насколько хрупким бывает классификатор. Но между успешной атакой на вектор признаков и успешной атакой на реальный PE лежит инженерный зазор: ограничения формата, валидность структуры, исполняемость и необходимость перенести изменение признаков обратно в файл без потери работоспособности.

Именно поэтому adversarial attacks против PE-классификаторов интересны не только как исследовательская задача, но и как практический тест на зрелость статического ML-детекта. Высокая точность ещё не означает устойчивость. Куда важнее понять, какие признаки модель считает значимыми, насколько ими можно управлять на уровне файла и в какой момент красивая устойчивость на датасете заканчивается при первой же направленной модификации образца.

Почему статический ML-классификатор малвари стал удобной целью для adversarial attacks​

Статическая ML-классификация вошла в детект малвари не потому, что кто-то решил заменить сигнатуры модным словом AI. Причина куда прозаичнее: классические правила плохо масштабируются на поток новых образцов, быстро упираются в вариативность упаковщиков, модифицированных семейств и автоматизированной сборки, а ручная аналитика слишком дорога, чтобы держать на ней ранний фильтр. Модель, которая умеет за доли секунды оценить PE-файл до запуска, в такой схеме выглядит очень выгодно. Она не требует эмуляции, не ждёт поведенческих событий, не гоняет образец по всей цепочке динамического анализа и при этом даёт защитнику ещё один слой отбора.

Но за эту скорость приходится платить тем, как именно модель видит файл.

Какие признаки PE-файла видит модель​

FireShot Capture 003 -  - [].webp

Статический классификатор почти никогда не работает с бинарником как с последовательностью инструкций в полном смысле. Обычно между файлом и моделью стоит feature extractor - слой извлечения признаков, который превращает PE в численное представление. Именно это представление и становится объектом классификации.

Состав таких признаков может различаться, но в реальных пайплайнах почти всегда встречаются одни и те же группы. Во-первых, это структурные признаки формата PE: размер и количество секций, их имена, виртуальные адреса, размеры в памяти и на диске, флаги доступа, характеристики заголовков, значения полей optional header, сведения о подсистеме, наличии релокаций, сертификатов, отладочной информации и других частей образа. Во-вторых, импортируемые API и DLL - один из самых старых и до сих пор полезных сигналов, особенно если модель учитывает не только наличие конкретного импорта, но и его сочетание с другими артефактами. В-третьих, строковые и статистические признаки: энтропия секций, распределение байтов, гистограммы, печатаемые строки, n-граммы, агрегированные характеристики содержимого. Наконец, поверх всего этого часто строят более компактные наборы признаков вроде EMBER, где низкоуровневые свойства файла уже сведены в вектор фиксированной длины.

Ключевой момент здесь в другом: модель видит не "вредоносную логику", а следы, которые эта логика оставила в артефактах файла. Для человека PE может быть очевидно подозрительным из-за цепочки импорта, структуры секций, упаковки и странной смеси API. Для модели всё это - просто координаты в признаковом пространстве. Она не понимает, зачем образцу нужны VirtualAlloc, WriteProcessMemory или CreateRemoteThread. Она видит, что подобные комбинации часто встречались в malicious-классе, а другие наборы свойств - в benign. Это не семантика, а статистическая аппроксимация.

Именно поэтому статический ML-классификатор может выглядеть очень сильным на валидации и одновременно быть уязвимым как объект атаки. Если итоговый вердикт зависит от набора инженерно извлекаемых признаков, атакующий получает шанс работать не против сигнатуры и не против ручного правила, а против самого представления, на котором держится классификация.

Почему вердикт модели опирается на корреляции, а не на семантику поведения​

Это место часто упрощают до банальной формулы "модель не понимает смысл". Формально она верна, но для детекта малвари этого недостаточно. Важно другое: статический классификатор почти всегда учится на корреляциях, часть из которых действительно связана с вредоносным поведением, а часть - только сопровождает его в обучающей выборке.

Если в датасете вредоносные образцы часто несут высокую энтропию секций, редкие имена секций, подозрительные сочетания импортов, а доброкачественные файлы чаще содержат стабильный набор библиотек, нормальный layout и более предсказуемую статистику содержимого, модель начнёт использовать эти различия как сигнал. Это рационально с точки зрения обучения. Проблема в том, что такой сигнал не всегда устойчив. Он может быть сильным, но не обязательно причинным. И как только атакующий понимает, что решение держится на управляемых корреляциях, появляется возможность сдвинуть объект ближе к benign-профилю без переписывания основной вредоносной логики.

Отсюда и возникает особая уязвимость статических PE-классификаторов. Они зависят от признаков, которыми часто можно управлять хотя бы частично. Иногда это грубые манипуляции - добавление лишних данных, новых секций, benign-подобных импортов, изменение статистического профиля файла. Иногда более аккуратные - изменение отдельных компонентов вектора признаков, если атакующий работает в исследовательском white-box сценарии. Но суть одна: модель отделена от поведения файла промежуточным слоем признаков, а значит атаковать можно не только сам объект, но и способ его представления.

На этом фоне статический классификатор становится удобной целью не потому, что он слабый по определению, а потому, что его граница решения можно исследовать и давить через признаки, которые не тождественны исполняемой логике. В этом и состоит главный интерес adversarial attacks для детекта малвари. Вопрос не только в том, может ли модель ошибиться. Куда важнее, можно ли заставить её ошибиться направленно - так, чтобы файл остался рабочим, а его признаковый профиль стал менее подозрительным.

Именно поэтому высокая точность сама по себе здесь мало что значит. Модель может уверенно разделять benign и malicious в обычной выборке, но это ещё не говорит о её устойчивости к управляемым модификациям. Если классификатор переоценивает хрупкие признаки или слишком сильно опирается на статистические артефакты, то атакующий получает пространство для обхода детекта через изменение признаков без обязательной поломки самого payload. И с этого момента задача перестаёт быть просто задачей классификации. Она становится задачей устойчивости модели к изменениям, которые для неё критичны, а для самого PE-файла могут оказаться вполне допустимыми.

Где рождается adversarial evasion​

Adversarial обход начинается не с append attack и не с manipulation импортов. Он начинается в тот момент, когда классификатор перестаёт видеть файл как исполняемый объект и начинает видеть его как точку в признаковом пространстве. Для аналитика вредоносный PE - это код, поведение, цепочка вызовов, следы упаковки, а иногда и вполне узнаваемая логика семейства. Для модели - вектор признаков, положение которого относительно decision boundary определяет итоговый вердикт.

Именно поэтому обход статического классификатора устроен иначе, чем обход сигнатурного детекта. Сигнатуру обычно ломают через конкретный шаблон. ML-модель приходится сдвигать через границу решения. Это уже не борьба с одним правилом, а работа против того, как классификатор разделяет malicious и benign объекты в feature space.

Decision boundary в задаче детекта малвари​

В общем виде всё выглядит довольно сухо: есть входной вектор признаков, есть функция модели, есть score и есть порог, по которому объект попадает в malicious или benign класс. Но в реальной задаче детекта малвари за этой сухой схемой скрывается важная вещь - граница решения почти никогда не отражает "суть вредоносности". Она отражает то, как модель научилась использовать различия в обучающем датасете.

Если определённые сочетания импортов, высокая энтропия секций, характерные размеры, особенности layout PE или специфическая байтовая статистика чаще встречались у вредоносных файлов, именно они начинают формировать рельеф decision boundary. Модель не обязана понимать, почему это опасно. Ей достаточно того, что эти признаки помогают разделять классы на обучении.

Отсюда и возникает уязвимость. Пока входные данные похожи на обычные объекты из датасета, классификатор может выглядеть вполне уверенно. Но если атакующий начинает менять признаки не случайно, а в тех направлениях, которые сильнее всего влияют на score, устойчивость решения быстро оказывается слабее, чем кажется. Для adversarial attack этого достаточно: не нужно менять смысл файла целиком, достаточно найти такой сдвиг, после которого объект пересечёт decision boundary и модель отдаст более мягкий вердикт.

Почему изменение признаков не обязано ломать payload​

При детекте малвари это особенно важно, потому что далеко не все признаки жёстко привязаны к полезной нагрузке. Часть из них описывает вторичные свойства PE-файла - структуру секций, заголовочные поля, статистику содержимого, наличие дополнительных данных, состав импортов или агрегированные характеристики, которые влияют на классификацию, но не всегда критичны для выполнения образца.

Именно поэтому semantic-preserving modification - изменение, сохраняющее функциональность - здесь не выглядит экзотикой. Атакующий не обязан переписывать payload, менять логику загрузки или трогать исполняемый участок кода. Во многих случаях достаточно работать вокруг него: добавить данные в допустимую область файла, создать новую секцию, загрязнить импорт-таблицу benign-подобными API или изменить статистический профиль объекта так, чтобы модель увидела менее подозрительное представление.

Для классификатора это может быть значимым сдвигом признаков. Для самого файла - допустимой модификацией. И именно здесь возникает главный риск для статического ML-детекта: вредоносность образца и его "подозрительность" для модели - не одно и то же. Если вердикт держится на признаках, которыми можно управлять без разрушения payload, то направленный обход становится вполне инженерной задачей, а не лабораторным трюком.

Feature space против file space​

Здесь проходит главный разлом всей темы. В feature space атакующий работает с тем представлением, которое уже прошло через feature extractor. Это удобная исследовательская среда: можно смотреть, какие признаки сильнее всего давят на классификатор, оценивать вклад отдельных компонент, считать градиенты и строить направленное возмущение, которое двигает объект через decision boundary.
FireShot Capture 002 -  - [].webp

В feature space хорошо видно, насколько модель уязвима как функция. Именно здесь особенно наглядны white-box атаки вроде FGSM или PGD: они показывают, что даже относительно небольшой, но правильно направленный сдвиг признаков может заметно изменить score. Проблема в том, что этот уровень даёт только половину ответа.

Успешная атака на признаки ещё не означает, что получившееся изменение можно безопасно перенести в реальный PE. Для детекта малвари это особенно жёсткое ограничение. Многие признаки дискретны, структурно связаны друг с другом или выводятся из тех частей файла, которые нельзя менять произвольно. Нельзя просто уменьшить одну компоненту вектора и оставить всё остальное на месте. Любое реальное изменение приходится реализовывать через допустимую модификацию бинарника.

Отсюда и начинается file space. Здесь атакующий работает уже не с абстрактным вектором, а с PE-файлом как с объектом формата. Нужно сохранить валидность заголовков, не разрушить загрузку образа, не повредить критичные зависимости внутри файла и по возможности не сломать сам payload. Атака становится грубее, грязнее и инженерно сложнее, но именно здесь проверяется её практическая ценность.

И в этом есть неприятный для защитника момент. Некоторые file-space модификации выглядят менее элегантно, чем классические adversarial perturbation в feature space, но зато вполне работают против статических PE-классификаторов. Append attack, section injection и import manipulation не всегда минимальны в строгом математическом смысле, но они дают контролируемый сдвиг признаков в области, где файл остаётся валидным. Для модели этого часто уже достаточно.

Практический adversarial evasion и рождается на стыке этих двух уровней. Feature space показывает, где классификатор уязвим как модель. File space показывает, можно ли превратить эту уязвимость в реальную атаку на PE-файл. Если смотреть только на первый слой, легко переоценить реализуемость атаки. Если смотреть только на второй, легко не понять, почему одни модификации двигают вердикт, а другие нет. Нормальный разбор начинается именно там, где эти два мира приходится сводить вместе.

Gradient-based атаки как способ нащупать слабые места модели​

Если смотреть на adversarial attacks только через file-space модификации, можно легко потерять главное: практический обход начинается не с append attack и не с PE surgery, а с понимания того, какие именно направления в признаковом пространстве для модели наиболее болезненны. Именно здесь и появляются градиентные атаки. Для детекта малвари они важны не потому, что любой реальный атакующий будет буквально считать градиент по production-классификатору, а потому, что такие методы хорошо вскрывают механику уязвимости. Они показывают, насколько легко decision boundary можно продавить направленным изменением признаков.

В задачах компьютерного зрения градиентные атаки давно стали почти базовой темой. В детекте малвари всё интереснее и жёстче. Здесь вход модели - не непрерывное изображение, а вектор признаков, часть которых дискретна, агрегирована или завязана на структуру PE. Поэтому сам факт того, что градиент указывает полезное направление для атаки, ещё не означает, что найденное возмущение можно безболезненно отобразить обратно в реальный файл. Но как исследовательский инструмент эти методы всё равно очень сильны: они позволяют быстро понять, насколько классификатор зависит от хрупких признаков и где именно проходит его наиболее уязвимая зона.

FGSM: одношаговый сдвиг через границу решения​

FGSM - Fast Gradient Sign Method - это, по сути, минималистичный способ проверить, насколько локально устойчива модель к направленному изменению входа. Идея простая: если у нас есть функция потерь модели, можно взять градиент этой потери по входным признакам и сдвинуть вход в сторону, которая увеличивает ошибку классификации. В классическом виде атака выглядит так:
Python:
x_adv = x + epsilon * sign(grad_x J(theta, x, y))
Здесь x - исходный вектор признаков, epsilon - бюджет изменения, J - функция потерь, а sign(grad_x) даёт направление, в котором каждый признак нужно толкнуть, чтобы ухудшить решение модели. Визуально это очень грубая атака: она не пытается найти тонкое оптимальное изменение, а просто делает один шаг в наиболее выгодную сторону.

Для malware классификатора этого часто уже достаточно, чтобы увидеть неприятную вещь. Если даже одношаговое возмущение заметно снижает malicious score или вообще меняет класс, значит локальная геометрия decision boundary довольно хрупкая. Модель опирается на признаки, которые можно продавить быстро и без сложной оптимизации. Именно поэтому FGSM ценен не как вершина adversarial research, а как быстрый тест на хрупкость классификатора.

Но в детекте малвари почти сразу всплывает ограничение. FGSM работает в feature space, а значит двигает не PE-файл, а его численное представление. Если среди признаков есть бинарные флаги, счётчики, категориальные компоненты, агрегаты по import table или структуре секций, то сдвиг "на epsilon в нужную сторону" может быть математически допустим для вектора и одновременно бессмысленен для реального PE. Модель покажет уязвимость, но это ещё не значит, что у атакующего уже есть рабочий file-space обход.

Именно поэтому FGSM в этой теме стоит воспринимать как диагностику. Он показывает, можно ли в принципе быстро столкнуть объект к benign-области, если работать против локального поведения модели. Если да - это сильный сигнал, что классификатор переоценивает уязвимые признаки и слишком уверенно разделяет классы на поверхности, которая плохо выдерживает направленное возмущение.

PGD: итеративное продавливание классификатора​

PGD - Projected Gradient Descent - развивает ту же идею, но делает её гораздо жёстче. Вместо одного шага атака идёт итеративно: на каждом шаге вход немного сдвигается в сторону увеличения ошибки, а затем проецируется обратно в допустимую область изменений. Это уже не грубый пинок в decision boundary, а последовательное продавливание объекта через локально выгодные направления.

Упрощённо это выглядит так:
Python:
x_adv_0 = x
x_adv_{t+1} = Project(x_adv_t + alpha * sign(grad_x J(theta, x_adv_t, y)))
Смысл здесь в двух вещах. Во-первых, PGD позволяет атаке адаптироваться по ходу движения: после каждого шага она пересчитывает градиент и уточняет направление. Во-вторых, ограничение через projection не даёт уйти за пределы заданного бюджета возмущения. В image-домене это обычно интерпретируют как ограничение малости perturbation. В детекте малвари смысл похожий, но трактовать его нужно аккуратнее: "малое" изменение в векторе признаков не обязательно означает малое или безопасное изменение в PE-файле.

Для статических malware-моделей PGD обычно даёт более реалистичную оценку хрупкости, чем FGSM. Если классификатор держится только против одношагового возмущения, но разваливается при нескольких итерациях, это уже плохой сигнал. Значит вокруг исходного объекта есть не просто одно локально опасное направление, а целая область, где решение модели можно систематически ухудшать. Для атакующего это важно, потому что многие real-world модификации PE тоже вносятся не одним идеальным движением, а серией изменений, каждое из которых немного двигает признаки.

Но и здесь действует тот же предел. PGD отлично показывает, что вектор признаков можно довести до состояния, где классификатор почти уверен в benign-метке. Однако чем дальше атака уходит от исходного вектора, тем выше риск, что это решение вообще нельзя реализовать в file space без поломки структуры PE. В этом смысле PGD иногда даже опаснее для интерпретации, чем FGSM: он даёт более сильный исследовательский результат, и на этом месте легко переоценить практическую значимость. Успешный PGD на EMBER-features - это доказательство уязвимости модели, но не автоматическое доказательство существования эквивалентной PE-модификации.

Почему успех в признаковом векторе ещё не означает успех в реальном PE​

Это и есть главный предел всех gradient-based атак в детекте малвари. Они работают с тем, что model-ready pipeline уже превратил в численное представление. Атаке удобно двигать признаки как координаты, но PE-файл так не устроен. В нём признаки связаны с конкретными артефактами формата: секциями, заголовками, импортами, байтовой статистикой, строками, layout и массой зависимостей между ними.

Из-за этого один и тот же adversarial step может иметь очень разный смысл на двух уровнях. В feature space он выглядит как локальное улучшение позиции объекта относительно decision boundary. В file space он может означать необходимость добавить новую секцию, поменять import table, пересчитать смещения, сохранить выравнивание, не повредить загрузку образа и при этом случайно не создать новый артефакт, который сделает файл ещё более подозрительным. То есть математически удобное изменение почти всегда оказывается инженерно дороже, чем выглядит в векторе.

Есть и ещё одна неприятная деталь. Многие признаки в malware classification не независимы. Изменение одной характеристики файла тянет за собой каскад других. Добавили секцию - поменялись размеры, количество секций, layout, статистика содержимого, иногда строки, иногда общая энтропия. Загрязнили import table - затронули сразу несколько групп признаков. Поэтому атака, которая в feature space выглядит как точечный сдвиг пары компонент, в реальном PE часто превращается в связанный пакет изменений с неочевидным побочным эффектом.

Именно поэтому градиентные методы в этой статье важны не как "готовый рецепт обхода", а как инструмент картирования слабых мест модели. Они отвечают на вопрос, где классификатор наиболее хрупок и насколько уверенно он держится за признаки, которыми потенциально можно манипулировать. Это уже очень ценно. Если даже на уровне признакового вектора модель легко уходит в ошибку под направленным давлением, значит её устойчивость явно переоценена. А дальше начинается уже следующая, более грязная часть задачи: какие из этих уязвимостей вообще переживают перенос в реальный PE-файл.

Practical evasion в реальном PE-файле​

В пространстве признаков атака выглядит аккуратно. Есть вектор признаков, есть оценка модели, есть направление, в котором объект выгодно сдвигать. В реальном PE всё намного грязнее. Здесь нельзя просто подвинуть одну компоненту вектора и посмотреть, как изменится вердикт. Любая модификация проходит через ограничения формата, структуру бинарника, выравнивание, взаимосвязь заголовков и, главное, через необходимость сохранить payload в рабочем состоянии. Поэтому practical evasion - это уже не про красивую оптимизацию, а про контроль над теми артефактами файла, из которых извлекатель признаков собирает итоговое представление для модели.

На этом уровне особенно хорошо видно, почему статический ML-детект уязвим не только к атакам с полным знанием модели, но и к куда более грубым преобразованиям. Если классификатор опирается на статистический профиль файла, структуру секций, таблицу импортов и другие управляемые артефакты, атакующему не всегда нужно точно воспроизводить adversarial perturbation из пространства признаков. Иногда достаточно провести допустимую модификацию PE, которая заметно сдвинет признаки в benign-подобную область.
FireShot Capture 004 -  - [].webp

Смысл всех practical evasion-техник один и тот же: атакующий работает не с вредоносностью как таковой, а с теми свойствами файла, которые модель использует как косвенные сигналы. Поэтому здесь важна не только сила самой модификации, но и то, насколько предсказуемо она сдвигает признаки, насколько безопасна для PE и насколько заметна для дополнительного анализа.

Append attacks и сдвиг статистических признаков​

Append attack - один из самых простых и потому особенно показательных вариантов обхода на уровне файла. В конец PE-файла добавляются дополнительные данные, которые не участвуют в выполнении образа напрямую. Payload не меняется, логика исполнения обычно остаётся прежней, а вот признаки, связанные с размером файла, распределением байтов, общей статистикой содержимого и агрегированными наборами признаков, могут заметно сдвинуться.

Почему это вообще работает - неприятный вопрос для защитника. Многие статические классификаторы используют признаки, завязанные на общий профиль файла, а не только на критичные для выполнения участки. Если извлекатель признаков учитывает энтропию, байтовые гистограммы, доли печатаемых строк, относительные размеры и другие агрегаты, добавленные данные могут изменить итоговый вектор достаточно сильно, чтобы модель увидела менее подозрительный объект. Особенно если добавляемое содержимое подбирается не случайно, а так, чтобы статистически быть ближе к benign-подобным образцам.

Но у append attack есть жёсткий потолок. Не все модели одинаково чувствительны к таким изменениям. Если классификатор ориентируется на более устойчивые структурные признаки, польза от добавленного хвоста быстро падает. Кроме того, такой приём не даёт тонкого контроля над тем, какие именно признаки изменятся и насколько. Это скорее широкий статистический толчок, чем точечная модификация.

Section injection и управление структурными артефактами​

Если append attack работает в первую очередь по общему статистическому фону, то section injection бьёт уже по структуре PE гораздо заметнее. Здесь в бинарник добавляется новая секция с контролируемыми параметрами - именем, размером, расположением, флагами доступа, выравниванием и содержимым. Для классификатора это уже не просто изменение хвоста файла, а сдвиг сразу нескольких групп признаков: количества секций, их распределения, характеристик заголовков, общей структуры образа и статистики содержимого.

С практической точки зрения этот подход интереснее append attack, потому что даёт атакующему больше контроля. Можно выбирать, насколько крупной будет новая секция, какие атрибуты она получит, каким будет её наполнение и как именно она впишется в структуру PE. Если модель использует секционные признаки как важную часть логики классификации, section injection может дать более сильный и более направленный эффект, чем простое добавление данных в конец файла.

Но цена у этого контроля выше. При неаккуратной модификации легко повредить таблицу секций, сломать выравнивание, сместить адреса внутри образа и нарушить согласованность заголовков. Ошибка здесь стоит дороже, чем в append attack: payload может перестать исполняться, загрузчик может интерпретировать файл некорректно, а сам модифицированный PE станет подозрительным уже на уровне структурного анализа.

Тем не менее section injection остаётся одной из самых показательных practical evasion-техник. Она хорошо демонстрирует главную слабость статического ML-детекта: модель может зависеть от структурных характеристик PE сильнее, чем от реально исполняемой логики. А если это так, то атакующий способен давить на вердикт через переразметку формы файла, а не через переписывание payload.

Когда разговор доходит до section injection, быстро становится ясно, что здесь уже мало понимать логику классификатора - нужно нормально чувствовать сам PE-формат. В нашей статье: "Структура и манипуляции секциями PE-файлов: глубокое погружение" мы разобрали, как устроены секции, выравнивание и что вообще происходит с файлом, когда начинаются манипуляции на этом уровне.

Import manipulation и загрязнение признакового пространства​

Импортируемые API и DLL - один из самых старых признаков в статическом детекте малвари, и именно поэтому import manipulation остаётся живой техникой, несмотря на всю её очевидность. Логика здесь простая: если классификатор считает профиль импортов значимым сигналом, значит этот профиль можно загрязнять benign-подобными импортами, чтобы ослабить malicious score или хотя бы размыть уверенность модели.

В теории это выглядит почти слишком прямолинейно. Добавляем функции и библиотеки, которые чаще встречаются в нормальном софте, чем в малвари, и заставляем модель видеть менее подозрительную комбинацию артефактов. На практике всё сложнее. Таблица импортов - это не декоративный список строк, а часть структуры PE, которую нужно корректно встроить в файл. Кроме того, слишком неестественный набор импортов может выглядеть уже не benign, а просто странно. Особенно если дополнительные API никак не согласуются с остальной структурой образа, размером файла, секциями и другими признаками.

Тем не менее техника может работать именно потому, что многие модели долгое время переоценивали import-based signals. Для классификатора наличие определённых DLL, комбинаций WinAPI или просто объём benign-подобных импортов способно сдвинуть объект в менее рискованную область. Для атакующего это удобная точка воздействия: исполняемую логику можно почти не трогать, а признаки, связанные с импортами, всё равно меняются заметно.

Ограничение здесь в том, что сильный классификатор редко смотрит на импорты изолированно. Обычно он оценивает их вместе со структурой PE, статистикой содержимого, секциями и другими группами признаков. Поэтому import manipulation как единственная техника не всегда даёт устойчивый результат. Но в комбинации с другими модификациями она становится куда интереснее. Загрязнение профиля импортов может не перевернуть вердикт само по себе, зато хорошо срабатывает как дополнительный сдвиг в составе более широкой стратегии обхода.

ТехникаУправляемость эффектаРиск повредить PEЗаметность для анализаТипичный характер сдвига
Append attackНизкая или средняяНизкийСредняяГрубый статистический сдвиг
Section injectionВысокаяВысокийВысокаяСтруктурный и статистический сдвиг
Import manipulationСредняяСреднийСредняя или высокаяСдвиг импортных сигналов

Именно этим practical evasion и отличается от абстрактной adversarial атаки на вектор признаков. Здесь редко работает одно идеальное движение. Гораздо чаще результат собирается из нескольких допустимых модификаций файла, каждая из которых немного двигает объект в сторону benign-профиля. Для модели этого может оказаться достаточно. Для защитника - это уже серьёзная проблема, потому что классификатор начинает ошибаться не из-за экзотического трюка, а из-за вполне реалистичных изменений PE-артефактов.
Если тема с import manipulation зацепила именно на уровне практики, дальше логично провалиться глубже в саму таблицу импорта. Об этом мы рассказали в нашей статье: "ASM Re-Fail: Разрушаем стереотипы. Часть 4. Импорт на практике".

Что это значит для защиты​

Разговор про adversarial attacks быстро упирается в неприятный, но полезный вывод: вопрос уже не в том, может ли статический ML-классификатор ошибаться. Может. Вопрос в другом - можно ли сделать так, чтобы он ошибался реже и дороже для атакующего. Именно здесь и начинаются реальные защитные меры. Но проблема в том, что вокруг устойчивости слишком легко построить ложную картину. Кажется, будто достаточно взять одну сильную технику - например, adversarial training или preprocessing - и модель станет заметно устойчивее. На практике так почти не бывает.

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

FireShot Capture 005 -  - [].webp

Почему adversarial training помогает не всегда​

Adversarial training выглядит почти как естественный ответ на проблему. Если модель уязвима к специально сгенерированным adversarial examples, значит эти примеры можно включить в обучение и заставить классификатор лучше распознавать похожие обходы. На уровне идеи это вполне рабочий подход. Он действительно может повысить устойчивость к тем модификациям, которые похожи на уже известные атакующие паттерны.

Но здесь есть жёсткое ограничение: adversarial training почти всегда усиливает модель локально, а не универсально. Она начинает лучше держаться против тех атак, на которых её дообучали, но это не означает хорошую устойчивость ко всем остальным. В детекте малвари это особенно заметно, потому что пространство допустимых PE-модификаций очень широкое. Обучить классификатор на одном наборе adversarial examples - ещё не значит подготовить его к другим способам сдвига признаков, особенно если они реализуются не в feature space, а через реальные изменения файла.

Есть и ещё одна проблема. Adversarial training часто приводит к компромиссу между обычной точностью и устойчивостью. Чтобы сделать модель менее чувствительной к части атак, приходится менять её поведение шире, чем хотелось бы. Для защитника это значит, что устойчивость не достаётся бесплатно. Если pipeline и так балансирует на грани по ложным срабатываниям, агрессивное усиление устойчивости может начать бить по качеству детекта на обычных benign- и malicious-образцах.

Почему preprocessing легко даёт ложное чувство устойчивости​

Preprocessing выглядит ещё практичнее. Идея в том, чтобы до классификации нормализовать вход, отрезать грубые артефакты, подавить шумовые модификации или сделать часть простых обходов менее эффективной. Для детекта малвари это может означать повторную нормализацию признаков, отсечение подозрительных хвостов, сглаживание отдельных статистических эффектов или другие способы ослабить влияние грубых file-space изменений на итоговый вектор признаков.

Проблема в том, что preprocessing очень легко перепутать с реальной устойчивостью. Да, он может ухудшить результат у простого append attack или у грубой модификации статистического профиля. Но это ещё не значит, что модель стала принципиально устойчивее. Часто происходит обратное: мы просто убираем самый дешёвый путь обхода, после чего атакующий переходит к чуть более аккуратной технике и снова получает рабочий результат.

Именно поэтому preprocessing опасен как источник ложной уверенности. На метриках может появиться улучшение. На демонстрационных сценариях атака начинает выглядеть слабее. Но если базовый классификатор по-прежнему зависит от хрупких признаков, никакая предварительная обработка не меняет главную проблему. Она только повышает цену для самых грубых обходов, не убирая саму уязвимость в логике модели.

Зачем detection pipeline нужна разнородность сигналов​

Самый зрелый вывод из всей темы звучит довольно просто: один статический классификатор почти никогда не должен быть последней линией решения. Если adversarial evasion работает за счёт управляемых признаков PE-файла, значит защитнику нужен не просто "более устойчивый" статический ML, а несколько независимых источников сигнала, которые сложнее сдвинуть одной и той же модификацией.

Именно поэтому multi-modal detection - сочетание статических, динамических и поведенческих признаков - выглядит не как роскошь, а как взрослая инженерная мера. Статический слой по-прежнему полезен: он дешёвый, быстрый и хорошо работает как ранний фильтр. Но его устойчивость резко растёт, когда итоговый вердикт зависит не только от PE-артефактов, но и от поведения процесса, цепочек API-вызовов, активности в памяти, сетевого профиля, порождённых процессов и другой телеметрии, которую уже нельзя сдвинуть простой переразметкой файла.

Это не делает систему неуязвимой. Зато меняет экономику атаки. Если раньше атакующему было достаточно аккуратно подвинуть статистику файла, структуру секций или import profile, то теперь обход требует согласованно обмануть несколько слоёв сразу. Для реального detection pipeline это намного важнее, чем красивая устойчивость одной модели на ограниченном наборе adversarial examples.

ПодходЧто улучшаетГде помогаетГлавное ограничение
Adversarial trainingУстойчивость к известным паттернам обходаПротив атак, похожих на обучающие примерыПлохо обобщается на новые техники
PreprocessingСнижает эффект грубых модификацийПротив простых статистических и шумовых обходовЛегко даёт ложное чувство устойчивости
Multi-modal detectionУсложняет направленный evasionКогда статических сигналов уже недостаточноДороже и сложнее в эксплуатации

Устойчивость против adversarial attacks - это не галочка у одной модели и не результат одной хитрой настройки. Она появляется там, где классификатор меньше зависит от хрупких корреляций, а итоговое решение строится на нескольких слоях сигнала сразу. В противном случае любой успех на тестовой выборке слишком легко превращается в красивую, но хрупкую иллюзию устойчивости.

Где static ML detection начинает проигрывать​

Самая опасная ошибка вокруг malware ML выглядит довольно невинно: если модель даёт хорошие метрики на тестовой выборке, значит она уже достаточно надёжна для реального detection pipeline. На практике именно здесь всё и начинает расходиться. Высокая точность может говорить о том, что классификатор хорошо разделяет классы в рамках имеющегося датасета. Но она почти ничего не говорит о том, насколько больно модель реагирует на управляемые изменения PE-файла и насколько быстро её можно столкнуть с уверенного решения в ошибочное.
FireShot Capture 006 -  - [].webp

Почему одного статического классификатора уже недостаточно​

Статический классификатор полезен ровно до того момента, пока он остаётся быстрым и дешёвым слоем первичной оценки. Проблемы начинаются, когда его начинают воспринимать как почти самодостаточный источник истины. В этот момент на него ложится слишком много ожиданий: не просто быстро отделять подозрительное от нормального, а делать это устойчиво, точно и ещё желательно без заметных потерь при попытках обхода. Для static-only модели это заведомо тяжёлая роль.

Причина уже понятна по всей предыдущей статье. Статический ML работает с представлением файла, а не с его полным поведением. Он видит структуру PE, набор секций, профиль импортов, статистику байтов и другие косвенные сигналы, часть которых действительно полезна, а часть просто коррелирует с вредоносным классом в обучающих данных. Пока атакующий не вмешивается в это представление направленно, модель может выглядеть очень уверенно. Но как только появляется adversarial pressure - пусть даже не в виде сложной white-box атаки, а в виде грубых file-space модификаций, - оказывается, что классификатор защищает не смысл объекта, а свою интерпретацию его артефактов.

Из-за этого одного статического слоя почти всегда мало там, где у атакующего есть пространство для экспериментов с PE. Даже если модель сильная, она остаётся ограниченной типом сигнала, на котором обучалась. Она не видит, что файл будет делать после запуска, не видит поведенческий след в памяти, не видит сетевую активность, дочерние процессы, последовательность вызовов и другие признаки, которые труднее подделать простой переработкой бинарника. И чем сильнее система опирается на статический вердикт, тем опаснее обход одного-единственного слоя для всей защиты.

Именно поэтому статический ML-детект начинает проигрывать не в момент, когда модель "плохая", а в момент, когда её начинают использовать как основное и почти автономное решение. Для ранней фильтрации этого слоя достаточно. Для последнего слова по сложному объекту - уже нет.

Что считать реальной устойчивостью, а не красивой метрикой​

С устойчивостью в malware ML вообще легко ошибиться на уровне терминов. Очень удобно назвать модель устойчивой, если она хорошо переживает один набор атак на одном датасете и при этом не слишком проседает по качеству. Но этого мало. Реальная устойчивость - это не просто способность пережить заранее подготовленный adversarial benchmark. Это свойство системы сохранять качество решения тогда, когда атакующий целенаправленно двигает именно те признаки, от которых модель зависит сильнее всего.

Для статического классификатора это означает несколько неприятных требований сразу. Во-первых, нужно смотреть не только на обычные метрики качества, но и на поведение модели под направленной модификацией объекта. Во-вторых, нужно учитывать не только feature-space атаки, но и те сценарии, которые вообще можно реализовать в валидном PE. В-третьих, устойчивость нельзя считать в отрыве от pipeline. Если модель стала чуть крепче против одной группы обходов, но итоговое решение по-прежнему строится на одном типе сигнала, такая устойчивость слишком легко оказывается локальной и хрупкой.

По-настоящему взрослая устойчивость начинается там, где защитник перестаёт мерить только чистоту классификации и начинает мерить цену обхода. Насколько сложно сдвинуть вердикт без поломки payload. Сколько независимых слоёв нужно пройти. Какие признаки можно двигать дешево, а какие нет. Где модель ломается быстро, а где заставляет атакующего уже не "декорировать" PE, а перестраивать весь сценарий. В этом смысле устойчивость - это не только характеристика модели, но и характеристика всей архитектуры детекта.

Что выглядит хорошоПочему этого мало
Высокая точностьНе показывает устойчивость к направленному обходу
Низкий false positive rateНе говорит, насколько хрупки используемые признаки
Хороший результат на валидацииНе гарантирует устойчивость в реальных PE-сценариях
Успех против одной атакиНе означает общей устойчивости

Там, гдестатический ML-детект начинает проигрывать, проблема почти никогда не выглядит драматично на бумаге. Обычно всё начинается с модели, которая слишком хорошо чувствует удобные корреляции и слишком плохо выдерживает их направленный сдвиг. Именно поэтому красивых метрик здесь мало. Намного важнее понять, насколько дорого атакующему обходится изменение признаков и остаётся ли система рабочей, когда один статический слой перестаёт быть надёжной опорой.

Где уязвимость оказывается глубже модели​

История со статическими ML-классификаторами малвари неприятна не потому, что модель можно обмануть. Любую модель при достаточном давлении можно сломать. Проблема в другом - слишком часто обход строится не на редкой экзотике, а на вполне допустимых изменениях PE-файла, которые сдвигают признаки быстрее, чем защитник успевает это заметить. Именно поэтому adversarial attacks здесь важны не как исследовательская экзотика, а как способ трезво посмотреть на саму логику static detection. Если вердикт держится на хрупких корреляциях, хорошая точность ещё ничего не гарантирует.

Из этого следует довольно жёсткий, но полезный вывод. Устойчивость в malware ML начинается не там, где модель красиво проходит один набор тестов, а там, где атакующему становится дорого и неудобно двигать её решение через реальные модификации файла. Один статический классификатор может быть полезным, быстрым и даже очень точным, но делать из него главный источник истины - плохая идея. Чем раньше detection pipeline начинает опираться на разнородные сигналы, а не только на признаки PE, тем меньше шансов, что вся защита просядет из-за аккуратно переработанного бинарника.

И вот здесь остаётся самый неприятный вопрос. Если даже грубые file-space модификации до сих пор способны заметно сдвигать вердикт, то сколько современных static ML-детекторов на самом деле выглядят устойчивыми только до первого по-настоящему целевого обхода?
 
Последнее редактирование:
  • Нравится
Реакции: JackSparrow
Мы в соцсетях:

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