|
|
|
@ -2,16 +2,11 @@
|
|
|
|
|
|
|
|
|
|
namespace vm
|
|
|
|
|
{
|
|
|
|
|
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),
|
|
|
|
|
vm_handler_table(nullptr),
|
|
|
|
|
uc(nullptr),
|
|
|
|
|
trace_entries(nullptr)
|
|
|
|
|
{}
|
|
|
|
|
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 ),
|
|
|
|
|
vm_handler_table( nullptr ), uc( nullptr ), trace_entries( nullptr )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool emu_t::init()
|
|
|
|
|
{
|
|
|
|
@ -43,8 +38,7 @@ namespace vm
|
|
|
|
|
std::printf( ">>> handler addr = 0x%p\n", vm_handler.address );
|
|
|
|
|
|
|
|
|
|
// unicorn init stuff...
|
|
|
|
|
const auto image_size =
|
|
|
|
|
NT_HEADER(module_base)->OptionalHeader.SizeOfImage;
|
|
|
|
|
const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage;
|
|
|
|
|
|
|
|
|
|
std::uintptr_t stack_base = 0x1000000;
|
|
|
|
|
std::uintptr_t stack_addr = ( stack_base + ( 0x1000 * 20 ) ) - 0x6000;
|
|
|
|
@ -52,57 +46,50 @@ namespace vm
|
|
|
|
|
uc_err err;
|
|
|
|
|
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 ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_mem_map( uc, module_base, image_size, UC_PROT_ALL ) ) )
|
|
|
|
|
{
|
|
|
|
|
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 ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_mem_map( uc, 0x1000000, 0x1000 * 20, UC_PROT_ALL ) ) )
|
|
|
|
|
{
|
|
|
|
|
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 ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_mem_write( uc, module_base, reinterpret_cast< void * >( module_base ), image_size ) ) )
|
|
|
|
|
{
|
|
|
|
|
std::printf("failed on uc_mem_write() with error returned %u: %s\n",
|
|
|
|
|
err, uc_strerror(err));
|
|
|
|
|
std::printf( "failed on uc_mem_write() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_reg_write( uc, UC_X86_REG_RIP, &vm_entry ) ) )
|
|
|
|
|
{
|
|
|
|
|
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 ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_reg_write( uc, UC_X86_REG_RSP, &stack_addr ) ) )
|
|
|
|
|
{
|
|
|
|
|
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 ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((err = uc_hook_add(uc, &trace, UC_HOOK_CODE, &vm::emu_t::hook_code,
|
|
|
|
|
this, module_base, module_base + image_size)))
|
|
|
|
|
if ( ( err = uc_hook_add( uc, &trace, UC_HOOK_CODE, &vm::emu_t::hook_code, this, module_base,
|
|
|
|
|
module_base + image_size ) ) )
|
|
|
|
|
{
|
|
|
|
|
std::printf("failed on uc_hook_add() with error returned %u: %s\n",
|
|
|
|
|
err, uc_strerror(err));
|
|
|
|
|
std::printf( "failed on uc_hook_add() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -110,8 +97,7 @@ namespace vm
|
|
|
|
|
if ( ( err = uc_hook_add( uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED,
|
|
|
|
|
vm::emu_t::hook_mem_invalid, this, 1, 0 ) ) )
|
|
|
|
|
{
|
|
|
|
|
std::printf("failed on uc_hook_add() with error returned %u: %s\n",
|
|
|
|
|
err, uc_strerror(err));
|
|
|
|
|
std::printf( "failed on uc_hook_add() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -120,7 +106,8 @@ namespace vm
|
|
|
|
|
|
|
|
|
|
emu_t::~emu_t()
|
|
|
|
|
{
|
|
|
|
|
if (uc) uc_close(uc);
|
|
|
|
|
if ( uc )
|
|
|
|
|
uc_close( uc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool emu_t::get_trace( std::vector< vmp2::v2::entry_t > &entries )
|
|
|
|
@ -131,8 +118,7 @@ namespace vm
|
|
|
|
|
|
|
|
|
|
if ( ( err = uc_emu_start( uc, vm_entry_rva + module_base, NULL, NULL, NULL ) ) )
|
|
|
|
|
{
|
|
|
|
|
std::printf("failed on uc_emu_start() with error returned %u: %s\n",
|
|
|
|
|
err, uc_strerror(err));
|
|
|
|
|
std::printf( "failed on uc_emu_start() with error returned %u: %s\n", err, uc_strerror( err ) );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -163,14 +149,13 @@ namespace vm
|
|
|
|
|
entry->decrypt_key = entry->regs.rbx;
|
|
|
|
|
|
|
|
|
|
uc_err err;
|
|
|
|
|
if ((err = uc_mem_read(uc, entry->regs.rdi,
|
|
|
|
|
entry->vregs.raw, sizeof entry->vregs.raw)))
|
|
|
|
|
if ( ( err = uc_mem_read( uc, entry->regs.rdi, entry->vregs.raw, sizeof entry->vregs.raw ) ) )
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
// copy virtual stack values...
|
|
|
|
|
for ( auto idx = 0u; idx < sizeof( entry->vsp ) / 8; ++idx )
|
|
|
|
|
if ((err = uc_mem_read(uc, entry->regs.rbp + (idx * 8),
|
|
|
|
|
&entry->vsp.qword[idx], sizeof entry->vsp.qword[idx])))
|
|
|
|
|
if ( ( err = uc_mem_read( uc, entry->regs.rbp + ( idx * 8 ), &entry->vsp.qword[ idx ],
|
|
|
|
|
sizeof entry->vsp.qword[ idx ] ) ) )
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
return UC_ERR_OK;
|
|
|
|
@ -181,10 +166,7 @@ namespace vm
|
|
|
|
|
std::printf( ">>> Tracing instruction at 0x%p, instruction size = 0x%x\n", address, size );
|
|
|
|
|
|
|
|
|
|
// 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->vm_entry[ obj->vm_entry.size() ].instr.operands[ 0 ].reg.value;
|
|
|
|
|
|
|
|
|
|
static ZydisDecoder decoder;
|
|
|
|
|
static std::once_flag once;
|
|
|
|
@ -193,8 +175,8 @@ namespace vm
|
|
|
|
|
|
|
|
|
|
// init zydis decoder just a single time...
|
|
|
|
|
std::call_once( once, [ & ]() -> void {
|
|
|
|
|
ZydisDecoderInit(&decoder,
|
|
|
|
|
ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); });
|
|
|
|
|
ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 );
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
// last instruction in vm_entry is jmp rcx/rdx...
|
|
|
|
|
if ( address == obj->vm_entry[ obj->vm_entry.size() - 1 ].addr )
|
|
|
|
@ -203,18 +185,16 @@ namespace vm
|
|
|
|
|
vmp2::v2::entry_t new_entry;
|
|
|
|
|
if ( ( err = obj->create_entry( &new_entry ) ) )
|
|
|
|
|
{
|
|
|
|
|
std::printf("[!] failed to create new entry... reason = %u, %s\n",
|
|
|
|
|
err, uc_strerror(err));
|
|
|
|
|
std::printf( "[!] failed to create new entry... reason = %u, %s\n", err, uc_strerror( err ) );
|
|
|
|
|
|
|
|
|
|
exit( 0 );
|
|
|
|
|
}
|
|
|
|
|
obj->trace_entries->push_back( new_entry );
|
|
|
|
|
}
|
|
|
|
|
// if we are getting a callback for a JMP RCX/RDX instruction...
|
|
|
|
|
else if (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(
|
|
|
|
|
&decoder, reinterpret_cast<void*>(address), size, &instr)) &&
|
|
|
|
|
instr.mnemonic == ZYDIS_MNEMONIC_JMP &&
|
|
|
|
|
instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
|
|
|
|
else if ( ZYAN_SUCCESS(
|
|
|
|
|
ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( address ), size, &instr ) ) &&
|
|
|
|
|
instr.mnemonic == ZYDIS_MNEMONIC_JMP && instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
|
|
|
|
instr.operands[ 0 ].reg.value == jmp_reg )
|
|
|
|
|
{
|
|
|
|
|
switch ( jmp_reg )
|
|
|
|
@ -232,20 +212,19 @@ namespace vm
|
|
|
|
|
|
|
|
|
|
// checks to see if the address
|
|
|
|
|
// in JMP RDX/RCX is a vm handler address...
|
|
|
|
|
static const auto vm_handler_check =
|
|
|
|
|
[&](const vm::handler::handler_t& vm_handler) -> bool
|
|
|
|
|
{ return vm_handler.address == reg_val; };
|
|
|
|
|
static const auto vm_handler_check = [ & ]( const vm::handler::handler_t &vm_handler ) -> bool {
|
|
|
|
|
return vm_handler.address == reg_val;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (std::find_if(obj->vm_handlers.begin(), obj->vm_handlers.end(),
|
|
|
|
|
vm_handler_check) == obj->vm_handlers.end())
|
|
|
|
|
if ( std::find_if( obj->vm_handlers.begin(), obj->vm_handlers.end(), vm_handler_check ) ==
|
|
|
|
|
obj->vm_handlers.end() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
uc_err err;
|
|
|
|
|
vmp2::v2::entry_t new_entry;
|
|
|
|
|
if ( ( err = obj->create_entry( &new_entry ) ) )
|
|
|
|
|
{
|
|
|
|
|
std::printf("[!] failed to create new entry... reason = %u, %s\n",
|
|
|
|
|
err, uc_strerror(err));
|
|
|
|
|
std::printf( "[!] failed to create new entry... reason = %u, %s\n", err, uc_strerror( err ) );
|
|
|
|
|
|
|
|
|
|
exit( 0 );
|
|
|
|
|
}
|
|
|
|
@ -255,28 +234,26 @@ namespace vm
|
|
|
|
|
uc_emu_stop( uc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool emu_t::hook_mem_invalid(uc_engine* uc, uc_mem_type type,
|
|
|
|
|
uint64_t address, int size, int64_t value, vm::emu_t* obj)
|
|
|
|
|
bool emu_t::hook_mem_invalid( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value,
|
|
|
|
|
vm::emu_t *obj )
|
|
|
|
|
{
|
|
|
|
|
uc_err err;
|
|
|
|
|
if ( ( err = uc_mem_map( obj->uc, address & ~0xFFFull, 0x1000, UC_PROT_ALL ) ) )
|
|
|
|
|
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 ) );
|
|
|
|
|
|
|
|
|
|
switch ( type )
|
|
|
|
|
{
|
|
|
|
|
case UC_MEM_WRITE_UNMAPPED:
|
|
|
|
|
printf(">>> Missing memory is being WRITE at 0x%p, data size = %u, data value = 0x%p\n",
|
|
|
|
|
address, size, value);
|
|
|
|
|
printf( ">>> Missing memory is being WRITE at 0x%p, data size = %u, data value = 0x%p\n", address, size,
|
|
|
|
|
value );
|
|
|
|
|
return true;
|
|
|
|
|
case UC_MEM_READ_UNMAPPED:
|
|
|
|
|
printf(">>> Missing memory is being READ at 0x%p, data size = %u, data value = 0x%p\n",
|
|
|
|
|
address, size, value);
|
|
|
|
|
printf( ">>> Missing memory is being READ at 0x%p, data size = %u, data value = 0x%p\n", address, size,
|
|
|
|
|
value );
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
printf(">>> Missing memory at 0x%p, data size = %u, data value = 0x%p\n",
|
|
|
|
|
address, size, value);
|
|
|
|
|
printf( ">>> Missing memory at 0x%p, data size = %u, data value = 0x%p\n", address, size, value );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // namespace vm
|