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