
Ein x86-64-Code-Virtualisierer für VM-basierte Verschleierung
Ein x86-64-Code-Virtualizer für VM-basierte Verschleierung.
*Die PE-Unterstützung wurde nur mit über MinGW-w64 kompilierten Binärdateien getestet
CMake wird alle diese Abhängigkeiten automatisch holen, daher ist eine manuelle Installation nicht erforderlich.
Zum Erstellen wird ein mit C++23 kompatibler Compiler benötigt.
git clone https://github.com/dmaivel/covirt.git
cd covirt
mkdir build
cd build
cmake ..
cmake --build . --config Release
Wenn Sie unter Windows mit Visual Studio kompilieren, müssen Sie clang-cl verwenden: 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
Damit covirt weiß, welche Funktionen virtualisiert werden müssen, müssen Sie die Start- und Endmarker in Ihren Quellcode einfügen, etwa so:
#include "covirt_stub.h"
int my_function(...)
{
int result = 0;
__covirt_vm_start();
// ...
__covirt_vm_end();
return result;
}
[!IMPORTANT]
- Platziere
__covirt_vm_endnicht an unerreichbaren Stellen (z. B. nach einemreturn), da dies verhindert, dass der End-Stub emittiert wird__covirt_vm_...();-Stubs funktionieren nicht mitMSVC, da sie Inline-Assembly verwendenSSE4-Unterstützung ist erforderlich
#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);
}
Die obige Beispielanwendung wurde mit covirt a.out -d virtualisiert, was nach Verschleierung und Virtualisierung einen Dump der VM-Instruktionen ausgibt. Die aktuelle VM-Implementierung legt die meisten Operanden zur Verarbeitung auf den Stack, was die Komplexität der Kodierung der VM-Instruktionen reduziert. Für Instruktionen ohne definierten VM-Handler werden diese nativ ausgeführt (vm_exit -> native instruction -> vm_enter). Das Aufrufen von Funktionen folgt derselben Pipeline: Wir verlassen die VM, rufen die Funktion auf und betreten die VM erneut. Insgesamt führen die Transformationen dazu, dass die Binärdateien erheblich an Größe zunehmen:
a.out als ELF: 15.5 kB -> 1.0 MBa.out als PE: 259.3 kB -> 1.3 MB| Beschreibung | IDA |
|---|---|
IDA-Dekompilierung von vm_entry, das nur mit dem MBA-Pass verschleiert wurde. Der Dekompiler erzeugte über 27k LOC. | ![]() |
IDA-Disassemblierung von vm_entry, das mit den MBA- & SMC-Pässen verschleiert wurde. Dekompilierung funktioniert nicht. |
![]() |