updated vmprofiler, added some sanity check stuff, also handle div by 0

dev
_xeroxz 3 years ago
parent 6f668423d9
commit 9e301d70e8

@ -1 +1 @@
Subproject commit d96f065dd3a696f0b0ef790378e287f2356ecc8d Subproject commit 1b35119de4d5edd74622a282bd4fbdde492d7787

@ -12,6 +12,8 @@
namespace vm namespace vm
{ {
inline bool g_force_emu = false;
class emu_t class emu_t
{ {
struct cpu_ctx_t struct cpu_ctx_t
@ -37,7 +39,7 @@ namespace vm
private: private:
std::uintptr_t img_base, img_size; 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; uc_engine *uc_ctx;
vm::ctx_t *g_vm_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; std::map< std::uintptr_t, std::shared_ptr< vm::ctx_t > > vm_ctxs;
uc_err create_entry( vmp2::v2::entry_t *entry ); 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 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, static void invalid_mem( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value,
emu_t *obj ); emu_t *obj );

@ -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( "--bin" ).description( "path to unpacked virtualized binary..." );
parser.add_argument().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( "--unpack" ).description( "unpack a vmp2 binary..." );
parser.add_argument().names( { "-f", "--force" } ).description( "force emulation of unknown vm handlers...\n" );
parser.add_argument() parser.add_argument()
.name( "--emuall" ) .name( "--emuall" )
.description( "scan for all vm enters and trace all of them... this may take a few minutes..." ); .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(); 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" ) ) 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 ) ) if ( !emu.get_trace( code_blocks ) )
{ {
std::printf( "[!] something failed during tracing, review the console for more information...\n" ); 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() ); std::printf( "> number of blocks = %d\n", code_blocks.size() );

@ -48,6 +48,12 @@ namespace vm
return false; 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, 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_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED |
UC_HOOK_INSN_INVALID, UC_HOOK_INSN_INVALID,
@ -361,6 +367,7 @@ namespace vm
static std::shared_ptr< vm::ctx_t > _jmp_ctx; static std::shared_ptr< vm::ctx_t > _jmp_ctx;
static zydis_routine_t _jmp_stream; static zydis_routine_t _jmp_stream;
static auto inst_cnt = 0ull;
static ZydisDecoder decoder; static ZydisDecoder decoder;
static ZydisFormatter formatter; static ZydisFormatter formatter;
@ -376,6 +383,7 @@ namespace vm
ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( address ), PAGE_4KB, &instr ) ) ) ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( address ), PAGE_4KB, &instr ) ) )
{ {
std::printf( "> failed to decode instruction at = 0x%p\n", address ); std::printf( "> failed to decode instruction at = 0x%p\n", address );
if ( ( err = uc_emu_stop( uc ) ) ) if ( ( err = uc_emu_stop( uc ) ) )
{ {
std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); std::printf( "> failed to stop emulation, exiting... reason = %d\n", err );
@ -391,6 +399,15 @@ namespace vm
return false; 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 // 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... // 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 && 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 ) ) ) instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX ) ) )
return true; return true;
// reset sanity check...
inst_cnt = 0ull;
// extract address of vm handler table... // extract address of vm handler table...
switch ( instr.operands[ 0 ].reg.value ) switch ( instr.operands[ 0 ].reg.value )
{ {
@ -471,20 +491,18 @@ namespace vm
if ( !vm_handler.profile ) if ( !vm_handler.profile )
{ {
if ( !g_force_emu )
obj->cc_block = nullptr; obj->cc_block = nullptr;
std::printf( "> virtual machine handler (0x%p): \n\n",
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_handler_addr - obj->g_vm_ctx->module_base ) + obj->g_vm_ctx->image_base );
vm::util::print( vm_handler.instrs ); vm::util::print( vm_handler.instrs );
std::printf( "\n\n" ); std::printf( "\n\n" );
if ( ( err = uc_emu_stop( uc ) ) ) if ( !g_force_emu )
{
std::printf( "> failed to stop emulation, exiting... reason = %d\n", err );
exit( 0 ); exit( 0 );
} }
return false;
}
auto vinstr = vm::instrs::get( *obj->g_vm_ctx, vinstr_entry ); auto vinstr = vm::instrs::get( *obj->g_vm_ctx, vinstr_entry );
@ -611,6 +629,45 @@ namespace vm
return true; 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 ) 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 ) switch ( type )

Loading…
Cancel
Save