Контекст

Антивирусы хорошо справляются с обнаружением вредоносных программ на протяжении десятилетий. Хотя большинство этих парней, или, скажем, традиционные, основаны на сигнатурах, что означает, что сигнатура вредоносного ПО (или хеш, или что-то еще) должна храниться где-то в базе данных, чтобы ее можно было обнаружить. Большая проблема заключается в том, что у нас нет реальной гарантии того, что антивирус обнаружит совершенно неизвестное вредоносное ПО, сигнатура которого еще не добавлена ​​ни в одну базу данных, особенно если соответствующее вредоносное ПО хорошо запутано.

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

В этой статье мы создадим инструмент на основе глубокого обучения, способный обнаруживать программы-вымогатели WannaCry, он сможет учиться на старых двоичных файлах вредоносных программ, как обнаруживать совершенно неизвестные вредоносные программы без проверки какой-либо базы данных сигнатур. Данные для обучения, которые мы собираемся использовать, — это известный набор данных вредоносных программ Microsoft, доступный на веб-сайте Kaggle. Набор данных содержит дизассемблированные двоичные файлы, помеченные семейством вредоносных программ. Нас не будут интересовать ярлыки, так как стратегия, которую мы примем, не контролируется.

Набор данных вредоносных программ Microsoft

Этот набор данных был первоначально опубликован в контексте задачи машинного обучения, организованной Microsoft. Содержит ассемблерный код вредоносных программ следующих семейств: Ramnit, Lollipop, Kelihos_ver3, Vundo, Simda,Tracur, Kelihos_ver1, Obfuscator.ACY, Gatak. Идея задачи заключалась в обучении модели, способной находить семейство заданного вредоносного ПО. Набор данных считается эталоном в исследованиях безопасности за последние несколько лет (с 2015 года). Его можно было найти и скачать здесь.

Какая стратегия?

Как сказано выше, нас будут интересовать только данные (разобранные двоичные файлы), а не метки, поэтому мы можем сказать, что речь идет об обучении без учителя. Идея состоит в том, чтобы обучить модель, способную максимально точно реконструировать вредоносное ПО.

Но мы хотим обнаруживать вредоносное ПО, а не восстанавливать его.

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

Как бетонировать реконструкцию

Решением для реализации описанной стратегии является нейронная сеть автоэнкодера. Автоэнкодер — это особая архитектура нейронной сети, которая позволяет реконструировать входные данные. Более подробную информацию об автокодировщиках можно найти в одной из моих предыдущих статей под названием Применение нейронной сети автокодировщика обнаружения аномалий при обнаружении вредоносных URL-адресов, где я использовал ее для обнаружения вредоносных URL-адресов.

Какие особенности?

Экспериментируя с этой идеей, я сначала использовал количество всех присутствующих ассемблерных инструкций в исходном коде ассемблера в качестве функций. Это сработало, но результаты были не очень хорошими. В текущей версии мы будем использовать количество наиболее часто используемых инструкций по сборке. На следующей диаграмме, полученной с https://www.strchr.com/x86_machine_code_statistics, представлены 20 лучших инструкций для архитектуры x86, мы рассмотрим 18 из них.

Выполнение

Извлечение признаков

Ниже приведен пример файла сборки, полученного из базы данных вредоносных программ Microsoft. В необработанном формате этот файл бесполезен с точки зрения обучения модели.

Чтобы сгенерировать окончательные данные, которые мы будем использовать для обучения автоэнкодера, нам нужно будет проанализировать все дизассемблированные файлы вредоносных программ и подсчитать количество каждой инструкции. Это делается с помощью скрипта gen_features.py, который вы найдете в исходном коде. Сценарий создаст файл CSV, в котором каждая операция сборки представлена ​​столбцом, а каждая вредоносная программа представлена ​​строкой. Всего у нас есть 18 функций, и наши готовые к использованию данные выглядят следующим образом.

Архитектура автоэнкодера

Как мы уже сказали, будет использоваться автоэнкодер. Эта нейронная сеть имеет столько же выходов, сколько и входов, потому что мы будем обучать ее изучению функции f(X) = X.

Лучшая архитектура, которая мне подошла, состоит из 13 скрытых слоев. Первые 7 содержат 17,15, 13, 11, 9, 7, 5, 3 узла, а последние 6 содержат 5, 7, 9, 11, 13, 15 и 17 узлов. Входные и выходные слои включают количество функций (18 инструкций по сборке) в качестве узлов. Визуально это выглядит следующим образом:

Обучение

Keras используется для реализации нейронной сети. Следующий фрагмент содержит сетевую архитектуру. Полный обучающий код можно найти в скрипте train.py в репозитории исходного кода.

