virtual jcc's are working, probably lots of bugs :bugs:

merge-requests/4/head
_xeroxz 3 years ago
parent 97a0bb5f26
commit eec49e9bb0

@ -1 +1 @@
Subproject commit cf403125643ac1e23391a36a3d8f484b33546a1f Subproject commit 75ac25445644c4a6dbc1c9995bc3e571a5e1ceca

@ -72,7 +72,7 @@ int __cdecl main( int argc, const char *argv[] )
std::printf( "> does this code block have a jcc? %s\n", code_block.jcc.has_jcc ? "yes" : "no" ); std::printf( "> does this code block have a jcc? %s\n", code_block.jcc.has_jcc ? "yes" : "no" );
if ( code_block.jcc.has_jcc ) if ( code_block.jcc.has_jcc )
std::printf( "> branch 1 = %p, branch 2 = %p\n", code_block.jcc.block_rva[ 0 ], std::printf( "> branch 1 = %p, branch 2 = %p\n", code_block.jcc.block_addr[ 0 ],
code_block.jcc.block_rva[ 1 ] ); code_block.jcc.block_addr[ 1 ] );
} }
} }

@ -2,7 +2,7 @@
namespace vm namespace vm
{ {
emu_t::emu_t( vm::ctx_t *vmctx ) : uc( nullptr ), code_blocks( nullptr ), vmctx( vmctx ) emu_t::emu_t( vm::ctx_t *vmctx ) : uc( nullptr ), vmctx( vmctx )
{ {
} }
@ -28,7 +28,7 @@ namespace vm
return false; return false;
} }
if ( ( err = uc_mem_map( uc, 0x1000000, 0x1000 * 20, UC_PROT_ALL ) ) ) if ( ( err = uc_mem_map( uc, UC_STACK_ADDR, sizeof vm::cpu_ctx::stack, 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 ) );
@ -83,16 +83,68 @@ namespace vm
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 )
{ {
// hook_code will fill this vector up with values...
code_blocks = &entries;
uc_err err; uc_err err;
code_blocks.push_back( { vm::instrs::code_block_t{ 0u }, {} } );
if ( ( err = uc_emu_start( uc, vmctx->vm_entry_rva + vmctx->module_base, NULL, NULL, NULL ) ) ) if ( ( err = uc_emu_start( uc, vmctx->vm_entry_rva + vmctx->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; return false;
} }
static const auto _already_traced = [ & ]( std::uintptr_t code_block_addr ) -> bool {
return std::find_if( code_blocks.begin(), code_blocks.end(), [ & ]( const auto code_block_data ) -> bool {
return code_block_data.first.vip_begin == code_block_addr;
} ) != code_blocks.end();
};
static const auto _trace_branch = [ & ]( vm::instrs::code_block_t &code_block,
std::shared_ptr< cpu_ctx > &context ) -> bool {
if ( !context )
return {};
// restore context to virtual jmp... changing branch...
uc_context_restore( uc, context->context );
// restore entire stack....
uc_mem_write( uc, UC_STACK_ADDR, context->stack, sizeof vm::cpu_ctx::stack );
std::uintptr_t rip = 0u;
uc_reg_read( uc, UC_X86_REG_RIP, &rip );
// change the top qword on the stack to the branch rva...
// the rva is image base'ed and only the bottom 32bits...
std::uintptr_t branch_rva =
( ( code_block.jcc.block_addr[ 0 ] - vmctx->module_base ) + vmctx->image_base ) & 0xFFFFFFFFull;
uc_mem_write( uc, code_block.vinstrs.back().trace_data.regs.rbp, &branch_rva, sizeof branch_rva );
code_blocks.push_back( { vm::instrs::code_block_t{ 0u }, {} } );
skip_current_jmp = true;
if ( ( err = uc_emu_start( uc, rip, NULL, NULL, NULL ) ) )
{
std::printf( "failed on uc_emu_start() with error returned %u: %s\n", err, uc_strerror( err ) );
return false;
}
};
for ( auto &[ code_block, uc_code_block_context ] : code_blocks )
{
if ( code_block.jcc.has_jcc )
{
if ( !_already_traced( code_block.jcc.block_addr[ 0 ] ) )
_trace_branch( code_block, uc_code_block_context );
if ( !_already_traced( code_block.jcc.block_addr[ 1 ] ) )
_trace_branch( code_block, uc_code_block_context );
}
}
for ( auto &[ code_block, uc_code_block_context ] : code_blocks )
entries.push_back( code_block );
return true; return true;
} }
@ -136,6 +188,14 @@ 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 );
// bad code... but i need to skip JMP instructions when tracing branches since i save context
// on the jmp instruction... so it needs to be skipped...
if ( obj->skip_current_jmp )
{
obj->skip_current_jmp = false;
return;
}
// grab JMP RDX/RCX <-- this register... // grab JMP RDX/RCX <-- this register...
static const auto jmp_reg = obj->vmctx->vm_entry[ obj->vmctx->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;
@ -187,10 +247,9 @@ namespace vm
exit( 0 ); exit( 0 );
} }
// the first virtual instruction we are going to create the first code_block_t... if ( !obj->code_blocks.back().first.vip_begin )
if ( static std::atomic< bool > once = true; once.exchange( false ) ) // -1 because the first byte is the opcode...
if ( obj->code_blocks->empty() ) obj->code_blocks.back().first.vip_begin = new_entry.vip - 1;
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() ) if ( virt_instr = vm::instrs::get( *obj->vmctx, new_entry ); !virt_instr.has_value() )
{ {
@ -199,7 +258,7 @@ namespace vm
exit( 0 ); exit( 0 );
} }
obj->code_blocks->back().vinstrs.push_back( virt_instr.value() ); obj->code_blocks.back().first.vinstrs.push_back( virt_instr.value() );
// if there is a virtual JMP instruction then we need to grab jcc data for the current code_block_t // if there is a virtual JMP instruction then we need to grab jcc data for the current code_block_t
// and then create a new code_block_t... // and then create a new code_block_t...
@ -207,15 +266,38 @@ namespace vm
vm_handler_profile->mnemonic == vm::handler::mnemonic_t::JMP ) vm_handler_profile->mnemonic == vm::handler::mnemonic_t::JMP )
{ {
const auto code_block_address = vm::instrs::code_block_addr( *obj->vmctx, new_entry ); const auto code_block_address = vm::instrs::code_block_addr( *obj->vmctx, new_entry );
auto jcc = vm::instrs::get_jcc_data( *obj->vmctx, obj->code_blocks->back() ); auto jcc = vm::instrs::get_jcc_data( *obj->vmctx, obj->code_blocks.back().first );
if ( jcc.has_value() ) if ( jcc.has_value() )
obj->code_blocks->back().jcc = jcc.value(); obj->code_blocks.back().first.jcc = jcc.value();
// save cpu state as well as stack...
obj->code_blocks.back().second = std::make_shared< cpu_ctx >();
if ( ( err = uc_context_alloc( uc, &obj->code_blocks.back().second->context ) ) )
{
std::printf( "[!] failed to allocate context space...\n" );
exit( 0 );
}
if ( ( err = uc_context_save( uc, obj->code_blocks.back().second->context ) ) )
{
std::printf( "[!] failed to save cpu context...\n" );
exit( 0 );
}
if ( ( err = uc_mem_read( uc, UC_STACK_ADDR, obj->code_blocks.back().second->stack,
sizeof vm::cpu_ctx::stack ) ) )
{
std::printf( "[!] failed to read stack into backup buffer...\n" );
exit( 0 );
}
if ( auto already_traced = std::find_if( obj->code_blocks->begin(), obj->code_blocks->end(), if ( auto already_traced = std::find_if( obj->code_blocks.begin(), obj->code_blocks.end(),
[ & ]( const vm::instrs::code_block_t &code_block ) -> bool { [ & ]( const auto &code_block_data ) -> bool {
return code_block.vip_begin == code_block_address; return code_block_data.first.vip_begin ==
code_block_address;
} ); } );
already_traced != obj->code_blocks->end() ) already_traced != obj->code_blocks.end() )
{ {
// stop tracing, dont step up the next code block since we already traced it... // stop tracing, dont step up the next code block since we already traced it...
uc_emu_stop( uc ); uc_emu_stop( uc );
@ -223,7 +305,7 @@ namespace vm
else else
{ {
// set the next code block up... // set the next code block up...
obj->code_blocks->push_back( vm::instrs::code_block_t{ code_block_address } ); obj->code_blocks.push_back( { vm::instrs::code_block_t{ 0u }, {} } );
} }
} }
} }
@ -231,7 +313,7 @@ namespace vm
{ {
uc_emu_stop( uc ); uc_emu_stop( uc );
// vmexit's cannot have a branch... // vmexit's cannot have a branch...
obj->code_blocks->back().jcc.has_jcc = false; obj->code_blocks.back().first.jcc.has_jcc = false;
} }
} }

