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> /// <summary>
/// gets a vm handler, puts all of the native instructions inside of the vm_handler param... /// gets a vm handler, puts all of the native instructions inside of the vm_handler param...
/// </summary> /// </summary>
/// <param name="vm_entry">reference to a zydis_routine_t containing the native instructions of a vm /// <param name="vm_handler">reference to a zydis_routine_t that will get filled with the
/// entry...</param> <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 /// 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 /// first instruction of the vm handler...</param> <returns>returns true if the native instructions of the vm
/// handler was extracted...</returns> /// 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> /// <summary>
/// get all 256 vm handlers... /// get all 256 vm handlers...

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

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

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

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

@ -160,7 +160,7 @@ namespace vm::util
void deobfuscate( zydis_routine_t &routine ) 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 ) switch ( op.type )
{ {
case ZYDIS_OPERAND_TYPE_MEMORY: case ZYDIS_OPERAND_TYPE_MEMORY:
@ -177,82 +177,88 @@ namespace vm::util
return false; return false;
}; };
static const auto _writes = []( zydis_decoded_instr_t &inst ) -> bool { static const auto _reads = []( zydis_decoded_instr_t &instr, zydis_register_t reg ) -> bool {
for ( auto idx = 0; idx < inst.operand_count; ++idx ) if ( instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
if ( inst.operands[ idx ].actions & ZYDIS_OPERAND_ACTION_MASK_WRITE ) 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 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; return false;
}; };
static const auto _remove = []( zydis_routine_t &routine, zydis_routine_t::iterator itr, zydis_register_t reg, std::uint32_t last_size = 0u;
u32 opcode_size ) -> void {
for ( ; itr >= routine.begin(); --itr ) do
{ {
const auto instruction = &itr->instr; last_size = routine.size();
auto stop = false;
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; 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 ); routine.erase( itr );
else if ( stop )
break; break;
} }
};
for ( const auto &instr_data : routine ) zydis_register_t reg = ZYDIS_REGISTER_NONE;
{ // look for operands with writes to a register...
if ( routine.empty() || routine.size() == 1 || instr_data.instr.mnemonic == ZYDIS_MNEMONIC_JMP ) for ( auto op_idx = 0u; op_idx < itr->instr.operand_count; ++op_idx )
continue; 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 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 ( itr->instr.mnemonic == ZYDIS_MNEMONIC_JMP || itr->instr.mnemonic == ZYDIS_MNEMONIC_RET ) if ( reg != ZYDIS_REGISTER_NONE )
break;
// find the write operations that happen...
for ( auto idx = 0u; idx < itr->instr.operand_count; ++idx )
{ {
const auto op = &itr->instr.operands[ idx ]; // find the next place that this register is written too...
// if its a read, continue to next opcode... auto write_result = std::find_if( itr + 1, routine.end(), [ & ]( zydis_instr_t &instr ) -> bool {
if ( op->actions & ZYDIS_OPERAND_ACTION_MASK_READ ) return _writes( instr.instr, reg );
continue; } );
// if its not a write then continue to next opcode... auto read_result = std::find_if( itr + 1, write_result, [ & ]( zydis_instr_t &instr ) -> bool {
if ( !( op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE ) ) 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; continue;
// if this operand is not a register then we continue... // if there is no read of the register before the next write... and there is
if ( op->type != ZYDIS_OPERAND_TYPE_REGISTER ) // a known next write, then remove the instruction from the stream...
continue; 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... routine.erase( itr );
_remove( routine, itr - 1, op->reg.value, op->size ); break;
}
} }
} }
} } while ( last_size != routine.size() );
} }
} // namespace vm::util } // namespace vm::util
Loading…
Cancel
Save