|
|
@ -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
|