input_layer = Input(shape=(input_dim, ))
nodes_number = 17
while (nodes_number›=3):
if nodes_number == 17:
layer = Dense(nodes_number, активация='tanh')(input_layer)
еще:
layer = Dense(nodes_number, активация='tanh')(layer)
nodes_number=nodes_number-2
nodes_number=5
while(nodes_number‹=17):
layer = Dense(nodes_number, активация='tanh')(layer)
nodes_number=nodes_number+2
output_layer = Dense(input_dim, активация='tanh')(слой)
autoencoder = Model(входы=входной_слой, выходы=выходной_слой)

А потом нам его нужно будет скомпилировать и обучить. Как вы можете видеть, 10 эпох было достаточно, чтобы получить респектабельный результат, используемый размер пакета равен 18. Следует упомянуть одну важную деталь: у нас есть X (функции) в качестве цели обучения, что не очень обычно и может сбивать с толку в традиционном нейронной сети, но совершенно нормально в автоэнкодере. Модель, сохраненная в файле model.h5, — это наш будущий современный антивирус!

nb_epoch = 10
batch_size = 18

autoencoder.compile(optimizer='adam',loss='mean_squared_error',metrics=['accuracy'])
checkpointer = ModelCheckpoint(filepath=”model.h5, verbose=0, save_best_only=True)
> history = autoencoder.fit(X_train, X_train, epochs=nb_epoch, batch_size=batch_size, shuffle=True, validation_data=(X_test, X_test), verbose=1, callbacks=[checkpointer]).history
autoencoder = load_model ('модель.h5')

При обучении этой сети получаем следующий результат:

Данные тестирования

Чтобы проверить, действительно ли наша модель обнаруживает вредоносное ПО, нам нужны отрицательные данные (не вредоносные двоичные файлы). Лучшим решением, которое я нашел для этого, является извлечение двоичных файлов с моей собственной машины (MacOs) и их дизассемблирование для окончательного создания функций, как мы сделали с вредоносными файлами. Чтобы получить все двоичные файлы на моем компьютере и разобрать их, я использовал команду find в сочетании с objdump следующим образом:

find / -perm +111 -type f -exec sh disassemble.sh {} \;

Вот содержимое disassemble.sh

имя_файла=$(базовое имя $1)
objdump $1 -d -x86-asm-syntax intel › ./benign_dataset/$filename.asm

Для параметра -x86-asm-syntax установлено значение intel, поскольку вредоносный набор данных использует этот вариант, и мы хотим, чтобы оба набора данных были похожи с точки зрения синтаксиса, особенно имен инструкций.

На этом этапе мы можем запустить скрипт gen_data.py, чтобы получить CSV, подобный тому, который мы получаем для вредоносных данных.

Нам также потребуются некоторые вредоносные данные тестирования, которые будут взяты из исходных вредоносных данных, которые мы сгенерировали в начале.

Обнаружение

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

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

предсказания_вредоносного = autoencoder.predict(X_test_malicious)
предсказания_non_malicous = autoencoder.predict(X_test_non_malicious)

Расчет ошибки выполняется с использованием среднеквадратичной ошибки следующим образом:

mse_malicious = np.mean(np.power(X_test_malicious — предсказания_вредоносного, 2), ось = 1)
mse_non_malicious = np.mean (np.power (X_test_malicious — предсказания_не_вредоносного, 2), ось = 1)

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

порог = np.average (mse_malicious)

На этом этапе мы можем рассчитать производительность и визуализировать результаты. Показатели производительности могут быть рассчитаны следующим образом:

tp, fp, tn, fn = 0, 0, 0, 0
for e в предсказаниях_malicous:
if e ‹ threshold:
tp+=1
else:
fn+=1
for e в mse_def_test:
if e›=threshold:
tn+=1
else:
fp+=1

Результат:

Точность: 0,70
Отзыв: 0,92
Точность: 0,67

Результат неплохой, за исключением того факта, что мы генерируем относительно много ложных срабатываний. Это все еще может быть решено тонкой настройкой порога.

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

Момент истины — Тестирование с помощью WannaCry

Чтобы убедиться, что модель сборки действительно обнаруживает вредоносное ПО, мы протестировали ее с помощью программы-вымогателя WannaCry, бинарный файл которой можно найти по адресу https://github.com/ytisf/theZoo/tree/master/malwares/Binaries/Ransomware.WannaCry. Кстати, этот репозиторий содержит действительно ценный контент с точки зрения анализа вредоносных программ.

Прежде чем выполнять следующие шаги, вы должны убедиться, что используете песочницу, особенно если вы используете ОС Windows. Если это не так, существует большой риск заражения вашего компьютера WannaCry.

Бинарный файл разбирается с помощью objdump, а функции извлекаются с помощью gen_features.py, как для данных обучения/тестирования. При восстановлении двоичного файла ошибка была значительно ниже порога, как показано на следующих двух рисунках. Это означает, что наша модель смогла определить WannaCry как вредоносное ПО!

Что дальше?

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

Исходный код

Исходный код, вредоносное ПО и безопасные данные в формате CSV, а также код WannaCry ASM доступны по адресу https://github.com/slrbl/malware-detection-with-deep-learning-autoencoder.