From 1a0ba7ef56d06c459c79fe4cb334f5f7a916c558 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Wed, 2 Jun 2021 19:26:49 -0700 Subject: [PATCH] added vm::ctx_t which can be passed around >:) --- include/vmprofiler.hpp | 104 ++++++++++++++++++++++++++++++------- src/vmctx.cpp | 20 +++++++ src/vminstrs.cpp | 66 +++++++++++++++++------ vmprofiler.vcxproj | 1 + vmprofiler.vcxproj.filters | 3 ++ 5 files changed, 159 insertions(+), 35 deletions(-) create mode 100644 src/vmctx.cpp diff --git a/include/vmprofiler.hpp b/include/vmprofiler.hpp index bf8d74b..9cbc78e 100644 --- a/include/vmprofiler.hpp +++ b/include/vmprofiler.hpp @@ -5,25 +5,6 @@ namespace vm { - namespace calc_jmp - { - bool get( const zydis_routine_t &vm_entry, zydis_routine_t &calc_jmp ); - - std::optional< vmp2::exec_type_t > get_advancement( const zydis_routine_t &calc_jmp ); - } // namespace calc_jmp - - namespace instrs - { - // decrypt transformations for encrypted virtual instruction rva... - bool get_rva_decrypt( const zydis_routine_t &vm_entry, std::vector< zydis_decoded_instr_t > &transform_instrs ); - - std::pair< std::uint64_t, std::uint64_t > decrypt_operand( transform::map_t &transforms, std::uint64_t operand, - std::uint64_t rolling_key ); - - std::pair< std::uint64_t, std::uint64_t > encrypt_operand( transform::map_t &transforms, std::uint64_t operand, - std::uint64_t rolling_key ); - } // namespace instrs - namespace handler { using instr_callback_t = bool ( * )( const zydis_decoded_instr_t &instr ); @@ -187,4 +168,89 @@ namespace vm &lrflags, &vmexit, &call }; } // namespace profile } // namespace handler + + class ctx_t + { + public: + explicit ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uint32_t vm_entry_rva ); + ctx_t( std::vector< vm::handler::handler_t > &vm_handlers, zydis_routine_t &vm_entry, zydis_routine_t &calc_jmp, + vmp2::exec_type_t exec_type ); + + vmp2::exec_type_t exec_type; + zydis_routine_t vm_entry, calc_jmp; + std::vector< vm::handler::handler_t > vm_handlers; + }; + + namespace instrs + { + struct virt_instr_t + { + vm::handler::mnemonic_t mnemonic_t; + std::uint8_t opcode; // aka vm handler idx... + + // can be used to look at values on the stack... + vmp2::v2::entry_t trace_data; + + struct + { + bool has_imm; + struct + { + std::uint8_t imm_size; // size in bits... + union + { + std::int64_t s; + std::uint64_t u; + }; + } imm; + } operand; + }; + + enum class jcc_type + { + none, + branching, + absolute + }; + + struct code_block_t + { + struct + { + bool has_jcc; + jcc_type type; + std::uint32_t block_rva[ 2 ]; + } jcc; + + std::uint32_t code_block_rva; + std::vector< virt_instr_t > vinstrs; + }; + + // decrypt transformations for encrypted virtual instruction rva... + bool get_rva_decrypt( const zydis_routine_t &vm_entry, std::vector< zydis_decoded_instr_t > &transform_instrs ); + + std::pair< std::uint64_t, std::uint64_t > decrypt_operand( transform::map_t &transforms, std::uint64_t operand, + std::uint64_t rolling_key ); + + std::pair< std::uint64_t, std::uint64_t > encrypt_operand( transform::map_t &transforms, std::uint64_t operand, + std::uint64_t rolling_key ); + + std::optional< virt_instr_t > get( vm::ctx_t &ctx, vmp2::v2::entry_t &entry ); + + /// + /// gets the second operand (imm) given vip and vm::ctx_t... + /// + /// vm context + /// immediate value size in bits... + /// virtual instruction pointer, linear virtual address... + /// returns immediate value if imm_size is not 0... + std::optional< std::uint64_t > get_imm( vm::ctx_t &ctx, std::uint8_t imm_size, std::uintptr_t vip ); + } // namespace instrs + + namespace calc_jmp + { + bool get( const zydis_routine_t &vm_entry, zydis_routine_t &calc_jmp ); + + std::optional< vmp2::exec_type_t > get_advancement( const zydis_routine_t &calc_jmp ); + } // namespace calc_jmp } // namespace vm \ No newline at end of file diff --git a/src/vmctx.cpp b/src/vmctx.cpp new file mode 100644 index 0000000..0b7e1ef --- /dev/null +++ b/src/vmctx.cpp @@ -0,0 +1,20 @@ +#include + +namespace vm +{ + ctx_t::ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uint32_t vm_entry_rva ) + { + vm::util::flatten( vm_entry, vm_entry_rva + module_base ); + vm::util::deobfuscate( vm_entry ); + vm::calc_jmp::get( vm_entry, calc_jmp ); + + auto vm_handler_table = vm::handler::table::get( vm_entry ); + vm::handler::get_all( module_base, image_base, vm_entry, vm_handler_table, vm_handlers ); + } + + ctx_t::ctx_t( std::vector< vm::handler::handler_t > &vm_handlers, zydis_routine_t &vm_entry, + zydis_routine_t &calc_jmp, vmp2::exec_type_t exec_type ) + : vm_handlers( vm_handlers ), vm_entry( vm_entry ), calc_jmp( calc_jmp ), exec_type( exec_type ) + { + } +} // namespace vm \ No newline at end of file diff --git a/src/vminstrs.cpp b/src/vminstrs.cpp index 9d92817..066e159 100644 --- a/src/vminstrs.cpp +++ b/src/vminstrs.cpp @@ -7,12 +7,12 @@ namespace vm std::pair< std::uint64_t, std::uint64_t > decrypt_operand( transform::map_t &transforms, std::uint64_t operand, std::uint64_t rolling_key ) { - const auto& generic_decrypt_0 = transforms[ transform::type::generic0 ]; - const auto& key_decrypt = transforms[ transform::type::rolling_key ]; - const auto& generic_decrypt_1 = transforms[ transform::type::generic1 ]; - const auto& generic_decrypt_2 = transforms[ transform::type::generic2 ]; - const auto& generic_decrypt_3 = transforms[ transform::type::generic3 ]; - const auto& update_key = transforms[ transform::type::update_key ]; + const auto &generic_decrypt_0 = transforms[ transform::type::generic0 ]; + const auto &key_decrypt = transforms[ transform::type::rolling_key ]; + const auto &generic_decrypt_1 = transforms[ transform::type::generic1 ]; + const auto &generic_decrypt_2 = transforms[ transform::type::generic2 ]; + const auto &generic_decrypt_3 = transforms[ transform::type::generic3 ]; + const auto &update_key = transforms[ transform::type::update_key ]; if ( generic_decrypt_0.mnemonic != ZYDIS_MNEMONIC_INVALID ) { @@ -44,8 +44,7 @@ namespace vm } // update rolling key... - auto result = - transform::apply( update_key.operands[ 0 ].size, update_key.mnemonic, rolling_key, operand ); + auto result = transform::apply( update_key.operands[ 0 ].size, update_key.mnemonic, rolling_key, operand ); // update decryption key correctly... switch ( update_key.operands[ 0 ].size ) @@ -70,15 +69,14 @@ namespace vm transform::map_t inverse; inverse_transforms( transforms, inverse ); - const auto& generic_decrypt_0 = inverse[ transform::type::generic0 ]; - const auto& key_decrypt = inverse[ transform::type::rolling_key ]; - const auto& generic_decrypt_1 = inverse[ transform::type::generic1 ]; - const auto& generic_decrypt_2 = inverse[ transform::type::generic2 ]; - const auto& generic_decrypt_3 = inverse[ transform::type::generic3 ]; - const auto& update_key = inverse[ transform::type::update_key ]; + const auto &generic_decrypt_0 = inverse[ transform::type::generic0 ]; + const auto &key_decrypt = inverse[ transform::type::rolling_key ]; + const auto &generic_decrypt_1 = inverse[ transform::type::generic1 ]; + const auto &generic_decrypt_2 = inverse[ transform::type::generic2 ]; + const auto &generic_decrypt_3 = inverse[ transform::type::generic3 ]; + const auto &update_key = inverse[ transform::type::update_key ]; - auto result = - transform::apply( update_key.operands[ 0 ].size, update_key.mnemonic, rolling_key, operand ); + auto result = transform::apply( update_key.operands[ 0 ].size, update_key.mnemonic, rolling_key, operand ); // make sure we update the rolling decryption key correctly... switch ( update_key.operands[ 0 ].size ) @@ -162,5 +160,41 @@ namespace vm return true; } + + std::optional< std::uint64_t > get_imm( vm::ctx_t &ctx, std::uint8_t imm_size, std::uintptr_t vip ) + { + if ( !imm_size ) + return {}; + + return ctx.exec_type == vmp2::exec_type_t::forward + ? *reinterpret_cast< std::uintptr_t * >( vip + ( imm_size / 8 ) ) + : *reinterpret_cast< std::uintptr_t * >( vip - ( imm_size / 8 ) ); + } + + std::optional< virt_instr_t > get( vm::ctx_t &ctx, vmp2::v2::entry_t &entry ) + { + virt_instr_t result; + const auto &vm_handler = ctx.vm_handlers[ entry.handler_idx ]; + const auto profile = vm_handler.profile; + + result.mnemonic_t = profile ? profile->mnemonic : vm::handler::mnemonic_t::INVALID; + result.opcode = entry.handler_idx; + result.trace_data = entry; + + if ( vm_handler.imm_size ) + { + result.operand.has_imm = true; + const auto imm_val = get_imm( ctx, vm_handler.imm_size, entry.vip ); + + if ( !imm_val.has_value() ) + return {}; + + result.operand.imm.u = imm_val.value(); + } + else + result.operand.has_imm = false; + + return result; + } } // namespace instrs } // namespace vm \ No newline at end of file diff --git a/vmprofiler.vcxproj b/vmprofiler.vcxproj index 760225c..e357f76 100644 --- a/vmprofiler.vcxproj +++ b/vmprofiler.vcxproj @@ -156,6 +156,7 @@ + diff --git a/vmprofiler.vcxproj.filters b/vmprofiler.vcxproj.filters index 99d0148..7c02f19 100644 --- a/vmprofiler.vcxproj.filters +++ b/vmprofiler.vcxproj.filters @@ -234,5 +234,8 @@ Source Files + + Source Files + \ No newline at end of file