VMProtect 2 Virtual Machines Profiler Library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
Go to file
_xeroxz 300eed7a62
Merge branch 'dev' into 'master'
3 years ago
dependencies removed xtils because its not linux friendly... 3 years ago
doxygen added manuals... 4 years ago
include ported the project to linux 3 years ago
src ported the project to linux 3 years ago
.clang-format added clang format, its 90% ok 4 years ago
.gitignore Port to cmkr 3 years ago
.gitmodules removed xtils because its not linux friendly... 3 years ago
CMakeLists.txt added more vm handler profiles... 3 years ago
LICENSE Add LICENSE 4 years ago
README.md updated transforms to include XCHG as well as updated readme... 3 years ago
cmake.toml removed xtils because its not linux friendly... 3 years ago
cmkr.cmake Port to cmkr 3 years ago

README.md

use: git clone --recursive https://githacks.org/vmp2/vmprofiler.git

VMProfiler - Library To Profile VMProtect 2 Virtual Machines

vmprofiler is a c++ library which is used to statically analyze VMProtect 2 polymorphic virtual machines. This project is inherited in vmprofiler-qt, vmprofiler-cli, and vmemu. This is the base project for all other VMProtect 2 projects inside of this group on githacks.

Doxygen Documentation

Credit & Contributors

  • VTIL - Virtual-machine Translation Intermediate Language
  • Zydis - Fast and lightweight x86/x86-64 disassembler library
  • irql0 - helped with the first version of vm handler pattern matching
  • mrexodia - ported project to cmkr.

Build Instructions

  • Warning: Visual Studio 2019 must be installed on the system... Also please make sure it is completely up-to-date! You will have compile issues if you are not updated :)
git clone --recursive https://githacks.org/vmp2/vmprofiler.git
cd vmprofiler
cmake -B build

You can now open vmprofiler.sln, select "Release" and "x64" then build.

Basic Usage - Creating a vm::ctx_t Object

The vm::ctx_t class is a small container-like class which is simply used to contain all information for a given vm entry. This class contains the following useful information:

  • all vm handlers for a given vm entry
  • the linear virtual address of the module base in memory
  • the image base address
  • the image size in virtual memory
  • which way VIP advances (exec_type)
  • vm entry relative virtual address
  • vm entry deobfuscated and flattened
  • calc jmp deobfuscated and flattened

All of the above information is generated by executing the vm::ctx_t::init member function. Below is a C++ example of how to create a vm::ctx_t object.

const auto module_base = reinterpret_cast< std::uintptr_t >(
    LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(),
        NULL, DONT_RESOLVE_DLL_REFERENCES ) );

const auto vm_entry_rva = std::strtoull( parser.get< std::string >( "vmentry" ).c_str(), nullptr, 16 );
const auto image_base = umtils->image_base( parser.get< std::string >( "bin" ).c_str() );
const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage;
vm::ctx_t vmctx( module_base, image_base, image_size, vm_entry_rva );

if ( !vmctx.init() )
{
    std::printf( "[!] failed to init vm::ctx_t... make sure all cli arguments are correct!\n" );
    return -1;
}

Using vm::ctx_t Object

Once you have instantiated vm::ctx_t and called vm::ctx_t::init with success, you now can directly access the data members of vm::ctx_t. Most importantly, vm::ctx_t::calc_jmp, vm::ctx_t::vm_entry, and vm::ctx_t::vm_handlers. An example usage of this data could be dumping the native x86_64 instructions which make up vm::ctx_t::vm_entry. Example c++ code for this is displayed below.

std::puts( "======================== [vm entry] ========================\n" );
vm::util::print( vmctx.vm_entry );

Output

