m0yv - модульный x86/x64 файловый инфектор

Структура исходников:
.\Core - основные сорцы ядра инфектора
.\Core\shellcode\Shellcode - загрузчики ядер в память, а также генератор шеллкода из exe -> shellcode
.\common - используемые структуры в модульной системе
.\modules - модули m0yv 
.\Zerogen - дроппер нулевого поколения, по факту загрузчик капсида в память
.\capsid_tools - тулза для генерации вирусного капсида из ядер и шеллкодов-загрузчиков, а также для загрузки в память
.\HandshakeEmulator - утилиты для эмуляции работы серверной стороны: подпись хэндшейка, создание ответа от сервера
.\strings_cryptor - криптит строку в контейнер для последующей вставки в strings.hpp
.\Ed25519_keygen - генерирует ed25519 пару ключей - паблик и приват
.\ConfigCreator - конфигуратор, создает конфиг-файл для вставки в ядра перед компиляцией

Структура m0yv, процесс работы


I. Процесс полной сборки

1) Создать конфиг в .\ConfigCreator, вставить в .\Core\config.h в config::RawData
2) Ipython сбилдить через .\Core\build_x86.py и .\Core\build_x64.py, оно соберет ядра инфектора x86 и x64 дллки
3) Собрать шеллкод загрузчика по пути .\Core\Shellcode\ использовав build_shell_x86.py и build_shell_x64.py
Результатом будет x64.exe и x86.exe, далее надо использовать Exe2shellcode, результатом будут файлы
Shellcode_x86.bin и x86_dword_offset.txt
Shellcode_x64.bin и x64_dword_offset.txt
данные файлы соответственно шеллкоды-загрузчики нужной разрядности и файлы содержащие офсет от начала шеллкода до точки входа в него (RVA EP - rva code section)
точка входа нужна из-за того, что бэкенд MSVC не гарантирует порядок функций, то есть каждую компиляцию entry может быть разным
4) Переместить файлы шеллкодов, а также текстовики вместе с ядрами инфектора в .\capsid_tools\Release
5) Использовать capsid_tools.exe для генерации капсида, который будет сохранен в capsid.bin
по состоянию на 15 июля 2021 рекомендуемый вызов: capsid_tools.exe --gen --shellcode
--gen означает генерацию
--shellcode означает, что проект был скомпилирован в режиме внешнего шеллкода (в global.hpp есть дефайн _EXTERNAL_SHELLCODE)
и нам нужны внешние загрузчики-шеллкоды, иначе он будет считать, что переданные на вход ядра являются рефлективными DLL и попытается найти загрузчики в их экспорте

в режиме шеллкода он их сожмет (что невозможно сделать в режиме рефлективной загрузки), перед этим изменив PE формат так, что ни одна утилита не распознает его как PE
шеллкод при обнаружении кастомного заголовка будет загружать его как кастомный PE файл, в противном случае как PE файл
если не задано в параметрах, то он соберет кастомный формат
--hive задает кастом формат явно
--pe говорит собрать в режиме PE (не рекомендуется по очевидным причинам)

6) capsid.bin вставить в загрузчик нулевого поколения Zerogen как С-массив по аналогии с пунктом 1)

II. Структура файлового инфектора

1) Вирусный капсид

основной структурой носителем ядер инфектора является специальная структура-капсид (по аналогии с белковой капсид-оболочкой биологических вирусов)
Заголовки капсида содержат в себе смещения до x86 и x64 ядер, смещения до входа в x86 и x64 загрузчики, метаданные заражения (далее подробно)

Основной процесс работы состоит в поиске новых файлов и их заражении в общем виде следующим образом: 
a) вставка зашифрованного капсида в конец последней секции
b) генерация стартап кода нужной разрядности для расшифровки капсида и перехода на соответствующий архитектуре загрузчик и вставка в конец последней секции
c) перехват управления хостового апп на стартап код

Стартап-код передает загрузчику указатель на начало капсида, загрузчик загружает ядра m0yv и передает туда физический адрес капсида, он копируется в изменяемый буффер и процесс заражения повторяется	

По состоянию на текущий момент (15 июля 2021) инфектор сразу же после зашифрованного капсида дописывает специальную структуру необходимую для восстановления управления хостового апп
структура считывается если поколение > 0 через указатель на физическое расположение капсида + его размер и происходит патчинг кода в памяти, снимаются перехваты, добавляется восстанавливающие трамплины
восстанавливающего кода нет в физическом файле, он генерируется изнутри m0yv в рантайме, что затрудняет лечение

Общая структура капсида следующая:

* RVA-значения капсида отсчитываются от начала капсид-структуры
актуально по состоянию на 15 июля 2021
-------------------------
| размер x86 ядра       |
-------------------------
-------------------------
| размер x64 ядра       |
-------------------------
-------------------------
| RVA x86 ядра m0yv     |
-------------------------
-------------------------
| RVA EP x86 загрузчика |
-------------------------
-------------------------
| RVA x64 ядра m0yv     |
-------------------------
-------------------------
| RVA EP x64 загрузчика |
-------------------------
-------------------------
| размер всего капсида  |
-------------------------
-------------------------
| метаданные            |
-------------------------


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

2) Полиморфный движок

Полиморфизм стартап-кода и шифрования основан на трех основных факторах

a) Полиморфизм декриптора на метауровне

Движок генерирует псевдоинструкции декриптора псевдоблочного шифра состоящие из операций ADD, SUB, NOT, XOR, таким образом размер блока и набор операций шифрования/расшифрования каждую мутацию разный
Для начала движок генерирует операции шифрования, которые потом интерпретирует для шифровки капсида
После чего инвертирует их и генерирует из промежуточного представления ассемблерный код

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

b) Пермутация встроенного ассемблера

Ассемблирующий движок сделан по принципу just-in-time assembler, имея функции для нужной инструкции, при вызове которых в байткод добавляется нужная инструкция.
Данный движок способен при генерации заменять инструкции на их аналоги

c) Медленная природа мутации на основе ДНК-кода и параметра мутагенности

Метаданные капсида содержат в себе специальный байткод, каждое поле которого служит зерном для рандомайзера соответствующих частей полиморфного движка,
а также специальную переменную параметра мутагенности, который является пограничным значением для мутатора.
Когда количество зараженных файлов переваливает за эту величину, байткод модифицируется (мутирует) случайным образом и сохраняется обратно в капсид.
Таким образом каждые N файлов ДНК-код мутирует, изменяя выхлоп полиморфного движка и параметр мутагенности. Полный функционал и пространство генерируемого кода остается скрытым засчет медленной мутации,
так как зерен много для множества функционала, а смутировать может лишь одно зерно за раз (UPD: окно мутации выбирается также случайным образом, как и операции мутации - инверсии зерен и так далее, xchg зерен и тому подобное), 
то после мутации остается большое количество старого кода и логика полиморфизма остается сокрыта от глаз аналитиков.

3) Перехват управления

Для перехвата управления модуль заражения файлов инфектора m0yv дизассемблирует в поточном режиме кодовую секцию, начиная с точки входа и следуя по всем JMP, входя рекурсивно в вызовы процедур.
У рекурсии есть ограничение глубины входа, а также триггеры выхода из дизассемблирования - невалид инструкция, нули и возврат из функции.
Во время дизассемблирования инфектор сохраняет RVA адреса всех инструкций вызова - call с опкодом 0xE8

Выбирая случайную инструкцию из N первых вызовов (случайность основана на ДНК-коде) перехватывает ее, патча адрес перехода на стартап-код.
После заражения добавляет структуру нужную для восстановления: RVA оригинальной вызываемой процедуры, RVA перехваченного кода, RVA начала и конца стартап-кода
Восстановление перехвата заключается в добавлении в памяти трамплинов в начале и конце стартап кода на оригинальную функцию, а также патчинге перехваченного вызова на оригинальную процедуру.

III. Бот

Бот выполняющий команды от админки состоит из следующих компонентов

1) Поиск сервера

Он состоит из нескольких компонент

a) DGA. Осуществляет поиск домена C2-сервера
На состояние 15 июля 2021 единственный вариант - это использование DGA-seed для генерации случайных строк
В планах добавить парсинг блокчейна (UPD: на состояние 9 декабря 2021 от парсинга блокчейна решено было отказаться, а реализовать в последствии p2p на роутерах и подключить к этому p2p в режиме клиентских пиров боты инфектора)
 
b) Handshake. Операция рукопожатия получает IP адрес сервера, создает сессию общения с сервером, содержащий псевдослучайный идентификатор генерируемый на основе времени и железа бота
Ожидает от сервера подписать идентификатор алгоритмом Ed25519, проверяя подпись зная публичный ключ сервера можно удостовериться, что сервер принадлежит m0yv
UPD: в идеале надо добавить подпись к каждому ответу от сервера, поскольку во время использования FastFlux возникли опасения взлома промежуточных прокси-серверов и отправка невалидного ответа по доверяемому и ранее проверенному пути

2) Выполнение команд

Команды от сервера получаются в виде массива json и их может быть неограниченное число
Выполнение команд делится на несколько типов

a) Собственно команды 
Скачать-запустить, загрузить dll в память и тому подобные стандартные команды
каждая команда имеет cid - айди команды, после выполнения отправляется статус выполнения обратно. 

b) модули
Модули делятся на резидентные и нерезидентные. Нерезидентные модули загружаются по аналогии с DLL в память, передается специальная контекст-структура, нужная для коммуникации ядра m0yv и его модулей,
а также содержащая необходимые хэндлы для синхронизации отстука

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

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

3) Модульная система

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

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

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