@ -9,14 +9,22 @@
#include <vmprofiler.hpp> #include <vmprofiler.hpp>
#include <xtils.hpp> #include <xtils.hpp>
#define UC_STACK_ADDR 0x1000000
namespace vm namespace vm
{ {
struct cpu_ctx
{
uc_context *context;
std::uint8_t stack[ PAGE_4K * 20 ];
};
class emu_t class emu_t
{ {
using callback_t = std::function< void( uc_engine *, uint64_t, uint32_t, void * ) >; using callback_t = std::function< void( uc_engine *, uint64_t, uint32_t, void * ) >;
public: public:
explicit emu_t( vm::ctx_t* vmctx ); explicit emu_t( vm::ctx_t *vmctx );
~emu_t(); ~emu_t();
bool init(); bool init();
@ -31,7 +39,8 @@ namespace vm
uc_engine *uc; uc_engine *uc;
uc_hook trace, trace1; uc_hook trace, trace1;
bool skip_current_jmp;
vm::ctx_t *vmctx; vm::ctx_t *vmctx;
std::vector< vm::instrs::code_block_t > *code_blocks; std::vector< std::pair< vm::instrs::code_block_t, std::shared_ptr<cpu_ctx> > > code_blocks;
}; };
} // namespace vm } // namespace vm

Binary file not shown.

Binary file not shown.
Loading…
Cancel
Save