fixed deadstore removal algo and added a few handlers...

merge-requests/15/head
_xeroxz 3 years ago
parent ae49869fe6
commit 7240a2a23c

@ -54,12 +54,11 @@ namespace vm::handler
/// <summary>
/// gets a vm handler, puts all of the native instructions inside of the vm_handler param...
/// </summary>
/// <param name="vm_entry">reference to a zydis_routine_t containing the native instructions of a vm
/// entry...</param> <param name="vm_handler">reference to a zydis_routine_t that will get filled with the
/// <param name="vm_handler">reference to a zydis_routine_t that will get filled with the
/// native instructions of the vm handler...</param> <param name="handler_addr">linear virtual address to the
/// first instruction of the vm handler...</param> <returns>returns true if the native instructions of the vm
/// handler was extracted...</returns>
bool get( zydis_routine_t &vm_entry, zydis_routine_t &vm_handler, std::uintptr_t handler_addr );
bool get( zydis_routine_t &vm_handler, std::uintptr_t handler_addr );
/// <summary>
/// get all 256 vm handlers...

@ -41,6 +41,7 @@ namespace vm::handler
LCONSTW,
READQ,
READGSQ,
READDW,
READW,
READB,
@ -158,6 +159,7 @@ namespace vm::handler
extern vm::handler::profile_t writeb;
extern vm::handler::profile_t readq;
extern vm::handler::profile_t readgsq;
extern vm::handler::profile_t readdw;
extern vm::handler::profile_t readw;
extern vm::handler::profile_t readb;
@ -180,16 +182,16 @@ namespace vm::handler
/// a vector of pointers to all defined vm handler profiles...
/// </summary>
inline std::vector< vm::handler::profile_t * > all = {
&sregq, &sregdw, &sregw, &lregq, &lregdw, &lconstq, &lconstbzxw,
&lconstbsxdw, &lconstbsxq, &lconstdwsxq, &lconstwsxq, &lconstwsxdw, &lconstdw, &lconstw,
&addq, &adddw, &addw, &lvsp,
&sregq, &sregdw, &sregw, &lregq, &lregdw, &lconstq, &lconstbzxw, &lconstbsxdw,
&lconstbsxq, &lconstdwsxq, &lconstwsxq, &lconstwsxdw, &lconstdw, &lconstw, &addq, &adddw,
&addw, &lvsp,
&shlq, &shldw, &writeq, &writedw, &writeb, &nandq, &nanddw,
&nandw, &nandb,
&shlq, &shldw, &writeq, &writedw, &writeb, &nandq, &nanddw, &nandw,
&nandb,
&shlddw,
&shrq, &shrw, &readq, &readdw, &readw, &readb, &mulq,
&pushvsp, &pushvspdw, &divq, &jmp, &lrflags, &vmexit, &call };
&shrq, &shrw, &readgsq, &readq, &readdw, &readw, &readb, &mulq,
&pushvsp, &pushvspdw, &divq, &jmp, &lrflags, &vmexit, &call };
} // namespace profile
} // namespace vm::handler

