diff --git a/include/vmhandlers.hpp b/include/vmhandlers.hpp
index ae59ebe..f5c90a1 100644
--- a/include/vmhandlers.hpp
+++ b/include/vmhandlers.hpp
@@ -54,12 +54,11 @@ namespace vm::handler
///
/// gets a vm handler, puts all of the native instructions inside of the vm_handler param...
///
- /// reference to a zydis_routine_t containing the native instructions of a vm
- /// entry... reference to a zydis_routine_t that will get filled with the
+ /// reference to a zydis_routine_t that will get filled with the
/// native instructions of the vm handler... linear virtual address to the
/// first instruction of the vm handler... returns true if the native instructions of the vm
/// handler was extracted...
- 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 );
///
/// get all 256 vm handlers...
diff --git a/include/vmprofiles.hpp b/include/vmprofiles.hpp
index 7e2643d..5ed98a9 100644
--- a/include/vmprofiles.hpp
+++ b/include/vmprofiles.hpp
@@ -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...
///
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
\ No newline at end of file
diff --git a/include/vmutils.hpp b/include/vmutils.hpp
index 489700c..d762322 100644
--- a/include/vmutils.hpp
+++ b/include/vmutils.hpp
@@ -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
{
diff --git a/src/vmhandler.cpp b/src/vmhandler.cpp
index 426b161..6d6cd0c 100644
--- a/src/vmhandler.cpp
+++ b/src/vmhandler.cpp
@@ -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 );
diff --git a/src/vmprofiles/read.cpp b/src/vmprofiles/read.cpp
index 9d06d41..e2760b5 100644
--- a/src/vmprofiles/read.cpp
+++ b/src/vmprofiles/read.cpp
@@ -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
diff --git a/src/vmutils.cpp b/src/vmutils.cpp
index 69d519c..65ad96d 100644
--- a/src/vmutils.cpp
+++ b/src/vmutils.cpp
@@ -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
\ No newline at end of file