finished vmemu recode, added --locateconst

merge-requests/6/head
_xeroxz 3 years ago
parent e9945bde6c
commit 3f8fea77cf

@ -42,6 +42,8 @@ namespace vm
std::vector< std::uintptr_t > vip_begins;
std::vector< code_block_data_t > code_blocks;
std::map< std::uintptr_t, std::shared_ptr< vm::ctx_t > > vm_ctxs;
uc_err create_entry( vmp2::v2::entry_t *entry );
static bool code_exec_callback( uc_engine *uc, uint64_t address, uint32_t size, emu_t *obj );
static void invalid_mem( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value,

@ -11,8 +11,11 @@ int __cdecl main( int argc, const char *argv[] )
argparse::argument_parser_t parser( "VMEmu", "VMProtect 2 VM Handler Emulator" );
parser.add_argument().name( "--vmentry" ).description( "relative virtual address to a vm entry..." );
parser.add_argument().name( "--bin" ).description( "path to unpacked virtualized binary..." );
parser.add_argument().required( true ).name( "--out" ).description( "output file name..." );
parser.add_argument().name( "--out" ).description( "output file name..." );
parser.add_argument().name( "--unpack" ).description( "unpack a vmp2 binary..." );
parser.add_argument()
.name( "--locateconst" )
.description( "scan all vm enters for a specific constant value...\n" );
parser.enable_help();
auto result = parser.parse( argc, argv );
@ -31,7 +34,7 @@ int __cdecl main( int argc, const char *argv[] )
auto umtils = xtils::um_t::get_instance();
if ( !parser.exists( "unpack" ) && parser.exists( "vmentry" ) && parser.exists( "bin" ) )
if ( !parser.exists( "unpack" ) && parser.exists( "vmentry" ) && parser.exists( "bin" ) && parser.exists( "out" ) )
{
const auto module_base = reinterpret_cast< std::uintptr_t >(
LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) );
@ -127,7 +130,7 @@ int __cdecl main( int argc, const char *argv[] )
output.close();
}
else
else if ( parser.exists( "unpack" ) && parser.exists( "bin" ) && parser.exists( "out" ) )
{
std::vector< std::uint8_t > packed_bin, unpacked_bin;
if ( !umtils->open_binary_file( parser.get< std::string >( "unpack" ), packed_bin ) )
@ -155,4 +158,73 @@ int __cdecl main( int argc, const char *argv[] )
output.write( reinterpret_cast< char * >( unpacked_bin.data() ), unpacked_bin.size() );
output.close();
}
else if ( parser.exists( "bin" ) && parser.exists( "locateconst" ) )
{
std::vector< vm::instrs::code_block_t > code_blocks;
const auto module_base = reinterpret_cast< std::uintptr_t >(
LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) );
const auto const_val = std::strtoull( parser.get< std::string >( "locateconst" ).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;
auto vm_handler_tables = vm::locate::all_handler_tables( module_base );
auto vm_enters = vm::locate::all_vm_enters( module_base, vm_handler_tables );
std::printf( "> number of vm enters = %d\n", vm_enters.size() );
if ( std::find_if( vm_enters.begin() + 1, vm_enters.end(),
[ & ]( const std::pair< std::uint32_t, std::uint32_t > &vm_enter_data ) -> bool {
return vm_enter_data.second == vm_enters[ 0 ].second;
} ) != vm_enters.end() )
{
std::printf( "> optimizations can be done.\n" );
std::getchar();
}
for ( const auto &[ vm_enter_offset, encrypted_rva ] : vm_enters )
{
std::printf( "> emulating vm enter at rva = 0x%x\n", vm_enter_offset );
vm::ctx_t vm_ctx( module_base, image_base, image_size, vm_enter_offset );
if ( !vm_ctx.init() )
{
std::printf( "[!] failed to init vmctx... this can be for many reasons..."
" try validating your vm entry rva... make sure the binary is unpacked and is"
"protected with VMProtect 2...\n" );
return -1;
}
vm::emu_t emu( &vm_ctx );
if ( !emu.init() )
{
std::printf( "[!] failed to init emulator...\n" );
return -1;
}
std::vector< vm::instrs::code_block_t > new_code_blocks;
if ( !emu.get_trace( new_code_blocks ) )
{
std::printf( "[!] something failed during tracing, review the console for more information...\n" );
return -1;
}
std::printf( "> number of blocks = %d\n", new_code_blocks.size() );
for ( auto &code_block : new_code_blocks )
{
for ( const auto &vinstr : code_block.vinstrs )
{
if ( vinstr.operand.has_imm && vinstr.operand.imm.u == const_val )
{
std::printf( "> found constant in vm enter at = 0x%x\n", vm_enter_offset );
std::getchar();
}
}
}
code_blocks.insert( code_blocks.end(), new_code_blocks.begin(), new_code_blocks.end() );
}
}
}

