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
IDontCode 900d4cb888
Update README.md
11 months ago
dependencies removed xtils from the CMakeLists file 11 months ago
doxygen added manuals... 1 year ago
include multi thread support 1 year ago
src added some code to vm::util::flatten so that vmp3 bins can be supported 12 months ago
.clang-format added some code to vm::util::flatten so that vmp3 bins can be supported 12 months ago
.gitignore removed xtils from the CMakeLists file 11 months ago
.gitmodules removed xtils because its not linux friendly... 1 year ago
CMakeLists.txt added more vm handler profiles... 1 year ago
LICENSE Add LICENSE 2 years ago
README.md Update README.md 11 months ago
cmake.toml removed xtils because its not linux friendly... 1 year ago
cmkr.cmake Port to cmkr 1 year 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