
Un virtualizador de código X86-64 para la ofuscación basada en VM.
*Soporte de PE solo probado en binarios compilados a través de Mingw-W64
CMake obtendrá todas estas dependencias, por lo que no es necesario instalarlas usted mismo.
| Nombre | Versión |
|---|---|
| CMake | 3.25+ |
| Zydis | 4.1.0+ |
| zasm | El último |
| Lieve | 0.15.1+ |
Se requiere un compilador compatible C++23 para construir.
git clone https://github.com/dmaivel/covirt.git
cd covirt
mkdir build
cd build
cmake ..
cmake --build . --config Release Si se está compilando en Windows a través de Visual Studio, debe 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 sepa qué funciones deben ser virtualizadas, debe agregar los marcadores de inicio y finalización a su código fuente, así:
#include "covirt_stub.h"
int my_function (...)
{
int result = 0 ;
__covirt_vm_start ();
// ...
__covirt_vm_end ();
return result ;
}Importante
__covirt_vm_end en ubicaciones inalcanzables (es decir, después de una devolución), ya que evitará que el trozo final emita__covirt_vm_...(); Los stubs no funcionarán usando MSVC porque usan ensamblaje en líneaSSE4 
#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 );
} La aplicación de ejemplo anterior se virtualizada usando covirt a.out -d , que genera un volcado de las instrucciones de VM después de la ofuscación y la virtualización. La implementación actual de VM empuja la mayoría de los operandos a la pila para procesarlos, reduciendo la complejidad de codificar las instrucciones de VM. Para las instrucciones que no tienen un controlador VM definido, se ejecutarán de forma nativa ( vm_exit -> native instruction -> vm_enter ). Las funciones de llamadas siguen la misma tubería, en la que salimos, llamamos a la función y volvemos a ingresar a la VM. En conjunto, las transformaciones hacen que los binarios crezcan significativamente en tamaño:
a.out como ELF : 15.5 kb -> 1.0 Mba.out como PE : 259.3 kb -> 1.3 Mb | Descripción | IDA |
|---|---|
La descompilación de IDA de vm_entry , que ha sido ofuscada solo a través del pase MBA. Más de 27k LOC fueron generados por el descompilador. | ![]() |
Desmontaje de vm_entry , que ha sido ofuscado a través de los pases MBA y SMC. La descompilación no funciona. | ![]() |