in the middle of something, adding branch detection cod

merge-requests/9/head
_xeroxz 4 years ago
parent 1a0ba7ef56
commit ffd45ecb8a

@ -172,11 +172,13 @@ namespace vm
class ctx_t
{
public:
explicit ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uint32_t vm_entry_rva );
ctx_t( std::vector< vm::handler::handler_t > &vm_handlers, zydis_routine_t &vm_entry, zydis_routine_t &calc_jmp,
vmp2::exec_type_t exec_type );
explicit ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uintptr_t image_size,
std::uintptr_t vm_entry_rva );
// never change...
const std::uintptr_t module_base, image_base, vm_entry_rva, image_size;
const vmp2::exec_type_t exec_type;
vmp2::exec_type_t exec_type;
zydis_routine_t vm_entry, calc_jmp;
std::vector< vm::handler::handler_t > vm_handlers;
};
@ -213,17 +215,18 @@ namespace vm
absolute
};
struct code_block_t
{
struct
struct jcc_data
{
bool has_jcc;
jcc_type type;
std::uint32_t block_rva[ 2 ];
} jcc;
};
std::uint32_t code_block_rva;
struct code_block_t
{
std::uintptr_t vip_begin;
std::vector< virt_instr_t > vinstrs;
jcc_data jcc;
};
// decrypt transformations for encrypted virtual instruction rva...
@ -245,6 +248,39 @@ namespace vm
/// <param name="vip">virtual instruction pointer, linear virtual address...</param>
/// <returns>returns immediate value if imm_size is not 0...</returns>
std::optional< std::uint64_t > get_imm( vm::ctx_t &ctx, std::uint8_t imm_size, std::uintptr_t vip );
/// <summary>
/// get jcc data out of a code block... this function will loop over the code block
/// and look for the last two NANDW in the virtual instructions, then it will look
/// for the last LCONSTW which is the xor key...
///
/// it will then loop and look for all PUSHVSP's, checking each to see if the stack
/// contains two encrypted rva's to each branch.. if there is not two encrypted rva's
/// then the virtual jmp instruction only has one dest...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="code_block">code block that does not have its jcc_data yet</param>
/// <returns>if last lconstdw is found, return filled in jcc_data structure...</returns>
std::optional< jcc_data > get_jcc_data( vm::ctx_t &ctx, code_block_t &code_block );
/// <summary>
/// the top of the stack will contain the lower 32bits of the RVA to the virtual instructions
/// that will be jumping too... the RVA is image based (not module based, but optional header image
/// based)... this means the value ontop of the stack could be "40007fd8" with image base being
/// 0x140000000... as you can see the 0x100000000 is missing... the below statement deals with this...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="entry">current trace entry for virtual JMP instruction</param>
/// <returns>returns linear virtual address of the next code block...</returns>
std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const vmp2::v2::entry_t &entry );
/// <summary>
/// same routine as above except lower_32bits is passed directly and not extracted from the stack...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="lower_32bits">lower 32bits of the relative virtual address...</param>
/// <returns>returns full linear virtual address of code block...</returns>
std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const std::uint32_t lower_32bits );
} // namespace instrs
namespace calc_jmp

@ -2,7 +2,9 @@
namespace vm
{
ctx_t::ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uint32_t vm_entry_rva )
ctx_t::ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uintptr_t image_size,
std::uintptr_t vm_entry_rva )
: module_base( module_base ), image_base( image_base ), image_size( image_size ), vm_entry_rva( vm_entry_rva )
{
vm::util::flatten( vm_entry, vm_entry_rva + module_base );
vm::util::deobfuscate( vm_entry );
@ -11,10 +13,4 @@ namespace vm
auto vm_handler_table = vm::handler::table::get( vm_entry );
vm::handler::get_all( module_base, image_base, vm_entry, vm_handler_table, vm_handlers );
}
ctx_t::ctx_t( std::vector< vm::handler::handler_t > &vm_handlers, zydis_routine_t &vm_entry,
zydis_routine_t &calc_jmp, vmp2::exec_type_t exec_type )
: vm_handlers( vm_handlers ), vm_entry( vm_entry ), calc_jmp( calc_jmp ), exec_type( exec_type )
{
}
} // namespace vm

@ -196,5 +196,78 @@ namespace vm
return result;
}
std::optional< jcc_data > get_jcc_data( vm::ctx_t &vmctx, code_block_t &code_block )
{
// there is no branch for this as this is a vmexit...
if ( code_block.vinstrs.back().mnemonic_t == vm::handler::VMEXIT )
return {};
// find the last LCONSTDW... the imm value is the JMP xor decrypt key...
// we loop backwards here (using rbegin and rend)...
auto result = std::find_if( code_block.vinstrs.rbegin(), code_block.vinstrs.rend(),
[]( const vm::instrs::virt_instr_t &vinstr ) -> bool {
auto profile = vm::handler::get_profile( vinstr.mnemonic_t );
return profile && profile->mnemonic == vm::handler::LCONSTDW;
} );
jcc_data jcc;
const auto xor_key = static_cast< std::uint32_t >( result->operand.imm.u );
const auto &last_trace = code_block.vinstrs.back().trace_data;
// since result is already a variable and is a reverse itr
// im going to be using rbegin and rend here again...
//
// look for PUSHVSP virtual instructions with two encrypted virtual
// instruction rva's ontop of the virtual stack...
result = std::find_if( code_block.vinstrs.rbegin(), code_block.vinstrs.rend(),
[ & ]( const vm::instrs::virt_instr_t &vinstr ) -> bool {
auto profile = vm::handler::get_profile( vinstr.mnemonic_t );
if ( profile && profile->mnemonic == vm::handler::PUSHVSP )
{
const auto possible_block_1 =
code_block_addr( vmctx, vinstr.trace_data.vsp.qword[ 0 ] ^ xor_key );
const auto possible_block_2 =
code_block_addr( vmctx, vinstr.trace_data.vsp.qword[ 1 ] ^ xor_key );
// if this returns too many false positives we might have to get
// our hands dirty and look into trying to emulate each branch
// to see if the first instruction is an SREGQ...
return possible_block_1 > vmctx.module_base &&
possible_block_1 < vmctx.module_base + vmctx.image_size &&
possible_block_2 > vmctx.module_base &&
possible_block_2 < vmctx.module_base + vmctx.image_size;
}
return false;
} );
// if there is not two branches...
if ( result == code_block.vinstrs.rend() )
{
jcc.block_rva[ 0 ] = code_block_addr( vmctx, last_trace );
jcc.has_jcc = false;
jcc.type = jcc_type::absolute;
}
// else there are two branches...
else
{
jcc.block_rva[ 0 ] =
}
return jcc;
}
std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const vmp2::v2::entry_t &entry )
{
return ( ( entry.vsp.qword[ 0 ] + ctx.image_base & ~0xFFFFFFFFull ) - ctx.image_base ) + ctx.module_base;
}
std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const std::uint32_t lower_32bits )
{
return ( ( lower_32bits + ctx.image_base & ~0xFFFFFFFFull ) - ctx.image_base ) + ctx.module_base;
}
} // namespace instrs
} // namespace vm
Loading…
Cancel
Save