@ -1,83 +1,77 @@
# include "vmemu_t.hpp"
namespace vm
{
namespace vm {
emu_t : : emu_t ( vm : : ctx_t * vm_ctx )
: g_vm_ctx ( vm_ctx ) , uc_ctx ( nullptr ) , img_base ( vm_ctx - > image_base ) , img_size ( vm_ctx - > image_size )
{
}
: g_vm_ctx ( vm_ctx ) ,
uc_ctx ( nullptr ) ,
img_base ( vm_ctx - > image_base ) ,
img_size ( vm_ctx - > image_size ) { }
emu_t : : ~ emu_t ( )
{
if ( uc_ctx )
uc_close ( uc_ctx ) ;
emu_t : : ~ emu_t ( ) {
if ( uc_ctx ) uc_close ( uc_ctx ) ;
}
bool emu_t : : init ( )
{
bool emu_t : : init ( ) {
uc_err err ;
if ( ( err = uc_open ( UC_ARCH_X86 , UC_MODE_64 , & uc_ctx ) ) )
{
if ( ( err = uc_open ( UC_ARCH_X86 , UC_MODE_64 , & uc_ctx ) ) ) {
std : : printf ( " > uc_open err = %d \n " , err ) ;
return false ;
}
if ( ( err = uc_mem_map ( uc_ctx , STACK_BASE , STACK_SIZE , UC_PROT_ALL ) ) )
{
if ( ( err = uc_mem_map ( uc_ctx , STACK_BASE , STACK_SIZE , UC_PROT_ALL ) ) ) {
std : : printf ( " > uc_mem_map stack err, reason = %d \n " , err ) ;
return false ;
}
if ( ( err = uc_mem_map ( uc_ctx , g_vm_ctx - > module_base , img_size , UC_PROT_ALL ) ) )
{
if ( ( err =
uc_mem_map ( uc_ctx , g_vm_ctx - > module_base , img_size , UC_PROT_ALL ) ) ) {
std : : printf ( " > map memory failed, reason = %d \n " , err ) ;
return false ;
}
if ( ( err = uc_mem_write ( uc_ctx , g_vm_ctx - > module_base , reinterpret_cast < void * > ( g_vm_ctx - > module_base ) ,
img_size ) ) )
{
if ( ( err = uc_mem_write ( uc_ctx , g_vm_ctx - > module_base ,
reinterpret_cast < void * > ( g_vm_ctx - > module_base ) ,
img_size ) ) ) {
std : : printf ( " > failed to write memory... reason = %d \n " , err ) ;
return false ;
}
if ( ( err = uc_hook_add ( uc_ctx , & code_exec_hook , UC_HOOK_CODE , & vm : : emu_t : : code_exec_callback , this ,
g_vm_ctx - > module_base , g_vm_ctx - > module_base + img_size ) ) )
{
if ( ( err = uc_hook_add ( uc_ctx , & code_exec_hook , UC_HOOK_CODE ,
( void * ) & vm : : emu_t : : code_exec_callback , this ,
g_vm_ctx - > module_base ,
g_vm_ctx - > module_base + img_size ) ) ) {
std : : printf ( " > uc_hook_add error, reason = %d \n " , err ) ;
return false ;
}
if ( ( err = uc_hook_add ( uc_ctx , & int_hook , UC_HOOK_INTR , & vm : : emu_t : : int_callback , this , 0ull , 0ull ) ) )
{
if ( ( err = uc_hook_add ( uc_ctx , & int_hook , UC_HOOK_INTR ,
( void * ) & 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 ,
& vm : : emu_t : : invalid_mem , this , true , 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 ,
( void * ) & vm : : emu_t : : invalid_mem , this , true , false ) ) ) {
std : : printf ( " > uc_hook_add error, reason = %d \n " , err ) ;
return false ;
}
return true ;
}
bool emu_t : : get_trace ( std : : vector < vm : : instrs : : code_block_t > & entries )
{
bool emu_t : : get_trace ( std : : vector < vm : : instrs : : code_block_t > & entries ) {
uc_err err ;
std : : uintptr_t rip = g_vm_ctx - > vm_entry_rva + g_vm_ctx - > module_base , rsp = STACK_BASE + STACK_SIZE - PAGE_4KB ;
std : : uintptr_t rip = g_vm_ctx - > vm_entry_rva + g_vm_ctx - > module_base ,
rsp = STACK_BASE + STACK_SIZE - PAGE_4KB ;
if ( ( err = uc_reg_write ( uc_ctx , UC_X86_REG_RSP , & rsp ) ) )
{
if ( ( err = uc_reg_write ( uc_ctx , UC_X86_REG_RSP , & rsp ) ) ) {
std : : printf ( " > uc_reg_write error, reason = %d \n " , err ) ;
return false ;
}
if ( ( err = uc_reg_write ( uc_ctx , UC_X86_REG_RIP , & rip ) ) )
{
if ( ( err = uc_reg_write ( uc_ctx , UC_X86_REG_RIP , & rip ) ) ) {
std : : printf ( " > uc_reg_write error, reason = %d \n " , err ) ;
return false ;
}
@ -87,33 +81,28 @@ namespace vm
cc_block = & code_block ;
std : : printf ( " > beginning execution at = 0x%p \n " , rip ) ;
if ( ( err = uc_emu_start ( uc_ctx , rip , 0ull , 0ull , 0ull ) ) )
{
if ( ( err = uc_emu_start ( uc_ctx , rip , 0ull , 0ull , 0ull ) ) ) {
std : : printf ( " > error starting emu... reason = %d \n " , err ) ;
return false ;
}
if ( cc_block )
code_blocks . push_back ( code_block ) ;
if ( cc_block ) code_blocks . push_back ( code_block ) ;
// code_blocks.size() will continue to grow as all branches are traced...
// when idx is > code_blocks.size() then we have traced all branches...
for ( auto idx = 0u ; idx < code_blocks . size ( ) ; + + idx )
{
for ( auto idx = 0u ; idx < code_blocks . size ( ) ; + + idx ) {
const auto _code_block = code_blocks [ idx ] ;
if ( ! _code_block . code_block . jcc . has_jcc )
continue ;
if ( ! _code_block . code_block . jcc . has_jcc ) continue ;
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 ] ) = =
vip_begins . end ( ) )
{
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 ] ) = =
vip_begins . end ( ) ) {
std : : uintptr_t rbp = 0ull ;
std : : uint32_t branch_rva =
( _code_block . code_block . jcc . block_addr [ 1 ] - g_vm_ctx - > module_base ) + g_vm_ctx - > image_base ;
std : : uint32_t branch_rva = ( _code_block . code_block . jcc . block_addr [ 1 ] -
g_vm_ctx - > module_base ) +
g_vm_ctx - > image_base ;
// setup object globals so that the tracing will work...
code_block_data_t branch_block { { } , nullptr , nullptr } ;
@ -121,36 +110,37 @@ namespace vm
g_vm_ctx = _code_block . g_vm_ctx . get ( ) ;
// restore register values...
if ( ( err = uc_context_restore ( uc_ctx , _code_block . cpu_ctx - > context ) ) )
{
std : : printf ( " > failed to restore emu context... reason = %d \n " , err ) ;
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 ;
}
// get the address in rbp (top of vsp)... then patch the branch rva...
if ( ( err = uc_reg_read ( uc_ctx , UC_X86_REG_RBP , & rbp ) ) )
{
if ( ( err = uc_reg_read ( uc_ctx , UC_X86_REG_RBP , & rbp ) ) ) {
std : : printf ( " > failed to read rbp... reason = %d \n " , err ) ;
return false ;
}
// patch the branch rva...
if ( ( err = uc_mem_write ( uc_ctx , rbp , & branch_rva , sizeof branch_rva ) ) )
{
if ( ( err =
uc_mem_write ( uc_ctx , rbp , & branch_rva , sizeof branch_rva ) ) ) {
std : : printf ( " > failed to patch branch rva... reason = %d \n " , err ) ;
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 ;
}
@ -162,15 +152,14 @@ namespace vm
// drop down and execute the absolute case as well since that
// will trace the first branch...
}
case vm : : instrs : : jcc_type : : absolute :
{
if ( std : : find ( vip_begins . begin ( ) , vip_begins . end ( ) , _code_block . code_block . jcc . block_addr [ 0 ] ) = =
vip_begins . end ( ) )
{
case vm : : instrs : : jcc_type : : absolute : {
if ( std : : find ( vip_begins . begin ( ) , vip_begins . end ( ) ,
_code_block . code_block . jcc . block_addr [ 0 ] ) = =
vip_begins . end ( ) ) {
std : : uintptr_t rbp = 0ull ;
std : : uint32_t branch_rva =
( _code_block . code_block . jcc . block_addr [ 0 ] - g_vm_ctx - > module_base ) + g_vm_ctx - > image_base ;
std : : uint32_t branch_rva = ( _code_block . code_block . jcc . block_addr [ 0 ] -
g_vm_ctx - > module_base ) +
g_vm_ctx - > image_base ;
// setup object globals so that the tracing will work...
code_block_data_t branch_block { { } , nullptr , nullptr } ;
@ -178,36 +167,37 @@ namespace vm
g_vm_ctx = _code_block . g_vm_ctx . get ( ) ;
// restore register values...
if ( ( err = uc_context_restore ( uc_ctx , _code_block . cpu_ctx - > context ) ) )
{
std : : printf ( " > failed to restore emu context... reason = %d \n " , err ) ;
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 ;
}
// get the address in rbp (top of vsp)... then patch the branch rva...
if ( ( err = uc_reg_read ( uc_ctx , UC_X86_REG_RBP , & rbp ) ) )
{
if ( ( err = uc_reg_read ( uc_ctx , UC_X86_REG_RBP , & rbp ) ) ) {
std : : printf ( " > failed to read rbp... reason = %d \n " , err ) ;
return false ;
}
// patch the branch rva...
if ( ( err = uc_mem_write ( uc_ctx , rbp , & branch_rva , sizeof branch_rva ) ) )
{
if ( ( err =
uc_mem_write ( uc_ctx , rbp , & branch_rva , sizeof branch_rva ) ) ) {
std : : printf ( " > failed to patch branch rva... reason = %d \n " , err ) ;
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 ;
}
@ -218,17 +208,18 @@ namespace vm
}
break ;
}
case vm : : instrs : : jcc_type : : switch_case :
{
for ( auto _idx = 0u ; _idx < _code_block . code_block . jcc . block_addr . size ( ) ; + + _idx )
{
case vm : : instrs : : jcc_type : : switch_case : {
for ( auto _idx = 0u ;
_idx < _code_block . code_block . jcc . block_addr . size ( ) ; + + _idx ) {
if ( std : : find ( vip_begins . begin ( ) , vip_begins . end ( ) ,
_code_block . code_block . jcc . block_addr [ _idx ] ) ! = vip_begins . end ( ) )
_code_block . code_block . jcc . block_addr [ _idx ] ) ! =
vip_begins . end ( ) )
continue ;
std : : uintptr_t rbp = 0ull ;
std : : uint32_t branch_rva =
( _code_block . code_block . jcc . block_addr [ _idx ] - g_vm_ctx - > module_base ) +
( _code_block . code_block . jcc . block_addr [ _idx ] -
g_vm_ctx - > module_base ) +
g_vm_ctx - > image_base ;
// setup object globals so that the tracing will work...
@ -237,36 +228,37 @@ namespace vm
g_vm_ctx = _code_block . g_vm_ctx . get ( ) ;
// restore register values...
if ( ( err = uc_context_restore ( uc_ctx , _code_block . cpu_ctx - > context ) ) )
{
std : : printf ( " > failed to restore emu context... reason = %d \n " , err ) ;
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 ;
}
// get the address in rbp (top of vsp)... then patch the branch rva...
if ( ( err = uc_reg_read ( uc_ctx , UC_X86_REG_RBP , & rbp ) ) )
{
if ( ( err = uc_reg_read ( uc_ctx , UC_X86_REG_RBP , & rbp ) ) ) {
std : : printf ( " > failed to read rbp... reason = %d \n " , err ) ;
return false ;
}
// patch the branch rva...
if ( ( err = uc_mem_write ( uc_ctx , rbp , & branch_rva , sizeof branch_rva ) ) )
{
if ( ( err =
uc_mem_write ( uc_ctx , rbp , & branch_rva , sizeof branch_rva ) ) ) {
std : : printf ( " > failed to patch branch rva... reason = %d \n " , err ) ;
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 ;
}
@ -280,30 +272,28 @@ namespace vm
}
}
for ( auto & [ code_block , cpu_ctx , vm_ctx ] : code_blocks )
{
for ( auto & [ code_block , cpu_ctx , vm_ctx ] : code_blocks ) {
// convert linear virtual addresses to image based addresses...
code_block . vip_begin = ( code_block . vip_begin - g_vm_ctx - > module_base ) + g_vm_ctx - > image_base ;
if ( code_block . jcc . has_jcc )
{
switch ( code_block . jcc . type )
{
case vm : : instrs : : jcc_type : : branching :
{
code_block . vip_begin =
( code_block . vip_begin - g_vm_ctx - > module_base ) + g_vm_ctx - > image_base ;
if ( code_block . jcc . has_jcc ) {
switch ( code_block . jcc . type ) {
case vm : : instrs : : jcc_type : : branching : {
code_block . jcc . block_addr [ 1 ] =
( code_block . jcc . block_addr [ 1 ] - g_vm_ctx - > module_base ) + g_vm_ctx - > image_base ;
( code_block . jcc . block_addr [ 1 ] - g_vm_ctx - > module_base ) +
g_vm_ctx - > image_base ;
}
case vm : : instrs : : jcc_type : : absolute :
{
case vm : : instrs : : jcc_type : : absolute : {
code_block . jcc . block_addr [ 0 ] =
( code_block . jcc . block_addr [ 0 ] - g_vm_ctx - > module_base ) + g_vm_ctx - > image_base ;
( code_block . jcc . block_addr [ 0 ] - g_vm_ctx - > module_base ) +
g_vm_ctx - > image_base ;
break ;
}
case vm : : instrs : : jcc_type : : switch_case :
{
case vm : : instrs : : jcc_type : : switch_case : {
for ( auto idx = 0u ; idx < code_block . jcc . block_addr . size ( ) ; + + idx )
code_block . jcc . block_addr [ idx ] =
( code_block . jcc . block_addr [ idx ] - g_vm_ctx - > module_base ) + g_vm_ctx - > image_base ;
( code_block . jcc . block_addr [ idx ] - g_vm_ctx - > module_base ) +
g_vm_ctx - > image_base ;
break ;
}
}
@ -314,8 +304,7 @@ namespace vm
return true ;
}
uc_err emu_t : : create_entry ( vmp2 : : v2 : : entry_t * entry )
{
uc_err emu_t : : create_entry ( vmp2 : : v2 : : entry_t * entry ) {
uc_reg_read ( uc_ctx , UC_X86_REG_R15 , & entry - > regs . r15 ) ;
uc_reg_read ( uc_ctx , UC_X86_REG_R14 , & entry - > regs . r14 ) ;
uc_reg_read ( uc_ctx , UC_X86_REG_R13 , & entry - > regs . r13 ) ;
@ -338,20 +327,22 @@ namespace vm
entry - > decrypt_key = entry - > regs . rbx ;
uc_err err ;
if ( ( err = uc_mem_read ( uc_ctx , entry - > regs . rdi , entry - > vregs . raw , sizeof entry - > vregs . raw ) ) )
if ( ( err = uc_mem_read ( uc_ctx , 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_ctx , entry - > regs . rbp + ( idx * 8 ) , & entry - > vsp . qword [ idx ] ,
sizeof entry - > vsp . qword [ idx ] ) ) )
if ( ( err =
uc_mem_read ( uc_ctx , entry - > regs . rbp + ( idx * 8 ) ,
& entry - > vsp . qword [ idx ] , sizeof entry - > vsp . qword [ idx ] ) ) )
return err ;
return UC_ERR_OK ;
}
bool emu_t : : code_exec_callback ( uc_engine * uc , uint64_t address , uint32_t size , emu_t * obj )
{
bool emu_t : : code_exec_callback ( uc_engine * uc , uint64_t address , uint32_t size ,
emu_t * obj ) {
uc_err err ;
vmp2 : : v2 : : entry_t vinstr_entry ;
std : : uint8_t vm_handler_table_idx = 0u ;
@ -365,44 +356,42 @@ namespace vm
static ZydisFormatter formatter ;
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 ( static std : : atomic < bool > once { false } ; ! once . exchange ( true ) ) {
ZydisDecoderInit ( & decoder , ZYDIS_MACHINE_MODE_LONG_64 ,
ZYDIS_ADDRESS_WIDTH_64 ) ;
ZydisFormatterInit ( & formatter , ZYDIS_FORMATTER_STYLE_INTEL ) ;
}
if ( ! ZYAN_SUCCESS (
ZydisDecoderDecodeBuffer ( & decoder , reinterpret_cast < void * > ( address ) , PAGE_4KB , & instr ) ) )
{
if ( ! ZYAN_SUCCESS ( 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 ) ) )
{
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
exit ( 0 ) ;
}
return false ;
}
if ( instr . mnemonic = = ZYDIS_MNEMONIC_INVALID )
{
if ( instr . mnemonic = = ZYDIS_MNEMONIC_INVALID ) {
obj - > cc_block = nullptr ;
uc_emu_stop ( uc ) ;
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 )
{
// 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 & &
// 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 & &
( instr . operands [ 0 ] . reg . value = = ZYDIS_REGISTER_RCX | |
instr . operands [ 0 ] . reg . value = = ZYDIS_REGISTER_RDX ) ) )
return true ;
@ -411,27 +400,24 @@ namespace vm
inst_cnt = 0ull ;
// extract address of vm handler table...
switch ( instr . operands [ 0 ] . reg . value )
{
switch ( instr . operands [ 0 ] . reg . value ) {
case ZYDIS_REGISTER_RCX :
if ( ( err = uc_reg_read ( uc , UC_X86_REG_RCX , & vm_handler_addr ) ) )
{
if ( ( err = uc_reg_read ( uc , UC_X86_REG_RCX , & vm_handler_addr ) ) ) {
std : : printf ( " > failed to read rcx... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " ,
err ) ;
exit ( 0 ) ;
}
return false ;
}
break ;
case ZYDIS_REGISTER_RDX :
if ( ( err = uc_reg_read ( uc , UC_X86_REG_RDX , & vm_handler_addr ) ) )
{
if ( ( err = uc_reg_read ( uc , UC_X86_REG_RDX , & vm_handler_addr ) ) ) {
std : : printf ( " > failed to read rdx... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " ,
err ) ;
exit ( 0 ) ;
}
return false ;
@ -439,11 +425,9 @@ namespace vm
break ;
}
if ( ( err = uc_reg_read ( obj - > uc_ctx , UC_X86_REG_AL , & vm_handler_table_idx ) ) )
{
if ( ( err = uc_reg_read ( obj - > uc_ctx , UC_X86_REG_AL , & vm_handler_table_idx ) ) ) {
std : : printf ( " > failed to read register... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
exit ( 0 ) ;
}
@ -452,28 +436,27 @@ namespace vm
auto & vm_handler = obj - > g_vm_ctx - > vm_handlers [ vm_handler_table_idx ] ;
if ( ( err = obj - > create_entry ( & vinstr_entry ) ) )
{
if ( ( err = obj - > create_entry ( & vinstr_entry ) ) ) {
std : : printf ( " > failed to create vinstr entry... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
exit ( 0 ) ;
}
return false ;
}
// quick check to ensure sanity... things can get crazy so this is good to check...
// quick check to ensure sanity... things can get crazy so this is good to
// check...
if ( vm_handler . address ! = vm_handler_addr | |
vinstr_entry . vip > = obj - > g_vm_ctx - > module_base + obj - > g_vm_ctx - > image_size | |
vinstr_entry . vip < obj - > g_vm_ctx - > module_base )
{
std : : printf ( " > vm handler index (%d) does not match vm handler address (%p)... \n " , vm_handler_table_idx ,
vm_handler_addr ) ;
vinstr_entry . vip > =
obj - > g_vm_ctx - > module_base + obj - > g_vm_ctx - > image_size | |
vinstr_entry . vip < obj - > g_vm_ctx - > module_base ) {
std : : printf (
" > vm handler index (%d) does not match vm handler address (%p)... \n " ,
vm_handler_table_idx , vm_handler_addr ) ;
obj - > cc_block = nullptr ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
exit ( 0 ) ;
}
@ -481,28 +464,24 @@ namespace vm
return false ;
}
if ( ! vm_handler . profile )
{
if ( ! g_force_emu )
obj - > cc_block = nullptr ;
if ( ! vm_handler . profile ) {
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_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 ( ! g_force_emu )
exit ( 0 ) ;
if ( ! g_force_emu ) exit ( 0 ) ;
}
auto vinstr = vm : : instrs : : get ( * obj - > g_vm_ctx , vinstr_entry ) ;
if ( ! vinstr . has_value ( ) )
{
if ( ! vinstr . has_value ( ) ) {
std : : printf ( " > failed to decode virtual instruction... \n " ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
exit ( 0 ) ;
}
@ -510,57 +489,58 @@ namespace vm
}
// log this virtual blocks vip_begin...
if ( obj - > cc_block - > code_block . vinstrs . empty ( ) )
{
if ( obj - > cc_block - > code_block . vinstrs . empty ( ) ) {
obj - > cc_block - > code_block . vip_begin =
obj - > g_vm_ctx - > exec_type = = vmp2 : : exec_type_t : : forward ? vinstr_entry . vip - 1 : vinstr_entry . vip + 1 ;
obj - > g_vm_ctx - > exec_type = = vmp2 : : exec_type_t : : forward
? vinstr_entry . vip - 1
: vinstr_entry . vip + 1 ;
obj - > vip_begins . push_back ( obj - > cc_block - > code_block . vip_begin ) ;
}
vinstr . value ( ) . trace_data . vm_handler_rva = ( vm_handler_addr - obj - > g_vm_ctx - > module_base ) ;
vinstr . value ( ) . trace_data . vm_handler_rva =
( vm_handler_addr - obj - > g_vm_ctx - > module_base ) ;
obj - > cc_block - > code_block . vinstrs . push_back ( vinstr . value ( ) ) ;
if ( vm_handler . profile )
{
switch ( vm_handler . profile - > mnemonic )
{
case vm : : handler : : VMEXIT :
{
if ( vm_handler . profile ) {
switch ( vm_handler . profile - > mnemonic ) {
case vm : : handler : : VMEXIT : {
obj - > cc_block - > code_block . jcc . has_jcc = false ;
obj - > cc_block - > code_block . jcc . type = vm : : instrs : : jcc_type : : none ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " ,
err ) ;
exit ( 0 ) ;
}
break ;
}
case vm : : handler : : JMP :
{
// get jcc data about the virtual instruction code block that was just emulated...
auto jcc_data = vm : : instrs : : get_jcc_data ( * obj - > g_vm_ctx , obj - > cc_block - > code_block ) ;
case vm : : handler : : JMP : {
// get jcc data about the virtual instruction code block that was just
// emulated...
auto jcc_data =
vm : : instrs : : get_jcc_data ( * obj - > g_vm_ctx , obj - > cc_block - > code_block ) ;
obj - > cc_block - > code_block . jcc = jcc_data . value ( ) ;
// allocate space for the cpu context and stack...
auto new_cpu_ctx = std : : make_shared < vm : : emu_t : : cpu_ctx_t > ( ) ;
// 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 ,
// 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 - > g_vm_ctx - > module_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 " ,
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 ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf (
" > failed to stop emulation, exiting... reason = %d \n " , err ) ;
exit ( 0 ) ;
}
return false ;
@ -568,12 +548,12 @@ namespace vm
}
_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 ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
std: : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_context_alloc ( uc , & new_cpu_ctx - > context ) ) ) {
std : : printf ( " > failed to allocate a unicorn context... reason = %d \n " ,
err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " ,
err) ;
exit ( 0 ) ;
}
return false ;
@ -581,32 +561,32 @@ namespace vm
// save the cpu's registers...
new_cpu_ctx - > rip = vm_handler_addr ;
if ( ( err = uc_context_save ( uc , new_cpu_ctx - > context ) ) )
{
std : : printf ( " > failed to save emulator context... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
std: : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_context_save ( uc , new_cpu_ctx - > context ) ) ) {
std : : printf ( " > failed to save emulator context... reason = %d \n " ,
err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " ,
err) ;
exit ( 0 ) ;
}
return false ;
}
// save the entire stack...
if ( ( err = uc_mem_read ( uc , STACK_BASE , new_cpu_ctx - > stack , STACK_SIZE ) ) )
{
if ( ( err =
uc_mem_read ( uc , STACK_BASE , new_cpu_ctx - > stack , STACK_SIZE ) ) ) {
std : : printf ( " > failed to read stack... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) )
{
std: : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " ,
err) ;
exit ( 0 ) ;
}
return false ;
}
if ( ( err = uc_emu_stop ( uc ) ) )
{
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " ,
err ) ;
exit ( 0 ) ;
}
@ -621,29 +601,26 @@ namespace vm
return true ;
}
void emu_t : : int_callback ( uc_engine * uc , std : : uint32_t intno , emu_t * obj )
{
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 ) ;
ZydisDecoderInit ( & decoder , ZYDIS_MACHINE_MODE_LONG_64 ,
ZYDIS_ADDRESS_WIDTH_64 ) ;
if ( ( err = uc_reg_read ( uc , UC_X86_REG_RIP , & rip ) ) )
{
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 ) ) )
{
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 ) ) )
{
if ( ( err = uc_emu_stop ( uc ) ) ) {
std : : printf ( " > failed to stop emulation, exiting... reason = %d \n " , err ) ;
exit ( 0 ) ;
}
@ -653,33 +630,32 @@ namespace vm
// advance rip over the instruction that caused the exception...
rip + = instr . length ;
if ( ( err = uc_reg_write ( uc , UC_X86_REG_RIP , & rip ) ) )
{
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 )
{
case UC_MEM_READ_UNMAPPED :
{
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 ) {
case UC_MEM_READ_UNMAPPED : {
uc_mem_map ( uc , address & ~ 0xFFFull , PAGE_4KB , UC_PROT_ALL ) ;
std : : printf ( " >>> reading invalid memory at address = 0x%p, size = 0x%x \n " , address , size ) ;
std : : printf ( " >>> reading invalid memory at address = 0x%p, size = 0x%x \n " ,
address , size ) ;
break ;
}
case UC_MEM_WRITE_UNMAPPED :
{
case UC_MEM_WRITE_UNMAPPED : {
uc_mem_map ( uc , address & ~ 0xFFFull , PAGE_4KB , UC_PROT_ALL ) ;
std : : printf ( " >>> writing invalid memory at address = 0x%p, size = 0x%x, val = 0x%x \n " , address , size ,
value ) ;
std : : printf (
" >>> writing invalid memory at address = 0x%p, size = 0x%x, val = "
" 0x%x \n " ,
address , size , value ) ;
break ;
}
case UC_MEM_FETCH_UNMAPPED :
{
std : : printf ( " >>> fetching invalid instructions at address = 0x%p\n " , address ) ;
case UC_MEM_FETCH_UNMAPPED : {
std : : printf ( " >>> fetching invalid instructions at address = 0x%p \n " ,
address ) ;
break ;
}
default :