
Um virtualizador de código x86-64 para ofuscação baseada em VM.
*Suporte de PE apenas testado em binários compilados via mingw-w64
CMake buscará todas essas dependências, portanto, instalá -las você mesmo não é necessário.
| Nome | Versão |
|---|---|
| Cmake | 3.25+ |
| Zydis | 4.1.0+ |
| Zasm | Mais recente |
| Lief | 0,15.1+ |
Um compilador compatível com C++23 é necessário para construir.
git clone https://github.com/dmaivel/covirt.git
cd covirt
mkdir build
cd build
cmake ..
cmake --build . --config Release Se você estiver compilando no Windows via Visual Studio, deve usar 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
Code virtualizer for x86-64 ELF & PE binaries
Positional arguments:
INPUT_PATH path to input binary to virtualize
Optional arguments:
-h, --help shows help message and exits
-v, --version prints version information and exits
-o, --output OUTPUT_PATH specify the output file [default: INPUT_PATH.covirt]
-vcode, --vm_code_size MAX specify the maximum allowed total lifted bytes [default: 2048]
-vstack, --vm_stack_size SIZE specify the size of the virtual stack [default: 2048]
-no_smc, --no_self_modifying_code disable smc pass
-no_mba, --no_mixed_boolean_arith disable mba pass
-d, --show_dump_table show disassembly of the vm instructions Para que covirt saiba quais funções precisam ser virtualizadas, você deve adicionar os marcadores de início e final ao seu código -fonte, assim:
#include "covirt_stub.h"
int my_function (...)
{
int result = 0 ;
__covirt_vm_start ();
// ...
__covirt_vm_end ();
return result ;
}Importante
__covirt_vm_end em locais inacessíveis (ou seja, após um retorno), pois impedirá que o stub final emitir__covirt_vm_...(); Os stubs não funcionam usando MSVC porque usam montagem embutidaSSE4 é necessário 
#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 = %dn" , result );
__covirt_vm_end ();
return result ;
}
int main ()
{
calculate ( 5 , 12 );
} O aplicativo de exemplo acima foi virtualizado usando covirt a.out -d , que gera um despejo das instruções da VM após ofuscação e virtualização. A implementação atual da VM empurra a maioria dos operandos para a pilha para processá -los, reduzindo a complexidade da codificação das instruções da VM. Para instruções que não possuem um manipulador de VM definido, elas serão executadas nativamente ( vm_exit -> native instruction -> vm_enter ). As funções de chamada seguem o mesmo pipeline, em que saímos, chamamos a função e reentramos a VM. Todos juntos, as transformações fazem com que os binários cresçam significativamente em tamanho:
a.out como ELF : 15,5 kb -> 1,0 mba.out como um PE : 259,3 kb -> 1,3 mb | Descrição | Ida |
|---|---|
A decompilação de vm_entry de Ida, que foi ofuscada apenas através do MBA Pass. Mais de 27k LOC foram gerados pelo decompilador. | ![]() |
A desmontagem de Ida de vm_entry , que foi ofuscada através dos passes MBA e SMC. A decompilação não funciona. | ![]() |