EntropyReducer: снизьте энтропию вашей полезной нагрузки и обфусцируйте её с помощью сериализованных связных списков
Как это работает
Алгоритм EntropyReducer определяется значениями BUFF_SIZE и NULL_BYTES. Ниже показано, как EntropyReducer организовал бы вашу полезную нагрузку, если бы BUFF_SIZE был установлен в 4, а NULL_BYTES — в 2.

Алгоритм обфускации
- EntropyReducer сначала проверяет, кратен ли размер входной необработанной полезной нагрузки
BUFF_SIZE; если нет, он дополняет её до нужного размера.
- Затем он берёт каждый фрагмент размером
BUFF_SIZE из полезной нагрузки и создаёт для него узел связного списка с помощью функции InitializePayloadList, инициализируя полезную нагрузку как связный список.
- Созданный узел будет иметь пустой буфер размером
NULL_BYTES, который будет использоваться для снижения энтропии.
- На этом этапе, хотя EntropyReducer уже выполнил свою задачу, снизив энтропию полезной нагрузки, он на этом не останавливается. Затем он продолжает рандомизировать порядок каждого узла в связном списке, разрушая исходный порядок необработанной полезной нагрузки. Этот шаг выполняется с помощью алгоритма сортировки слиянием, реализованного в функции MergeSort.
- Отсортированный связный список имеет случайный порядок, поскольку значением, по которому сортируется связный список, является XOR-значение первых трёх байтов необработанной полезной нагрузки; это значение определяет его позицию в реорганизованном связном списке. Этот шаг можно увидеть здесь.
- Поскольку сохранить связный список в файл невозможно из-за того, что он связан указателями, мы вынуждены его сериализовать.
- Сериализация сгенерированного связного списка выполняется с помощью функции
Obfuscate здесь.
- После этого сериализованные данные готовы к записи в выходной файл.
Алгоритм деобфускации
- Поскольку последним шагом в
Алгоритме обфускации была сериализация связного списка, первое, что нужно сделать здесь, — это десериализовать обфусцированную полезную нагрузку, создав из неё связный список. Этот шаг выполняется здесь в функции Deobfuscate.
- Следующий шаг — отсортировать связный список по Id узла, что делается с помощью того же алгоритма сортировки слиянием, который использовался ранее.
- Теперь связный список находится в правильном порядке для восстановления байтов полезной нагрузки в нужной последовательности. Поэтому мы просто извлекаем исходные байты полезной нагрузки из каждого узла, как показано здесь.
- Последний шаг — освободить выделенные узлы, что делается здесь.
Использование
- EntropyReducer просто читает файл необработанной полезной нагрузки из командной строки и записывает обфусцированную версию в файл с тем же именем, но с префиксом ".ER".
- Размер итоговой обфусцированной полезной нагрузки зависит от значений
BUFF_SIZE и NULL_BYTES. Однако его можно определить с помощью следующего уравнения
FinalSize = ((OriginalSize + BUFF_SIZE - OriginalSize % BUFF_SIZE ) / BUFF_SIZE) * (BUFF_SIZE + NULL_BYTES + sizeof(INT))
Проект PoC в этом репозитории используется для выполнения файла ".ER", созданного в качестве примера его десериализации и деобфускации.
Включение в ваши проекты
Всё, что нужно сделать, — добавить файлы EntropyReducer.c и EntropyReducer.h в ваш проект и вызвать функцию Deobfuscate. Для справки можно посмотреть PoC/main.c.
Пример вывода
В этом примере BUFF_SIZE был установлен в 3, а NULL_BYTES — в 1.
- Исходная полезная нагрузка, первый фрагмент полезной нагрузки (
FC 48 83)

- Тот же фрагмент полезной нагрузки, но со смещением

Результат
- Шелл-код x64 calc, сгенерированный metasploit, имеет энтропию
5.883, просмотрено в pestudio.

- Тот же файл, зашифрованный AES, имеет энтропию
7.110.

- Практически тот же результат и с алгоритмом RC4;
7.210

- Однако при использовании EntropyReducer энтропия оказывается даже ниже, чем у исходной полезной нагрузки;
4.093

Алгоритм сортировки слиянием взят из c-linked-list.