diff --git a/dependencies/vmprofiler b/dependencies/vmprofiler index d96f065..1b35119 160000 --- a/dependencies/vmprofiler +++ b/dependencies/vmprofiler @@ -1 +1 @@ -Subproject commit d96f065dd3a696f0b0ef790378e287f2356ecc8d +Subproject commit 1b35119de4d5edd74622a282bd4fbdde492d7787 diff --git a/include/vmemu_t.hpp b/include/vmemu_t.hpp index 8f98089..0143222 100644 --- a/include/vmemu_t.hpp +++ b/include/vmemu_t.hpp @@ -12,6 +12,8 @@ namespace vm { + inline bool g_force_emu = false; + class emu_t { struct cpu_ctx_t @@ -37,7 +39,7 @@ namespace vm private: std::uintptr_t img_base, img_size; - uc_hook code_exec_hook, invalid_mem_hook; + uc_hook code_exec_hook, invalid_mem_hook, int_hook; uc_engine *uc_ctx; vm::ctx_t *g_vm_ctx; @@ -48,6 +50,7 @@ namespace vm std::map< std::uintptr_t, std::shared_ptr< vm::ctx_t > > vm_ctxs; uc_err create_entry( vmp2::v2::entry_t *entry ); + static void int_callback( uc_engine *uc, std::uint32_t intno, emu_t *obj ); 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, emu_t *obj ); diff --git a/src/main.cpp b/src/main.cpp index 455ddfb..7de4fcf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ int __cdecl main( int argc, const char *argv[] ) parser.add_argument().name( "--bin" ).description( "path to unpacked virtualized binary..." ); parser.add_argument().name( "--out" ).description( "output file name..." ); parser.add_argument().name( "--unpack" ).description( "unpack a vmp2 binary..." ); + parser.add_argument().names( { "-f", "--force" } ).description( "force emulation of unknown vm handlers...\n" ); parser.add_argument() .name( "--emuall" ) .description( "scan for all vm enters and trace all of them... this may take a few minutes..." ); @@ -37,6 +38,7 @@ int __cdecl main( int argc, const char *argv[] ) } auto umtils = xtils::um_t::get_instance(); + vm::g_force_emu = parser.exists( "force" ); if ( !parser.exists( "unpack" ) && parser.exists( "vmentry" ) && parser.exists( "bin" ) && parser.exists( "out" ) ) { @@ -256,7 +258,7 @@ int __cdecl main( int argc, const char *argv[] ) if ( !emu.get_trace( code_blocks ) ) { std::printf( "[!] something failed during tracing, review the console for more information...\n" ); - return -1; + continue; } std::printf( "> number of blocks = %d\n", code_blocks.size() ); diff --git a/src/vmemu_t.cpp b/src/vmemu_t.cpp index 5e8f475..70b88fb 100644 --- a/src/vmemu_t.cpp +++ b/src/vmemu_t.cpp @@ -48,6 +48,12 @@ namespace vm return false; } + if ( ( err = uc_hook_add( uc_ctx, &int_hook, UC_HOOK_INTR, &vm::emu_t::int_callback, this, 0ull, 0ull ) ) ) + { + std::printf( "> uc_hook_add error, reason = %d\n", err ); + return false; + } + if ( ( err = uc_hook_add( uc_ctx, &invalid_mem_hook, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_INSN_INVALID, @@ -361,6 +367,7 @@ namespace vm static std::shared_ptr< vm::ctx_t > _jmp_ctx; static zydis_routine_t _jmp_stream; + static auto inst_cnt = 0ull; static ZydisDecoder decoder; static ZydisFormatter formatter; @@ -376,6 +383,7 @@ namespace vm ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( address ), PAGE_4KB, &instr ) ) ) { std::printf( "> failed to decode instruction at = 0x%p\n", address ); + if ( ( err = uc_emu_stop( uc ) ) ) { std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); @@ -391,6 +399,15 @@ namespace vm return false; } + // if there are over 4k instructions executed before a JMP is found then we are gunna stop emulation + // this is a sanity check to prevent inf loops... + if ( ++inst_cnt > 0x1000 ) + { + obj->cc_block = nullptr, inst_cnt = 0ull; + uc_emu_stop( uc ); + return false; + } + // if the native instruction is a jmp rcx/rdx... then AL will contain the vm handler // table index of the vm handler that the emulator is about to jmp too... if ( !( instr.mnemonic == ZYDIS_MNEMONIC_JMP && instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && @@ -398,6 +415,9 @@ namespace vm instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX ) ) ) return true; + // reset sanity check... + inst_cnt = 0ull; + // extract address of vm handler table... switch ( instr.operands[ 0 ].reg.value ) { @@ -471,19 +491,17 @@ namespace vm if ( !vm_handler.profile ) { - obj->cc_block = nullptr; - std::printf( "> virtual machine handler (0x%p): \n\n", + if ( !g_force_emu ) + obj->cc_block = nullptr; + + std::printf( "> please define virtual machine handler (0x%p): \n\n", ( vm_handler_addr - obj->g_vm_ctx->module_base ) + obj->g_vm_ctx->image_base ); vm::util::print( vm_handler.instrs ); std::printf( "\n\n" ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); + if ( !g_force_emu ) exit( 0 ); - } - return false; } auto vinstr = vm::instrs::get( *obj->g_vm_ctx, vinstr_entry ); @@ -611,6 +629,45 @@ namespace vm return true; } + void emu_t::int_callback( uc_engine *uc, std::uint32_t intno, emu_t *obj ) + { + uc_err err; + std::uintptr_t rip = 0ull; + static ZydisDecoder decoder; + static ZydisDecodedInstruction instr; + + if ( static std::atomic< bool > once{ false }; !once.exchange( true ) ) + ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); + + if ( ( err = uc_reg_read( uc, UC_X86_REG_RIP, &rip ) ) ) + { + std::printf( "> failed to read rip... reason = %d\n", err ); + return; + } + + if ( !ZYAN_SUCCESS( + ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( rip ), PAGE_4KB, &instr ) ) ) + { + std::printf( "> failed to decode instruction at = 0x%p\n", rip ); + + if ( ( err = uc_emu_stop( uc ) ) ) + { + std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); + exit( 0 ); + } + return; + } + + // advance rip over the instruction that caused the exception... + rip += instr.length; + + if ( ( err = uc_reg_write( uc, UC_X86_REG_RIP, &rip ) ) ) + { + std::printf( "> failed to write rip... reason = %d\n", err ); + return; + } + } + void emu_t::invalid_mem( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, emu_t *obj ) { switch ( type )