======================== [vm entry] ========================
> 0x00007FF7911A822C push 0xFFFFFFFF890001FA
> 0x00007FF7911A7FC9 push 0x45D3BF1F
> 0x00007FF7911A48E4 push r13
> 0x00007FF7911A4690 push rsi
> 0x00007FF7911A4E53 push r14
> 0x00007FF7911A74FB push rcx
> 0x00007FF7911A607C push rsp
> 0x00007FF7911A4926 pushfq
> 0x00007FF7911A4DC2 push rbp
> 0x00007FF7911A5C8C push r12
> 0x00007FF7911A52AC push r10
> 0x00007FF7911A51A5 push r9
> 0x00007FF7911A5189 push rdx
> 0x00007FF7911A7D5F push r8
> 0x00007FF7911A4505 push rdi
> 0x00007FF7911A4745 push r11
> 0x00007FF7911A478B push rax
> 0x00007FF7911A7A53 push rbx
> 0x00007FF7911A500D push r15
> 0x00007FF7911A6030 push [0x00007FF7911A7912]
> 0x00007FF7911A593A mov rax, 0x7FF6511A0000
> 0x00007FF7911A5955 mov r13, rax
> 0x00007FF7911A595F test dl, al
> 0x00007FF7911A5965 push rax
> 0x00007FF7911A5969 btr si, bx
> 0x00007FF7911A596F mov esi, [rsp+0xA0]
> 0x00007FF7911A5979 not esi
> 0x00007FF7911A5985 neg esi
> 0x00007FF7911A598D ror esi, 0x1A
> 0x00007FF7911A599E mov rbp, rsp
> 0x00007FF7911A59A8 sub rsp, 0x140
> 0x00007FF7911A59B5 and rsp, 0xFFFFFFFFFFFFFFF0
> 0x00007FF7911A59BE inc ax
> 0x00007FF7911A59C1 mov rdi, rsp
> 0x00007FF7911A59C7 bsr r12, rax
> 0x00007FF7911A59CB lea r12, [0x00007FF7911A6473]
> 0x00007FF7911A59DF mov rax, 0x100000000
> 0x00007FF7911A59EC add rsi, rax
> 0x00007FF7911A59F3 mov rbx, rsi
> 0x00007FF7911A59FA add rsi, [rbp]
> 0x00007FF7911A5A03 rcr dl, cl
> 0x00007FF7911A5A05 mov al, [rsi]
> 0x00007FF7911A5A0A xor al, bl
> 0x00007FF7911A5A11 neg al
> 0x00007FF7911A5A19 rol al, 0x05
> 0x00007FF7911A5A26 inc al
> 0x00007FF7911A5A2F xor bl, al
> 0x00007FF7911A5A34 movzx rax, al
> 0x00007FF7911A5A41 mov rdx, [r12+rax*8]
> 0x00007FF7911A5A49 xor rdx, 0x7F3D2149
> 0x00007FF7911A5507 inc rsi
> 0x00007FF7911A7951 add rdx, r13
> 0x00007FF7911A7954 jmp rdx

You can also loop through all vm handlers easily. Below is an example of looping through the vm handler vector inside of vm::ctx_t::vm_handlers.

for ( auto idx = 0u; idx < vmctx.vm_handlers.size(); ++idx )
{
    std::printf( "======================== [%s #%d] ========================\n",
                 vmctx.vm_handlers[ idx ].profile ? vmctx.vm_handlers[ idx ].profile->name : "UNK", idx );

    vm::util::print( vmctx.vm_handlers[ idx ].instrs );

    // if there is no imm then there are no transforms...
    if ( !vmctx.vm_handlers[ idx ].imm_size )
    {
        std::puts( "\n" );
        continue;
    }

    std::puts( "======================== [transforms] ========================\n" );
    for ( auto &[ mnemonic, instr ] : vmctx.vm_handlers[ idx ].transforms )
    {
        if ( instr.mnemonic == ZYDIS_MNEMONIC_INVALID )
            continue;

        vm::util::print( instr );
    }
    std::puts( "\n" );
}

Output

...

======================== [LCONSTBZXW #253] ========================
> 0x00007FF6DEA85C2C and al, 0x45
> 0x00007FF6DEA85C2E movzx eax, byte ptr [rsi]
> 0x00007FF6DEA85C82 add al, bl
> 0x00007FF6DEA85C85 add al, 0xD3
> 0x00007FF6DEA86FC7 not al
> 0x00007FF6DEA84D23 inc al
> 0x00007FF6DEA85633 add bl, al
> 0x00007FF6DEA853D5 sub rsi, 0xFFFFFFFFFFFFFFFF
> 0x00007FF6DEA85CD1 sub rbp, 0x02
> 0x00007FF6DEA862F8 mov [rbp], ax
> 0x00007FF6DEA844A7 rol ah, 0x07
======================== [transforms] ========================

add al, bl
add al, 0xD3
not al
inc al
add bl, al

...

Copyright (c) 2021 _xeroxz, Independent Researcher @back.engineering

Licensed under the MIT License