
VM 기반 난독화를 위한 x86-64 코드 버추얼라이저
| 이름 | 버전 |
|---|
| CMake | 3.25+ |
| Zydis | 4.1.0+ |
| zasm | Latest |
| LIEF | 0.15.1+ |
빌드하려면 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
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
covirt가 어떤 함수를 가상화해야 하는지 알 수 있도록, 소스 코드에 시작 및 종료 마커를 다음과 같이 추가해야 합니다:
#include "covirt_stub.h"
int my_function(...)
{
int result = 0;
__covirt_vm_start();
// ...
__covirt_vm_end();
return result;
}
[!IMPORTANT]
- 도달할 수 없는 위치(예: return 뒤)에
__covirt_vm_end를 배치하지 마십시오. 종료 스텁이 생성되지 않습니다.__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 명령어 덤프를 출력합니다. 현재 VM 구현은 대부분의 피연산자를 스택에 푸시하여 처리하므로 VM 명령어 인코딩의 복잡성을 줄입니다. 정의된 VM 핸들러가 없는 명령어는 네이티브로 실행됩니다(vm_exit -> native instruction -> vm_enter). 함수 호출도 동일한 파이프라인을 따르며, VM을 빠져나가 함수를 호출한 후 다시 VM으로 재진입합니다. 이러한 변환들로 인해 바이너리 크기가 크게 증가합니다:
a.out (ELF): 15.5 kB -> 1.0 MBa.out (PE): 259.3 kB -> 1.3 MB| 설명 | IDA |
|---|---|
MBA 패스만 적용되어 난독화된 vm_entry의 IDA 디컴파일 결과. 디컴파일러가 27,000줄 이상의 코드를 생성했습니다. | ![]() |
MBA 및 SMC 패스가 적용되어 난독화된 vm_entry의 IDA 디스어셈블리. 디컴파일이 작동하지 않습니다. | ![]() |