|
|
@ -3,47 +3,19 @@
|
|
|
|
namespace vm
|
|
|
|
namespace vm
|
|
|
|
{
|
|
|
|
{
|
|
|
|
emu_t::emu_t( std::uint32_t vm_entry_rva, std::uintptr_t image_base, std::uintptr_t module_base )
|
|
|
|
emu_t::emu_t( std::uint32_t vm_entry_rva, std::uintptr_t image_base, std::uintptr_t module_base )
|
|
|
|
: module_base( module_base ), image_base( image_base ), vm_entry_rva( vm_entry_rva ),
|
|
|
|
: module_base( module_base ), image_base( image_base ), vm_entry_rva( vm_entry_rva ), uc( nullptr ),
|
|
|
|
vm_handler_table( nullptr ), uc( nullptr ), trace_entries( nullptr )
|
|
|
|
code_blocks( nullptr ), vmctx( new vm::ctx_t( module_base, image_base, vm_entry_rva ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool emu_t::init()
|
|
|
|
bool emu_t::init()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// vmprofiler init stuff...
|
|
|
|
uc_err err;
|
|
|
|
if ( !vm::util::flatten( vm_entry, vm_entry_rva + module_base ) )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::printf( "[!] failed to get vm entry...\n" );
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vm::util::deobfuscate( vm_entry );
|
|
|
|
|
|
|
|
vm::util::print( vm_entry );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( !( vm_handler_table = vm::handler::table::get( vm_entry ) ) )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::printf( "[!] failed to get vm handler table...\n" );
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::printf( "> vm handler table = 0x%p\n", vm_handler_table );
|
|
|
|
|
|
|
|
if ( !vm::handler::get_all( module_base, image_base, vm_entry, vm_handler_table, vm_handlers ) )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::printf( "[!] failed to get all vm handlers...\n" );
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::printf( "> got all vm handlers...\n" );
|
|
|
|
|
|
|
|
for ( const vm::handler::handler_t &vm_handler : vm_handlers )
|
|
|
|
|
|
|
|
std::printf( ">>> handler addr = 0x%p\n", vm_handler.address );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// unicorn init stuff...
|
|
|
|
|
|
|
|
const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::uintptr_t stack_base = 0x1000000;
|
|
|
|
std::uintptr_t stack_base = 0x1000000;
|
|
|
|
std::uintptr_t stack_addr = ( stack_base + ( 0x1000 * 20 ) ) - 0x6000;
|
|
|
|
std::uintptr_t stack_addr = ( stack_base + ( 0x1000 * 20 ) ) - 0x6000;
|
|
|
|
|
|
|
|
const auto rip = module_base + vm_entry_rva;
|
|
|
|
|
|
|
|
const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage;
|
|
|
|
|
|
|
|
|
|
|
|
uc_err err;
|
|
|
|
|
|
|
|
if ( ( err = uc_open( UC_ARCH_X86, UC_MODE_64, &uc ) ) )
|
|
|
|
if ( ( err = uc_open( UC_ARCH_X86, UC_MODE_64, &uc ) ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::printf( "failed on uc_mem_map() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
|
std::printf( "failed on uc_mem_map() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
@ -72,7 +44,7 @@ namespace vm
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_reg_write( uc, UC_X86_REG_RIP, &vm_entry ) ) )
|
|
|
|
if ( ( err = uc_reg_write( uc, UC_X86_REG_RIP, &rip ) ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::printf( "failed on uc_reg_write() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
|
std::printf( "failed on uc_reg_write() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
|
|
|
|
|
|
|
|
@ -108,12 +80,14 @@ namespace vm
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if ( uc )
|
|
|
|
if ( uc )
|
|
|
|
uc_close( uc );
|
|
|
|
uc_close( uc );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
delete vmctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool emu_t::get_trace( std::vector< vmp2::v2::entry_t > &entries )
|
|
|
|
bool emu_t::get_trace( std::vector< vm::instrs::code_block_t > &entries )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// hook_code will fill this vector up with values...
|
|
|
|
// hook_code will fill this vector up with values...
|
|
|
|
trace_entries = &entries;
|
|
|
|
code_blocks = &entries;
|
|
|
|
uc_err err;
|
|
|
|
uc_err err;
|
|
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_emu_start( uc, vm_entry_rva + module_base, NULL, NULL, NULL ) ) )
|
|
|
|
if ( ( err = uc_emu_start( uc, vm_entry_rva + module_base, NULL, NULL, NULL ) ) )
|
|
|
@ -166,17 +140,15 @@ namespace vm
|
|
|
|
std::printf( ">>> Tracing instruction at 0x%p, instruction size = 0x%x\n", address, size );
|
|
|
|
std::printf( ">>> Tracing instruction at 0x%p, instruction size = 0x%x\n", address, size );
|
|
|
|
|
|
|
|
|
|
|
|
// grab JMP RDX/RCX <-- this register...
|
|
|
|
// grab JMP RDX/RCX <-- this register...
|
|
|
|
static const auto jmp_reg = obj->vm_entry[ obj->vm_entry.size() ].instr.operands[ 0 ].reg.value;
|
|
|
|
static const auto jmp_reg = obj->vmctx->vm_entry[ obj->vmctx->vm_entry.size() ].instr.operands[ 0 ].reg.value;
|
|
|
|
|
|
|
|
|
|
|
|
static ZydisDecoder decoder;
|
|
|
|
static ZydisDecoder decoder;
|
|
|
|
static std::once_flag once;
|
|
|
|
|
|
|
|
static ZydisDecodedInstruction instr;
|
|
|
|
static ZydisDecodedInstruction instr;
|
|
|
|
static std::uintptr_t reg_val = 0u;
|
|
|
|
static std::uintptr_t reg_val = 0u;
|
|
|
|
|
|
|
|
|
|
|
|
// init zydis decoder just a single time...
|
|
|
|
// init zydis decoder only a single time...
|
|
|
|
std::call_once( once, [ & ]() -> void {
|
|
|
|
if ( static std::atomic< bool > once = true; once.exchange( false ) )
|
|
|
|
ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 );
|
|
|
|
ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 );
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( ZYAN_SUCCESS(
|
|
|
|
if ( ZYAN_SUCCESS(
|
|
|
|
ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( address ), size, &instr ) ) &&
|
|
|
|
ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( address ), size, &instr ) ) &&
|
|
|
@ -185,6 +157,7 @@ namespace vm
|
|
|
|
{
|
|
|
|
{
|
|
|
|
uc_err err;
|
|
|
|
uc_err err;
|
|
|
|
vmp2::v2::entry_t new_entry;
|
|
|
|
vmp2::v2::entry_t new_entry;
|
|
|
|
|
|
|
|
std::optional< vm::instrs::virt_instr_t > virt_instr;
|
|
|
|
vm::handler::profile_t *vm_handler_profile = nullptr;
|
|
|
|
vm::handler::profile_t *vm_handler_profile = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
switch ( jmp_reg )
|
|
|
|
switch ( jmp_reg )
|
|
|
@ -206,8 +179,8 @@ namespace vm
|
|
|
|
return vm_handler.address == reg_val;
|
|
|
|
return vm_handler.address == reg_val;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if ( std::find_if( obj->vm_handlers.begin(), obj->vm_handlers.end(), vm_handler_check ) ==
|
|
|
|
if ( std::find_if( obj->vmctx->vm_handlers.begin(), obj->vmctx->vm_handlers.end(), vm_handler_check ) ==
|
|
|
|
obj->vm_handlers.end() )
|
|
|
|
obj->vmctx->vm_handlers.end() )
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if ( ( err = obj->create_entry( &new_entry ) ) )
|
|
|
|
if ( ( err = obj->create_entry( &new_entry ) ) )
|
|
|
@ -217,21 +190,34 @@ namespace vm
|
|
|
|
exit( 0 );
|
|
|
|
exit( 0 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( ( vm_handler_profile = obj->vm_handlers[ new_entry.handler_idx ].profile ) &&
|
|
|
|
// the first virtual instruction we are going to create the first code_block_t...
|
|
|
|
vm_handler_profile->mnemonic == vm::handler::mnemonic_t::JMP )
|
|
|
|
if ( static std::atomic< bool > once = true; once.exchange( false ) )
|
|
|
|
|
|
|
|
if ( obj->code_blocks->empty() )
|
|
|
|
|
|
|
|
obj->code_blocks->push_back( vm::instrs::code_block_t{ new_entry.vip } );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( virt_instr = vm::instrs::get( *obj->vmctx, new_entry ); !virt_instr.has_value() )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::printf( "> stopping at virtual jump instruction...\n" );
|
|
|
|
std::printf( "[!] failed to create vm::instrs::virt_instr_t...\n" );
|
|
|
|
std::getchar();
|
|
|
|
|
|
|
|
|
|
|
|
exit( 0 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
obj->trace_entries->push_back( new_entry );
|
|
|
|
obj->code_blocks->back().vinstrs.push_back( virt_instr.value() );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if there is a virtual JMP instruction then we need to create a new code_block_t...
|
|
|
|
|
|
|
|
if ( ( vm_handler_profile = obj->vmctx->vm_handlers[ new_entry.handler_idx ].profile ) &&
|
|
|
|
|
|
|
|
vm_handler_profile->mnemonic == vm::handler::mnemonic_t::JMP )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const auto code_block_address =
|
|
|
|
|
|
|
|
vm::instrs::code_block_addr( *obj->vmctx, new_entry, obj->image_base, obj->module_base );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set the next code block up...
|
|
|
|
|
|
|
|
obj->code_blocks->push_back( vm::instrs::code_block_t{ code_block_address } );
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( instr.mnemonic == ZYDIS_MNEMONIC_RET ) // finish tracing...
|
|
|
|
else if ( instr.mnemonic == ZYDIS_MNEMONIC_RET ) // finish tracing...
|
|
|
|
{
|
|
|
|
{
|
|
|
|
uc_emu_stop( uc );
|
|
|
|
uc_emu_stop( uc );
|
|
|
|
|
|
|
|
|
|
|
|
std::printf( "> stopping at vmexit instruction...\n" );
|
|
|
|
|
|
|
|
std::getchar();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|