@ -92,35 +92,35 @@ namespace vm
// when idx is > code_blocks.size() then we have traced all branches...
for ( auto idx = 0u; idx < code_blocks.size(); ++idx )
{
const auto &code_block = code_blocks[ idx ];
if ( !code_block.code_block.jcc.has_jcc )
const auto _code_block = code_blocks[ idx ];
if ( !_code_block.code_block.jcc.has_jcc )
continue;
switch ( code_block.code_block.jcc.type )
switch ( _code_block.code_block.jcc.type )
{
case vm::instrs::jcc_type::branching:
{
if ( std::find( vip_begins.begin(), vip_begins.end(), code_block.code_block.jcc.block_addr[ 1 ] ) !=
if ( std::find( vip_begins.begin(), vip_begins.end(), _code_block.code_block.jcc.block_addr[ 1 ] ) !=
vip_begins.end() )
continue;
std::uintptr_t rbp = 0ull;
std::uint32_t branch_rva = code_block.code_block.jcc.block_addr[ 1 ];
std::uint32_t branch_rva = _code_block.code_block.jcc.block_addr[ 1 ];
// setup object globals so that the tracing will work...
code_block_data_t branch_block{ { code_block.cpu_ctx->rip }, nullptr, nullptr };
code_block_data_t branch_block{ { _code_block.cpu_ctx->rip }, nullptr, nullptr };
cc_block = &branch_block;
g_vm_ctx = code_block.g_vm_ctx.get();
g_vm_ctx = _code_block.g_vm_ctx.get();
// restore register values...
if ( ( err = uc_context_restore( uc_ctx, code_block.cpu_ctx->context ) ) )
if ( ( err = uc_context_restore( uc_ctx, _code_block.cpu_ctx->context ) ) )
{
std::printf( "> failed to restore emu context... reason = %d\n", err );
return false;
}
// restore stack values...
if ( ( err = uc_mem_write( uc_ctx, STACK_BASE, code_block.cpu_ctx->stack, STACK_SIZE ) ) )
if ( ( err = uc_mem_write( uc_ctx, STACK_BASE, _code_block.cpu_ctx->stack, STACK_SIZE ) ) )
{
std::printf( "> failed to restore stack... reason = %d\n", err );
return false;
@ -140,8 +140,8 @@ namespace vm
return false;
}
std::printf( "> beginning execution at = 0x%p\n", code_block.cpu_ctx->rip );
if ( ( err = uc_emu_start( uc_ctx, code_block.cpu_ctx->rip, 0ull, 0ull, 0ull ) ) )
std::printf( "> beginning execution at = 0x%p\n", _code_block.cpu_ctx->rip );
if ( ( err = uc_emu_start( uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull ) ) )
{
std::printf( "> error starting emu... reason = %d\n", err );
return false;
@ -155,27 +155,27 @@ namespace vm
}
case vm::instrs::jcc_type::absolute:
{
if ( std::find( vip_begins.begin(), vip_begins.end(), code_block.code_block.jcc.block_addr[ 0 ] ) !=
if ( std::find( vip_begins.begin(), vip_begins.end(), _code_block.code_block.jcc.block_addr[ 0 ] ) !=
vip_begins.end() )
continue;
std::uintptr_t rbp = 0ull;
std::uint32_t branch_rva = code_block.code_block.jcc.block_addr[ 0 ];
std::uint32_t branch_rva = _code_block.code_block.jcc.block_addr[ 0 ];
// setup object globals so that the tracing will work...
code_block_data_t branch_block{ { code_block.cpu_ctx->rip }, nullptr, nullptr };
code_block_data_t branch_block{ { _code_block.cpu_ctx->rip }, nullptr, nullptr };
cc_block = &branch_block;
g_vm_ctx = code_block.g_vm_ctx.get();
g_vm_ctx = _code_block.g_vm_ctx.get();
// restore register values...
if ( ( err = uc_context_restore( uc_ctx, code_block.cpu_ctx->context ) ) )
if ( ( err = uc_context_restore( uc_ctx, _code_block.cpu_ctx->context ) ) )
{
std::printf( "> failed to restore emu context... reason = %d\n", err );
return false;
}
// restore stack values...
if ( ( err = uc_mem_write( uc_ctx, STACK_BASE, code_block.cpu_ctx->stack, STACK_SIZE ) ) )
if ( ( err = uc_mem_write( uc_ctx, STACK_BASE, _code_block.cpu_ctx->stack, STACK_SIZE ) ) )
{
std::printf( "> failed to restore stack... reason = %d\n", err );
return false;
@ -195,8 +195,8 @@ namespace vm
return false;
}
std::printf( "> beginning execution at = 0x%p\n", code_block.cpu_ctx->rip );
if ( ( err = uc_emu_start( uc_ctx, code_block.cpu_ctx->rip, 0ull, 0ull, 0ull ) ) )
std::printf( "> beginning execution at = 0x%p\n", _code_block.cpu_ctx->rip );
if ( ( err = uc_emu_start( uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull ) ) )
{
std::printf( "> error starting emu... reason = %d\n", err );
return false;
@ -258,6 +258,9 @@ namespace vm
std::uint8_t vm_handler_table_idx = 0u;
std::uintptr_t vm_handler_addr;
static std::shared_ptr< vm::ctx_t > _jmp_ctx;
static zydis_routine_t _jmp_stream;
static ZydisDecoder decoder;
static ZydisFormatter formatter;
static ZydisDecodedInstruction instr;
@ -402,9 +405,14 @@ namespace vm
// allocate space for the cpu context and stack...
auto new_cpu_ctx = std::make_shared< vm::emu_t::cpu_ctx_t >();
auto new_vm_ctx = std::make_shared< vm::ctx_t >( obj->g_vm_ctx->module_base, obj->img_base,
obj->img_size, vm_handler_addr - obj->img_base );
if ( !new_vm_ctx->init() )
// optimize so that we dont need to create a new vm::ctx_t every single virtual JMP...
if ( obj->vm_ctxs.find( vm_handler_addr ) == obj->vm_ctxs.end() )
{
obj->vm_ctxs[ vm_handler_addr ] = std::make_shared< vm::ctx_t >(
obj->g_vm_ctx->module_base, obj->img_base, obj->img_size, vm_handler_addr - obj->img_base );
if ( !obj->vm_ctxs[ vm_handler_addr ]->init() )
{
std::printf( "> failed to init vm::ctx_t for virtual jmp... vip = 0x%p, jmp handler = 0x%p\n",
vinstr_entry.vip, vm_handler_addr );
@ -416,7 +424,9 @@ namespace vm
}
return false;
}
}
_jmp_ctx = obj->vm_ctxs[ vm_handler_addr ];
if ( ( err = uc_context_alloc( uc, &new_cpu_ctx->context ) ) )
{
std::printf( "> failed to allocate a unicorn context... reason = %d\n", err );
@ -460,7 +470,7 @@ namespace vm
}
obj->cc_block->cpu_ctx = new_cpu_ctx;
obj->cc_block->g_vm_ctx = new_vm_ctx;
obj->cc_block->g_vm_ctx = _jmp_ctx;
break;
}
default:

Loading…
Cancel
Save