@ -17,6 +17,7 @@ using u128 = __m128;
using zydis_decoded_instr_t = ZydisDecodedInstruction;
using zydis_register_t = ZydisRegister;
using zydis_mnemonic_t = ZydisMnemonic;
using zydis_decoded_operand_t = ZydisDecodedOperand;
struct zydis_instr_t
{

@ -2,29 +2,19 @@
namespace vm::handler
{
bool get( zydis_routine_t &calc_jmp, zydis_routine_t &vm_handler, std::uintptr_t handler_addr )
bool get( zydis_routine_t &vm_handler, std::uintptr_t handler_addr )
{
if ( !vm::util::flatten( vm_handler, handler_addr ) )
return false;
vm::util::deobfuscate( vm_handler );
static const auto calc_jmp_check = [ & ]( std::uintptr_t addr ) -> bool {
for ( const auto &[ instr, instr_raw, instr_addr ] : calc_jmp )
if ( instr_addr == addr )
return true;
return false;
};
// find LEA RAX, [RDI+0xE0], else determine if the instruction is inside of calc_jmp...
auto result = std::find_if( vm_handler.begin(), vm_handler.end(), []( const zydis_instr_t &instr ) -> bool {
return instr.instr.mnemonic == ZYDIS_MNEMONIC_LEA &&
instr.instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RAX &&
instr.instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RDI &&
instr.instr.operands[ 1 ].mem.disp.value == 0xE0
? true
: calc_jmp_check( instr.addr );
instr.instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RAX &&
instr.instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RDI &&
instr.instr.operands[ 1 ].mem.disp.value == 0xE0;
} );
// remove calc_jmp from the vm handler vector...
@ -33,30 +23,20 @@ namespace vm::handler
else // locate the last mov al, [rsi],
// then remove all instructions after that...
{
zydis_routine_t::iterator last = vm_handler.end();
result = vm_handler.begin();
while ( result != vm_handler.end() )
{
result = std::find_if( ++result, vm_handler.end(), []( const zydis_instr_t &instr_data ) -> bool {
// mov/movsx/movzx rax/eax/ax/al, [rsi]
return instr_data.instr.operand_count > 1 &&
( instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOV ||
instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOVSX ||
instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOVZX ) &&
instr_data.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
util::reg::to64( instr_data.instr.operands[ 0 ].reg.value ) == ZYDIS_REGISTER_RAX &&
instr_data.instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr_data.instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RSI;
} );
if ( result != vm_handler.end() )
last = result;
}
auto last = std::find_if( vm_handler.rbegin(), vm_handler.rend(), []( const zydis_instr_t &instr ) -> bool {
return instr.instr.operand_count > 1 &&
( instr.instr.mnemonic == ZYDIS_MNEMONIC_MOV || instr.instr.mnemonic == ZYDIS_MNEMONIC_MOVSX ||
instr.instr.mnemonic == ZYDIS_MNEMONIC_MOVZX ) &&
instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
vm::util::reg::to64( instr.instr.operands[ 0 ].reg.value ) == ZYDIS_REGISTER_RAX &&
instr.instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RSI;
} );
if ( last != vm_handler.end() )
vm_handler.erase( last, vm_handler.end() );
if ( last != vm_handler.rend() )
vm_handler.erase( std::next( last ).base(), vm_handler.end() );
}
return true;
}
@ -67,10 +47,6 @@ namespace vm::handler
if ( !vm::handler::table::get_transform( vm_entry, &instr ) )
return false;
zydis_routine_t calc_jmp;
if ( !vm::calc_jmp::get( vm_entry, calc_jmp ) )
return false;
for ( auto idx = 0u; idx < 256; ++idx )
{
handler_t vm_handler;
@ -78,7 +54,7 @@ namespace vm::handler
zydis_routine_t vm_handler_instrs;
const auto decrypt_val = vm::handler::table::decrypt( instr, vm_handler_table[ idx ] );
if ( !vm::handler::get( calc_jmp, vm_handler_instrs, ( decrypt_val - image_base ) + module_base ) )
if ( !vm::handler::get( vm_handler_instrs, ( decrypt_val - image_base ) + module_base ) )
return false;
const auto has_imm = vm::handler::has_imm( vm_handler_instrs );

@ -3,17 +3,58 @@
namespace vm::handler::profile
{
vm::handler::profile_t readq = {
// MOV RAX, [RBP]
// MOV RAX, [RAX]
// MOV [RBP], RAX
"READQ",
READQ,
NULL,
{ { // MOV RAX, [RAX]
{ { // MOV RAX, [RBP]
[]( const zydis_decoded_instr_t &instr ) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RAX &&
instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RBP;
},
// MOV RAX, [RAX]
[]( const zydis_decoded_instr_t &instr ) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RAX &&
instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RAX;
},
// MOV [RBP], RAX
[]( const zydis_decoded_instr_t &instr ) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_MOV && instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.operands[ 0 ].mem.base == ZYDIS_REGISTER_RBP &&
instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[ 1 ].reg.value == ZYDIS_REGISTER_RAX;
} } } };
vm::handler::profile_t readgsq = {
// MOV RAX, [RBP]
// MOV RAX, GS:[RAX]
// MOV [RBP], RAX
"READGSQ",
READQ,
NULL,
{ { // MOV RAX, [RBP]
[]( const zydis_decoded_instr_t &instr ) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RAX &&
instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RBP;
},
// MOV RAX, GS:[RAX]
[]( const zydis_decoded_instr_t &instr ) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RAX &&
instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.operands[ 1 ].mem.segment == ZYDIS_REGISTER_GS &&
instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RAX;
},
// MOV [RBP], RAX

@ -160,7 +160,7 @@ namespace vm::util
void deobfuscate( zydis_routine_t &routine )
{
static const auto _uses = []( ZydisDecodedOperand &op, zydis_register_t reg ) -> bool {
static const auto _uses_reg = []( zydis_decoded_operand_t &op, zydis_register_t reg ) -> bool {
switch ( op.type )
{
case ZYDIS_OPERAND_TYPE_MEMORY:
@ -177,82 +177,88 @@ namespace vm::util
return false;
};
static const auto _writes = []( zydis_decoded_instr_t &inst ) -> bool {
for ( auto idx = 0; idx < inst.operand_count; ++idx )
if ( inst.operands[ idx ].actions & ZYDIS_OPERAND_ACTION_MASK_WRITE )
static const auto _reads = []( zydis_decoded_instr_t &instr, zydis_register_t reg ) -> bool {
if ( instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
reg::compare( instr.operands[ 0 ].mem.base, reg ) )
return true;
for ( auto op_idx = 0u; op_idx < instr.operand_count; ++op_idx )
if ( instr.operands[ op_idx ].actions & ZYDIS_OPERAND_ACTION_READ &&
_uses_reg( instr.operands[ op_idx ], reg ) )
return true;
return false;
};
static const auto _writes = []( zydis_decoded_instr_t &instr, zydis_register_t reg ) -> bool {
for ( auto op_idx = 0u; op_idx < instr.operand_count; ++op_idx )
// if instruction writes to the specific register...
if ( instr.operands[ op_idx ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[ op_idx ].actions & ZYDIS_OPERAND_ACTION_WRITE &&
!( instr.operands[ op_idx ].actions & ZYDIS_OPERAND_ACTION_READ ) &&
reg::compare( instr.operands[ op_idx ].reg.value, reg ) )
return true;
return false;
};
static const auto _remove = []( zydis_routine_t &routine, zydis_routine_t::iterator itr, zydis_register_t reg,
u32 opcode_size ) -> void {
for ( ; itr >= routine.begin(); --itr )
{
const auto instruction = &itr->instr;
auto stop = false;
std::uint32_t last_size = 0u;
do
{
last_size = routine.size();
if ( instruction->mnemonic == ZYDIS_MNEMONIC_JMP )
for ( auto itr = routine.begin(); itr != routine.end(); ++itr )
{
// dont remove these... at all...
if ( itr->instr.mnemonic == ZYDIS_MNEMONIC_PUSH || itr->instr.mnemonic == ZYDIS_MNEMONIC_POP )
continue;
for ( auto op_idx = 0u; op_idx < instruction->operand_count; ++op_idx )
// lol... we are only looking at linear code anyways... :P
if ( itr->instr.mnemonic == ZYDIS_MNEMONIC_CLC || itr->instr.mnemonic == ZYDIS_MNEMONIC_BT ||
itr->instr.mnemonic == ZYDIS_MNEMONIC_TEST || itr->instr.mnemonic == ZYDIS_MNEMONIC_CMP ||
itr->instr.mnemonic == ZYDIS_MNEMONIC_CMC || itr->instr.mnemonic == ZYDIS_MNEMONIC_STC )
{
const auto op = &instruction->operands[ op_idx ];
if ( !_uses( *op, reg ) )
continue;
if ( op->type == ZYDIS_OPERAND_TYPE_MEMORY )
{
stop = true;
continue;
}
if ( opcode_size < 32 && op->size > opcode_size )
continue;
op->actions &ZYDIS_OPERAND_ACTION_MASK_WRITE ? op->actions &= ~ZYDIS_OPERAND_ACTION_MASK_WRITE
: stop = true;
}
if ( !_writes( *instruction ) )
routine.erase( itr );
else if ( stop )
break;
}
};
}
for ( const auto &instr_data : routine )
{
if ( routine.empty() || routine.size() == 1 || instr_data.instr.mnemonic == ZYDIS_MNEMONIC_JMP )
continue;
zydis_register_t reg = ZYDIS_REGISTER_NONE;
// look for operands with writes to a register...
for ( auto op_idx = 0u; op_idx < itr->instr.operand_count; ++op_idx )
if ( itr->instr.operands[ op_idx ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
itr->instr.operands[ op_idx ].actions & ZYDIS_OPERAND_ACTION_WRITE )
reg = reg::to64( itr->instr.operands[ 0 ].reg.value );
for ( auto itr = routine.begin() + 1; itr != routine.end(); itr++ )
{
if ( itr->instr.mnemonic == ZYDIS_MNEMONIC_JMP || itr->instr.mnemonic == ZYDIS_MNEMONIC_RET )
break;
// find the write operations that happen...
for ( auto idx = 0u; idx < itr->instr.operand_count; ++idx )
// if this current instruction writes to a register, look ahead in the instruction stream to see
// if it gets written too before it gets read...
if ( reg != ZYDIS_REGISTER_NONE )
{
const auto op = &itr->instr.operands[ idx ];
// if its a read, continue to next opcode...
if ( op->actions & ZYDIS_OPERAND_ACTION_MASK_READ )
continue;
// if its not a write then continue to next opcode...
if ( !( op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE ) )
// find the next place that this register is written too...
auto write_result = std::find_if( itr + 1, routine.end(), [ & ]( zydis_instr_t &instr ) -> bool {
return _writes( instr.instr, reg );
} );
auto read_result = std::find_if( itr + 1, write_result, [ & ]( zydis_instr_t &instr ) -> bool {
return _reads( instr.instr, reg );
} );
// if there is neither a read or a write to this register in the instruction stream
// then we are going to be safe and leave the instruction in the stream...
if ( read_result == routine.end() && write_result == routine.end() )
continue;
// if this operand is not a register then we continue...
if ( op->type != ZYDIS_OPERAND_TYPE_REGISTER )
continue;
// if there is no read of the register before the next write... and there is
// a known next write, then remove the instruction from the stream...
if ( read_result == write_result && write_result != routine.end() )
{
// if the instruction reads and writes the same register than skip...
if ( _reads( read_result->instr, reg ) && _writes( read_result->instr, reg ) )
continue;
// else we see if we can remove dead writes to this register...
_remove( routine, itr - 1, op->reg.value, op->size );
routine.erase( itr );
break;
}
}
}
}
} while ( last_size != routine.size() );
}
} // namespace vm::util
Loading…
Cancel
Save