
Виртуализатор кода x86-64 для обфускации на основе виртуальной машины
Виртуализатор кода x86-64 для обфускации на основе виртуальных машин.
Поддержка PE проверена только на бинарниках, собранных через MinGW-w64
CMake загрузит все эти зависимости самостоятельно, поэтому устанавливать их вручную не требуется.
Для сборки требуется компилятор, совместимый с C++23.
git clone https://github.com/dmaivel/covirt.git
cd covirt
mkdir build
cd build
cmake ..
cmake --build . --config Release
Если вы компилируете на Windows через Visual Studio, необходимо использовать clang-cl: cmake .. -T ClangCL -A x64.
Usage: covirt [--help] [--version] [--output OUTPUT_PATH] [--vm_code_size MAX] [--vm_stack_size SIZE] [--no_self_modifying_code] [--no_mixed_boolean_arith] [--show_dump_table] INPUT_PATH
Виртуализатор кода для x86-64 ELF и PE бинарников
Позиционные аргументы:
INPUT_PATH путь к входному бинарнику для виртуализации
Необязательные аргументы:
-h, --help показать справку и выйти
-v, --version показать информацию о версии и выйти
-o, --output OUTPUT_PATH указать выходной файл [по умолчанию: INPUT_PATH.covirt]
-vcode, --vm_code_size MAX указать максимально допустимое количество извлечённых байтов [по умолчанию: 2048]
-vstack, --vm_stack_size SIZE указать размер виртуального стека [по умолчанию: 2048]
-no_smc, --no_self_modifying_code отключить проход SMC
-no_mba, --no_mixed_boolean_arith отключить проход MBA
-d, --show_dump_table показать дизассемблирование инструкций ВМ
Чтобы covirt знал, какие функции нужно виртуализировать, необходимо добавить в исходный код маркеры начала и конца, например:
#include "covirt_stub.h"
int my_function(...)
{
int result = 0;
__covirt_vm_start();
// ...
__covirt_vm_end();
return result;
}
[!IMPORTANT]
- Не размещайте
__covirt_vm_endв недостижимых местах (например, после return), так как это помешает генерации конечной заглушки.- Заглушки
__covirt_vm_...();не будут работать сMSVC, поскольку они используют встроенный ассемблер.- Требуется поддержка
SSE4.
#include <covirt_stub.h>
#include <stdio.h>
int calculate(int a, int b)
{
int result = 0;
__covirt_vm_start();
for (int i = 0; i < 10; i++)
if (i > 5)
result += result + a;
else
result += (result >> 1) + b;
printf("result = %d\n", result);
__covirt_vm_end();
return result;
}
int main()
{
calculate(5, 12);
}
Пример приложения выше был виртуализирован с помощью covirt a.out -d, что выводит дамп инструкций ВМ после обфускации и виртуализации. Текущая реализация ВМ помещает большинство операндов в стек для их обработки, снижая сложность кодирования инструкций ВМ. Для инструкций, для которых нет определённого обработчика ВМ, они выполняются нативно (vm_exit -> native instruction -> vm_enter). Вызов функций следует тому же конвейеру: мы выходим, вызываем функцию и возвращаемся в ВМ. В совокупности эти преобразования значительно увеличивают размер бинарников:
a.out как ELF: 15,5 кБ -> 1,0 МБa.out как PE: 259,3 кБ -> 1,3 МБ| Описание | IDA |
|---|---|
Декомпиляция IDA для vm_entry, обфусцированного только проходом MBA. Декомпилятор сгенерировал более 27 тыс. строк кода. | ![]() |
Дизассемблирование IDA для vm_entry, обфусцированного проходами MBA и SMC. Декомпиляция не работает. |
![]() |