From 31f934cfb7cf57ae2c98db2eefe5aa0160b3b34b Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Wed, 9 Jun 2021 23:24:50 -0700 Subject: [PATCH] cleaned the code a bunch, preparing for doxygen... --- include/calc_jmp.hpp | 12 + include/transform.hpp | 2 +- include/vmctx.hpp | 20 ++ include/vmhandlers.hpp | 38 ++++ include/vminstrs.hpp | 69 ++++++ include/vmp2.hpp | 87 +++++++- include/vmprofiler.hpp | 343 +---------------------------- include/vmprofiles.hpp | 138 ++++++++++++ include/{vmutils.h => vmutils.hpp} | 8 + src/calc_jmp.cpp | 41 ++-- src/vmhandler.cpp | 66 ++---- src/vminstrs.cpp | 30 +-- src/vmprofiles/add.cpp | 2 +- src/vmprofiles/call.cpp | 2 +- src/vmprofiles/div.cpp | 2 +- src/vmprofiles/jmp.cpp | 2 +- src/vmprofiles/lconst.cpp | 2 +- src/vmprofiles/lflags.cpp | 2 +- src/vmprofiles/lreg.cpp | 2 +- src/vmprofiles/mul.cpp | 2 +- src/vmprofiles/nand.cpp | 2 +- src/vmprofiles/pushvsp.cpp | 2 +- src/vmprofiles/read.cpp | 2 +- src/vmprofiles/shl.cpp | 2 +- src/vmprofiles/shr.cpp | 2 +- src/vmprofiles/sreg.cpp | 2 +- src/vmprofiles/vmexit.cpp | 2 +- src/vmprofiles/write.cpp | 2 +- src/vmutils.cpp | 69 ++++-- vmprofiler.vcxproj | 7 +- vmprofiler.vcxproj.filters | 17 +- 31 files changed, 517 insertions(+), 462 deletions(-) create mode 100644 include/calc_jmp.hpp create mode 100644 include/vmctx.hpp create mode 100644 include/vmhandlers.hpp create mode 100644 include/vminstrs.hpp create mode 100644 include/vmprofiles.hpp rename include/{vmutils.h => vmutils.hpp} (81%) diff --git a/include/calc_jmp.hpp b/include/calc_jmp.hpp new file mode 100644 index 0000000..973efdf --- /dev/null +++ b/include/calc_jmp.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +namespace vm +{ + namespace calc_jmp + { + bool get( 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/include/transform.hpp b/include/transform.hpp index 7ce3075..2bfe3c0 100644 --- a/include/transform.hpp +++ b/include/transform.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include namespace vm { diff --git a/include/vmctx.hpp b/include/vmctx.hpp new file mode 100644 index 0000000..e47403f --- /dev/null +++ b/include/vmctx.hpp @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include + +namespace vm +{ + class ctx_t + { + public: + explicit ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uintptr_t image_size, + std::uintptr_t vm_entry_rva ); + + bool init(); + const std::uintptr_t module_base, image_base, vm_entry_rva, image_size; + vmp2::exec_type_t exec_type; + zydis_routine_t vm_entry, calc_jmp; + std::vector< vm::handler::handler_t > vm_handlers; + }; +} // namespace vm \ No newline at end of file diff --git a/include/vmhandlers.hpp b/include/vmhandlers.hpp new file mode 100644 index 0000000..a2927bf --- /dev/null +++ b/include/vmhandlers.hpp @@ -0,0 +1,38 @@ +#pragma once +#include +#include + +namespace vm +{ + namespace handler + { + struct handler_t + { + u8 imm_size; // size in bits... + vm::transform::map_t transforms; + vm::handler::profile_t *profile; + zydis_routine_t instrs; + std::uintptr_t address; + }; + + bool has_imm( const zydis_routine_t &vm_handler ); + std::optional< std::uint8_t > imm_size( const zydis_routine_t &vm_handler ); + bool get( zydis_routine_t &vm_entry, zydis_routine_t &vm_handler, std::uintptr_t handler_addr ); + + bool get_all( std::uintptr_t module_base, std::uintptr_t image_base, zydis_routine_t &vm_entry, + std::uintptr_t *vm_handler_table, std::vector< handler_t > &vm_handlers ); + + bool get_operand_transforms( zydis_routine_t &vm_handler, transform::map_t &transforms ); + vm::handler::profile_t *get_profile( handler_t &vm_handler ); + vm::handler::profile_t *get_profile( vm::handler::mnemonic_t mnemonic ); + + namespace table + { + std::uintptr_t *get( const zydis_routine_t &vm_entry ); + bool get_transform( const zydis_routine_t &vm_entry, zydis_decoded_instr_t *transform_instr ); + + std::uint64_t encrypt( zydis_decoded_instr_t &transform_instr, std::uint64_t val ); + std::uint64_t decrypt( zydis_decoded_instr_t &transform_instr, std::uint64_t val ); + } // namespace table + } // namespace handler +} // namespace vm \ No newline at end of file diff --git a/include/vminstrs.hpp b/include/vminstrs.hpp new file mode 100644 index 0000000..8e98a28 --- /dev/null +++ b/include/vminstrs.hpp @@ -0,0 +1,69 @@ +#pragma once +#include +#include +#include +#include + +namespace vm +{ + 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 ); + + /// + /// get virt_instr_t filled in with data given a vmp2 trace entry and vm context... + /// + /// current vm context + /// vmp2 trace entry containing all of the native/virtual register/stack values... + /// returns a filled in virt_instr_t on success... + 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 ); + + /// + /// get jcc data out of a code block... this function will loop over the code block + /// and look for the last LCONSTDW in the virtual instructions. + /// + /// it will then loop and look for all PUSHVSP's, checking each to see if the stack + /// contains two encrypted rva's to each branch.. if there is not two encrypted rva's + /// then the virtual jmp instruction only has one dest... + /// + /// vm context + /// code block that does not have its jcc_data yet + /// if last lconstdw is found, return filled in jcc_data structure... + std::optional< jcc_data > get_jcc_data( vm::ctx_t &ctx, code_block_t &code_block ); + + /// + /// the top of the stack will contain the lower 32bits of the RVA to the virtual instructions + /// that will be jumping too... the RVA is image based (not module based, but optional header image + /// based)... this means the value ontop of the stack could be "40007fd8" with image base being + /// 0x140000000... as you can see the 0x100000000 is missing... the below statement deals with this... + /// + /// vm context + /// current trace entry for virtual JMP instruction + /// returns linear virtual address of the next code block... + std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const vmp2::v2::entry_t &entry ); + + /// + /// same routine as above except lower_32bits is passed directly and not extracted from the stack... + /// + /// vm context + /// lower 32bits of the relative virtual address... + /// returns full linear virtual address of code block... + std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const std::uint32_t lower_32bits ); + } // namespace instrs +} // namespace vm \ No newline at end of file diff --git a/include/vmp2.hpp b/include/vmp2.hpp index decdb85..02520d2 100644 --- a/include/vmp2.hpp +++ b/include/vmp2.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #define VMP_MAGIC '2PMV' namespace vmp2 @@ -139,4 +140,88 @@ namespace vmp2 } vsp; }; } // namespace v2 -} +} // namespace vmp2 + +namespace vm +{ + 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 jcc_data + { + bool has_jcc; + jcc_type type; + std::uintptr_t block_addr[ 2 ]; + }; + + struct code_block_t + { + std::uintptr_t vip_begin; + jcc_data jcc; + std::vector< virt_instr_t > vinstrs; + }; + } // namespace instrs +} // namespace vm + +namespace vmp2 +{ + namespace v3 + { + struct file_header + { + u32 magic; // VMP2 + u64 epoch_time; + version_t version; + + u64 module_base; + u64 image_base; + u64 vm_entry_rva; + + u32 module_offset; + u32 module_size; + + u32 code_block_offset; + u32 code_block_count; + }; + + struct code_block_t + { + std::uintptr_t vip_begin; + std::uintptr_t next_block_offset; + vm::instrs::jcc_data jcc; + + // serialized from std::vector... + std::uint32_t vinstr_count; + vm::instrs::virt_instr_t vinstr[]; + }; + } // namespace v3 +} // namespace vmp2 diff --git a/include/vmprofiler.hpp b/include/vmprofiler.hpp index b1a5d9a..976d76f 100644 --- a/include/vmprofiler.hpp +++ b/include/vmprofiler.hpp @@ -1,340 +1,9 @@ #pragma once -#include #include +#include #include - -namespace vm -{ - namespace handler - { - using instr_callback_t = bool ( * )( const zydis_decoded_instr_t &instr ); - - enum mnemonic_t - { - INVALID, - LRFLAGS, - PUSHVSP, - MULQ, - DIVQ, - CALL, - JMP, - VMEXIT, - - SREGQ, - SREGDW, - SREGW, - - LREGQ, - LREGDW, - - LCONSTQ, - LCONSTBZXW, - LCONSTBSXQ, - LCONSTBSXDW, - LCONSTDWSXQ, - LCONSTWSXQ, - LCONSTWSXDW, - LCONSTDW, - LCONSTW, - - READQ, - READDW, - READW, - - WRITEQ, - WRITEDW, - WRITEW, - WRITEB, - - ADDQ, - ADDDW, - ADDW, - - SHLQ, - SHLDW, - - SHRQ, - SHRW, - - NANDQ, - NANDDW, - NANDW - }; - - enum extention_t - { - none, - sign_extend, - zero_extend - }; - - struct profile_t - { - const char *name; - mnemonic_t mnemonic; - u8 imm_size; - std::vector< instr_callback_t > signature; - extention_t extention; - }; - - struct handler_t - { - u8 imm_size; // size in bits... - vm::transform::map_t transforms; - vm::handler::profile_t *profile; - zydis_routine_t instrs; - std::uintptr_t address; - }; - - bool has_imm( const zydis_routine_t &vm_handler ); - std::uint8_t imm_size( const zydis_routine_t &vm_handler ); - bool get( zydis_routine_t &vm_entry, zydis_routine_t &vm_handler, std::uintptr_t handler_addr ); - - // may throw an exception... - bool get_all( std::uintptr_t module_base, std::uintptr_t image_base, zydis_routine_t &vm_entry, - std::uintptr_t *vm_handler_table, std::vector< handler_t > &vm_handlers ); - - // can be used on calc_jmp... - bool get_operand_transforms( const zydis_routine_t &vm_handler, transform::map_t &transforms ); - vm::handler::profile_t *get_profile( handler_t &vm_handler ); - vm::handler::profile_t *get_profile( vm::handler::mnemonic_t mnemonic ); - - namespace table - { - std::uintptr_t *get( const zydis_routine_t &vm_entry ); - bool get_transform( const zydis_routine_t &vm_entry, zydis_decoded_instr_t *transform_instr ); - - std::uint64_t encrypt( zydis_decoded_instr_t &transform_instr, std::uint64_t val ); - std::uint64_t decrypt( zydis_decoded_instr_t &transform_instr, std::uint64_t val ); - } // namespace table - - namespace profile - { - extern vm::handler::profile_t sregq; - extern vm::handler::profile_t sregdw; - extern vm::handler::profile_t sregw; - - extern vm::handler::profile_t lregq; - extern vm::handler::profile_t lregdw; - - extern vm::handler::profile_t lconstq; - extern vm::handler::profile_t lconstdw; - extern vm::handler::profile_t lconstw; - - extern vm::handler::profile_t lconstbzxw; - extern vm::handler::profile_t lconstbsxdw; - extern vm::handler::profile_t lconstbsxq; - extern vm::handler::profile_t lconstdwsxq; - extern vm::handler::profile_t lconstwsxq; - extern vm::handler::profile_t lconstwsxdw; - - extern vm::handler::profile_t addq; - extern vm::handler::profile_t adddw; - extern vm::handler::profile_t addw; - - extern vm::handler::profile_t shlq; - extern vm::handler::profile_t shldw; - - extern vm::handler::profile_t nandq; - extern vm::handler::profile_t nanddw; - extern vm::handler::profile_t nandw; - - extern vm::handler::profile_t writeq; - extern vm::handler::profile_t writedw; - extern vm::handler::profile_t writeb; - - extern vm::handler::profile_t readq; - extern vm::handler::profile_t readdw; - - extern vm::handler::profile_t shrq; - extern vm::handler::profile_t shrw; - - extern vm::handler::profile_t lrflags; - extern vm::handler::profile_t call; - extern vm::handler::profile_t pushvsp; - extern vm::handler::profile_t mulq; - extern vm::handler::profile_t divq; - extern vm::handler::profile_t jmp; - extern vm::handler::profile_t vmexit; - - 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, - - &shlq, &shldw, &writeq, &writedw, &writeb, &nandq, &nanddw, &nandw, - - &shrq, &shrw, &readq, &readdw, &mulq, &pushvsp, &divq, &jmp, - &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::uintptr_t image_size, - std::uintptr_t vm_entry_rva ); - - bool init(); - const std::uintptr_t module_base, image_base, vm_entry_rva, image_size; - vmp2::exec_type_t exec_type; - zydis_routine_t vm_entry, calc_jmp; - std::vector< vm::handler::handler_t > vm_handlers; - }; -} // namespace vm - -namespace vm -{ - 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 jcc_data - { - bool has_jcc; - jcc_type type; - std::uintptr_t block_addr[ 2 ]; - }; - - struct code_block_t - { - std::uintptr_t vip_begin; - jcc_data jcc; - std::vector< virt_instr_t > vinstrs; - }; - } // namespace instrs -} // namespace vm - -namespace vmp2 -{ - namespace v3 - { - struct file_header - { - u32 magic; // VMP2 - u64 epoch_time; - version_t version; - - u64 module_base; - u64 image_base; - u64 vm_entry_rva; - - u32 module_offset; - u32 module_size; - - u32 code_block_offset; - u32 code_block_count; - }; - - struct code_block_t - { - std::uintptr_t vip_begin; - std::uintptr_t next_block_offset; - vm::instrs::jcc_data jcc; - - // serialized from std::vector... - std::uint32_t vinstr_count; - vm::instrs::virt_instr_t vinstr[]; - }; - } // namespace v3 -} // namespace vmp2 - -namespace vm -{ - 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 ); - - /// - /// get virt_instr_t filled in with data given a vmp2 trace entry and vm context... - /// - /// current vm context - /// vmp2 trace entry containing all of the native/virtual register/stack values... - /// returns a filled in virt_instr_t on success... - 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 ); - - /// - /// get jcc data out of a code block... this function will loop over the code block - /// and look for the last two NANDW in the virtual instructions, then it will look - /// for the last LCONSTW which is the xor key... - /// - /// it will then loop and look for all PUSHVSP's, checking each to see if the stack - /// contains two encrypted rva's to each branch.. if there is not two encrypted rva's - /// then the virtual jmp instruction only has one dest... - /// - /// vm context - /// code block that does not have its jcc_data yet - /// if last lconstdw is found, return filled in jcc_data structure... - std::optional< jcc_data > get_jcc_data( vm::ctx_t &ctx, code_block_t &code_block ); - - /// - /// the top of the stack will contain the lower 32bits of the RVA to the virtual instructions - /// that will be jumping too... the RVA is image based (not module based, but optional header image - /// based)... this means the value ontop of the stack could be "40007fd8" with image base being - /// 0x140000000... as you can see the 0x100000000 is missing... the below statement deals with this... - /// - /// vm context - /// current trace entry for virtual JMP instruction - /// returns linear virtual address of the next code block... - std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const vmp2::v2::entry_t &entry ); - - /// - /// same routine as above except lower_32bits is passed directly and not extracted from the stack... - /// - /// vm context - /// lower 32bits of the relative virtual address... - /// returns full linear virtual address of code block... - std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const std::uint32_t lower_32bits ); - } // 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 +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/include/vmprofiles.hpp b/include/vmprofiles.hpp new file mode 100644 index 0000000..34d3d32 --- /dev/null +++ b/include/vmprofiles.hpp @@ -0,0 +1,138 @@ +#pragma once +#include + +namespace vm +{ + namespace handler + { + enum mnemonic_t + { + INVALID, + LRFLAGS, + PUSHVSP, + MULQ, + DIVQ, + CALL, + JMP, + VMEXIT, + + SREGQ, + SREGDW, + SREGW, + + LREGQ, + LREGDW, + + LCONSTQ, + LCONSTBZXW, + LCONSTBSXQ, + LCONSTBSXDW, + LCONSTDWSXQ, + LCONSTWSXQ, + LCONSTWSXDW, + LCONSTDW, + LCONSTW, + + READQ, + READDW, + READW, + + WRITEQ, + WRITEDW, + WRITEW, + WRITEB, + + ADDQ, + ADDDW, + ADDW, + + SHLQ, + SHLDW, + + SHRQ, + SHRW, + + NANDQ, + NANDDW, + NANDW + }; + + using zydis_callback_t = std::function; + + enum extention_t + { + none, + sign_extend, + zero_extend + }; + + struct profile_t + { + const char *name; + mnemonic_t mnemonic; + u8 imm_size; + std::vector< zydis_callback_t > signature; + extention_t extention; + }; + + namespace profile + { + extern vm::handler::profile_t sregq; + extern vm::handler::profile_t sregdw; + extern vm::handler::profile_t sregw; + + extern vm::handler::profile_t lregq; + extern vm::handler::profile_t lregdw; + + extern vm::handler::profile_t lconstq; + extern vm::handler::profile_t lconstdw; + extern vm::handler::profile_t lconstw; + + extern vm::handler::profile_t lconstbzxw; + extern vm::handler::profile_t lconstbsxdw; + extern vm::handler::profile_t lconstbsxq; + extern vm::handler::profile_t lconstdwsxq; + extern vm::handler::profile_t lconstwsxq; + extern vm::handler::profile_t lconstwsxdw; + + extern vm::handler::profile_t addq; + extern vm::handler::profile_t adddw; + extern vm::handler::profile_t addw; + + extern vm::handler::profile_t shlq; + extern vm::handler::profile_t shldw; + + extern vm::handler::profile_t nandq; + extern vm::handler::profile_t nanddw; + extern vm::handler::profile_t nandw; + + extern vm::handler::profile_t writeq; + extern vm::handler::profile_t writedw; + extern vm::handler::profile_t writeb; + + extern vm::handler::profile_t readq; + extern vm::handler::profile_t readdw; + + extern vm::handler::profile_t shrq; + extern vm::handler::profile_t shrw; + + extern vm::handler::profile_t lrflags; + extern vm::handler::profile_t call; + extern vm::handler::profile_t pushvsp; + extern vm::handler::profile_t mulq; + extern vm::handler::profile_t divq; + extern vm::handler::profile_t jmp; + extern vm::handler::profile_t vmexit; + + 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, + + &shlq, &shldw, &writeq, &writedw, &writeb, &nandq, &nanddw, &nandw, + + &shrq, &shrw, &readq, &readdw, &mulq, &pushvsp, &divq, &jmp, + &lrflags, &vmexit, &call }; + } // namespace profile + } // namespace handler +} // namespace vm \ No newline at end of file diff --git a/include/vmutils.h b/include/vmutils.hpp similarity index 81% rename from include/vmutils.h rename to include/vmutils.hpp index 9a9bac0..a118b16 100644 --- a/include/vmutils.h +++ b/include/vmutils.hpp @@ -1,9 +1,14 @@ #pragma once #include #include + +#include #include #include +#define NOMINMAX +#include + using u8 = unsigned char; using u16 = unsigned short; using u32 = unsigned int; @@ -34,6 +39,9 @@ namespace vm bool compare( zydis_register_t a, zydis_register_t b ); } // namespace reg + bool get_fetch_operand( const zydis_routine_t &routine, zydis_instr_t &fetch_instr ); + std::optional< zydis_routine_t::iterator > get_fetch_operand( zydis_routine_t &routine ); + void print( zydis_routine_t &routine ); void print( const zydis_decoded_instr_t &instr ); bool is_jmp( const zydis_decoded_instr_t &instr ); diff --git a/src/calc_jmp.cpp b/src/calc_jmp.cpp index e612da1..adbb115 100644 --- a/src/calc_jmp.cpp +++ b/src/calc_jmp.cpp @@ -4,25 +4,14 @@ namespace vm { namespace calc_jmp { - bool get( const zydis_routine_t &vm_entry, zydis_routine_t &calc_jmp ) + bool get( zydis_routine_t &vm_entry, zydis_routine_t &calc_jmp ) { - auto result = - std::find_if( vm_entry.begin(), vm_entry.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 ); - } ); + auto result = vm::util::get_fetch_operand( vm_entry ); - if ( result == vm_entry.end() ) + if ( !result.has_value() ) return false; - calc_jmp.insert( calc_jmp.end(), result, vm_entry.end() ); + calc_jmp.insert( calc_jmp.end(), result.value(), vm_entry.end() ); return true; } @@ -39,26 +28,26 @@ namespace vm if ( result == calc_jmp.end() ) return {}; - const auto instr = &result->instr; + const auto &instr = result->instr; - switch ( instr->mnemonic ) + switch ( instr.mnemonic ) { case ZYDIS_MNEMONIC_LEA: // if operand type is memory, then return advancement type // based off of the disposition value... (neg == backward, pos == forward) - if ( instr->operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY ) - return instr->operands[ 1 ].mem.disp.value > 0 ? vmp2::exec_type_t::forward - : vmp2::exec_type_t::backward; + if ( instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY ) + return instr.operands[ 1 ].mem.disp.value > 0 ? vmp2::exec_type_t::forward + : vmp2::exec_type_t::backward; break; case ZYDIS_MNEMONIC_ADD: - if ( instr->operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE ) - return instr->operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::forward - : vmp2::exec_type_t::backward; + if ( instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE ) + return instr.operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::forward + : vmp2::exec_type_t::backward; break; case ZYDIS_MNEMONIC_SUB: - if ( instr->operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE ) - return instr->operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::backward - : vmp2::exec_type_t::forward; + if ( instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE ) + return instr.operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::backward + : vmp2::exec_type_t::forward; break; case ZYDIS_MNEMONIC_INC: return vmp2::exec_type_t::forward; diff --git a/src/vmhandler.cpp b/src/vmhandler.cpp index 546785a..b745ef9 100644 --- a/src/vmhandler.cpp +++ b/src/vmhandler.cpp @@ -86,12 +86,13 @@ namespace vm const auto has_imm = vm::handler::has_imm( vm_handler_instrs ); const auto imm_size = vm::handler::imm_size( vm_handler_instrs ); - if ( has_imm && !vm::handler::get_operand_transforms( vm_handler_instrs, transforms ) ) + if ( has_imm && ( !vm::handler::get_operand_transforms( vm_handler_instrs, transforms ) || + !imm_size.has_value() ) ) return false; vm_handler.address = ( decrypt_val - image_base ) + module_base; vm_handler.instrs = vm_handler_instrs; - vm_handler.imm_size = imm_size; + vm_handler.imm_size = imm_size.value(); vm_handler.transforms = transforms; vm_handler.profile = vm::handler::get_profile( vm_handler ); vm_handlers.push_back( vm_handler ); @@ -102,65 +103,30 @@ namespace vm bool has_imm( const zydis_routine_t &vm_handler ) { - const auto result = - std::find_if( vm_handler.begin(), 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; - } ); - - return result != vm_handler.end(); + zydis_instr_t instr_data; + return vm::util::get_fetch_operand( vm_handler, instr_data ); } - std::uint8_t imm_size( const zydis_routine_t &vm_handler ) + std::optional< std::uint8_t > imm_size( const zydis_routine_t &vm_handler ) { - const auto result = - std::find_if( vm_handler.begin(), 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; - } ); + zydis_instr_t instr_data; + if ( !vm::util::get_fetch_operand( vm_handler, instr_data ) ) + return {}; - if ( result == vm_handler.end() ) - return 0u; - - return result->instr.operands[ 1 ].size; + return instr_data.instr.operands[ 1 ].size; } - bool get_operand_transforms( const zydis_routine_t &vm_handler, transform::map_t &transforms ) + bool get_operand_transforms( zydis_routine_t &vm_handler, transform::map_t &transforms ) { - auto imm_fetch = - std::find_if( vm_handler.begin(), 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; - } ); + auto imm_fetch = vm::util::get_fetch_operand( vm_handler ); - if ( imm_fetch == vm_handler.end() ) + if ( !imm_fetch.has_value() ) return false; // this finds the first transformation which looks like: // transform rax, rbx <--- note these registers can be smaller so we to64 them... auto transform_instr = - std::find_if( imm_fetch, vm_handler.end(), []( const zydis_instr_t &instr_data ) -> bool { + std::find_if( imm_fetch.value(), vm_handler.end(), []( const zydis_instr_t &instr_data ) -> bool { return vm::transform::valid( instr_data.instr.mnemonic ) && instr_data.instr.operands[ 0 ].actions & ZYDIS_OPERAND_ACTION_WRITE && util::reg::compare( instr_data.instr.operands[ 0 ].reg.value, ZYDIS_REGISTER_RAX ) && @@ -171,7 +137,7 @@ namespace vm return false; // look for a primer/instruction that alters RAX prior to the 5 transformations... - auto generic0 = std::find_if( imm_fetch, transform_instr, []( const zydis_instr_t &instr_data ) -> bool { + auto generic0 = std::find_if( imm_fetch.value(), transform_instr, []( const zydis_instr_t &instr_data ) -> bool { return vm::transform::valid( instr_data.instr.mnemonic ) && instr_data.instr.operands[ 0 ].actions & ZYDIS_OPERAND_ACTION_WRITE && util::reg::compare( instr_data.instr.operands[ 0 ].reg.value, ZYDIS_REGISTER_RAX ) && @@ -264,7 +230,7 @@ namespace vm if ( result == vm_entry.end() ) return nullptr; - std::uintptr_t ptr = 0u; + auto ptr = 0ull; ZydisCalcAbsoluteAddress( &result->instr, &result->instr.operands[ 1 ], result->addr, &ptr ); return reinterpret_cast< std::uintptr_t * >( ptr ); diff --git a/src/vminstrs.cpp b/src/vminstrs.cpp index 3301df3..fb241a5 100644 --- a/src/vminstrs.cpp +++ b/src/vminstrs.cpp @@ -50,10 +50,10 @@ namespace vm switch ( update_key.operands[ 0 ].size ) { case 8: - rolling_key = ( rolling_key & ~0xFFull ) + result; + rolling_key = ( rolling_key & ~std::numeric_limits< u8 >::max() ) + result; break; case 16: - rolling_key = ( rolling_key & ~0xFFFFull ) + result; + rolling_key = ( rolling_key & ~std::numeric_limits< u16 >::max() ) + result; break; default: rolling_key = result; @@ -79,14 +79,16 @@ namespace vm auto result = transform::apply( update_key.operands[ 0 ].size, update_key.mnemonic, rolling_key, operand ); - // make sure we update the rolling decryption key correctly... + // mov rax, al does not clear the top bits... + // mov rax, ax does not clear the top bits... + // mov rax, eax does clear the top bits... switch ( update_key.operands[ 0 ].size ) { case 8: - rolling_key = ( rolling_key & ~0xFFull ) + result; + rolling_key = ( rolling_key & ~std::numeric_limits< u8 >::max() ) + result; break; case 16: - rolling_key = ( rolling_key & ~0xFFFFull ) + result; + rolling_key = ( rolling_key & ~std::numeric_limits< u16 >::max() ) + result; break; default: rolling_key = result; @@ -163,11 +165,10 @@ namespace vm if ( !imm_size ) return {}; - std::uint64_t result = 0u; - if ( ctx.exec_type == vmp2::exec_type_t::forward ) - std::memcpy( &result, reinterpret_cast< void * >( vip ), imm_size / 8 ); - else // else the second operand is below vip... - std::memcpy( &result, reinterpret_cast< void * >( vip - ( imm_size / 8 ) ), imm_size / 8 ); + auto result = 0ull; + ctx.exec_type == vmp2::exec_type_t::forward + ? std::memcpy( &result, reinterpret_cast< void * >( vip ), imm_size / 8 ) + : std::memcpy( &result, reinterpret_cast< void * >( vip - ( imm_size / 8 ) ), imm_size / 8 ); return result; } @@ -181,6 +182,7 @@ namespace vm result.mnemonic_t = profile ? profile->mnemonic : vm::handler::INVALID; result.opcode = entry.handler_idx; result.trace_data = entry; + result.operand.has_imm = false; if ( vm_handler.imm_size ) { @@ -194,8 +196,6 @@ namespace vm result.operand.imm.u = vm::instrs::decrypt_operand( vm_handler.transforms, imm_val.value(), entry.decrypt_key ).first; } - else - result.operand.has_imm = false; return result; } @@ -266,12 +266,14 @@ namespace vm std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const vmp2::v2::entry_t &entry ) { - return ( ( entry.vsp.qword[ 0 ] & 0xFFFFFFFFull ) - ( ctx.image_base & 0xFFFFFFFFull ) ) + ctx.module_base; + return ( ( entry.vsp.qword[ 0 ] & std::numeric_limits< u32 >::max() ) - + ( ctx.image_base & std::numeric_limits< u32 >::max() ) ) + + ctx.module_base; } std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const std::uint32_t lower_32bits ) { - return ( lower_32bits - ( ctx.image_base & 0xFFFFFFFFull ) ) + ctx.module_base; + return ( lower_32bits - ( ctx.image_base & std::numeric_limits< u32 >::max() ) ) + ctx.module_base; } } // namespace instrs } // namespace vm \ No newline at end of file diff --git a/src/vmprofiles/add.cpp b/src/vmprofiles/add.cpp index 4d92c58..0700c81 100644 --- a/src/vmprofiles/add.cpp +++ b/src/vmprofiles/add.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/call.cpp b/src/vmprofiles/call.cpp index bb3af63..861c6d8 100644 --- a/src/vmprofiles/call.cpp +++ b/src/vmprofiles/call.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/div.cpp b/src/vmprofiles/div.cpp index e69c211..e459897 100644 --- a/src/vmprofiles/div.cpp +++ b/src/vmprofiles/div.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/jmp.cpp b/src/vmprofiles/jmp.cpp index dc2a94e..00d4298 100644 --- a/src/vmprofiles/jmp.cpp +++ b/src/vmprofiles/jmp.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/lconst.cpp b/src/vmprofiles/lconst.cpp index 9b7bbca..cd93080 100644 --- a/src/vmprofiles/lconst.cpp +++ b/src/vmprofiles/lconst.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/lflags.cpp b/src/vmprofiles/lflags.cpp index 5b84ef6..0317bea 100644 --- a/src/vmprofiles/lflags.cpp +++ b/src/vmprofiles/lflags.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/lreg.cpp b/src/vmprofiles/lreg.cpp index 265311d..93ccb9e 100644 --- a/src/vmprofiles/lreg.cpp +++ b/src/vmprofiles/lreg.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/mul.cpp b/src/vmprofiles/mul.cpp index d1a0cf9..29c22b3 100644 --- a/src/vmprofiles/mul.cpp +++ b/src/vmprofiles/mul.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/nand.cpp b/src/vmprofiles/nand.cpp index 0fbcec0..b92a051 100644 --- a/src/vmprofiles/nand.cpp +++ b/src/vmprofiles/nand.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/pushvsp.cpp b/src/vmprofiles/pushvsp.cpp index 2995c7f..5206bd4 100644 --- a/src/vmprofiles/pushvsp.cpp +++ b/src/vmprofiles/pushvsp.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/read.cpp b/src/vmprofiles/read.cpp index 31fc822..632cbd7 100644 --- a/src/vmprofiles/read.cpp +++ b/src/vmprofiles/read.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/shl.cpp b/src/vmprofiles/shl.cpp index 939507f..57bb435 100644 --- a/src/vmprofiles/shl.cpp +++ b/src/vmprofiles/shl.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/shr.cpp b/src/vmprofiles/shr.cpp index 610e2f7..41f1f78 100644 --- a/src/vmprofiles/shr.cpp +++ b/src/vmprofiles/shr.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/sreg.cpp b/src/vmprofiles/sreg.cpp index 294f736..8380479 100644 --- a/src/vmprofiles/sreg.cpp +++ b/src/vmprofiles/sreg.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/vmexit.cpp b/src/vmprofiles/vmexit.cpp index 1183fc0..76e1791 100644 --- a/src/vmprofiles/vmexit.cpp +++ b/src/vmprofiles/vmexit.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmprofiles/write.cpp b/src/vmprofiles/write.cpp index bf0b7ff..5041a29 100644 --- a/src/vmprofiles/write.cpp +++ b/src/vmprofiles/write.cpp @@ -1,4 +1,4 @@ -#include "../../include/vmprofiler.hpp" +#include namespace vm { diff --git a/src/vmutils.cpp b/src/vmutils.cpp index 432bbbc..85cb779 100644 --- a/src/vmutils.cpp +++ b/src/vmutils.cpp @@ -1,5 +1,4 @@ -#include "vmutils.h" -#include +#include namespace vm { @@ -18,13 +17,55 @@ namespace vm } } // namespace reg + bool get_fetch_operand( const zydis_routine_t &routine, zydis_instr_t &fetch_instr ) + { + const auto result = + std::find_if( routine.begin(), routine.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 == routine.end() ) + return false; + + fetch_instr = *result; + return true; + } + + std::optional< zydis_routine_t::iterator > get_fetch_operand( zydis_routine_t &routine ) + { + auto result = std::find_if( routine.begin(), routine.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 == routine.end() ) + return {}; + + return result; + } + void print( const zydis_decoded_instr_t &instr ) { char buffer[ 256 ]; ZydisFormatter formatter; ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL ); ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ), 0u ); - puts( buffer ); + std::puts( buffer ); } void print( zydis_routine_t &routine ) @@ -35,9 +76,8 @@ namespace vm for ( auto [ instr, raw, addr ] : routine ) { - std::printf( "> 0x%p ", addr ); ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ), addr ); - puts( buffer ); + std::printf( "> 0x%p %s", addr, buffer ); } } @@ -83,10 +123,10 @@ namespace vm while ( ZYAN_SUCCESS( ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( routine_addr ), 0x1000, &instr ) ) ) { - // detect if we have already been at this instruction... if so that means there is a loop and we are going - // to just return... - if (std::find_if( routine.begin(), routine.end(), [ & ]( const zydis_instr_t &zydis_instr ) -> bool { - return zydis_instr.addr == routine_addr; + // detect if we have already been at this instruction... if so that means there is a loop and we are + // going to just return... + if ( std::find_if( routine.begin(), routine.end(), [ & ]( const zydis_instr_t &zydis_instr ) -> bool { + return zydis_instr.addr == routine_addr; } ) != routine.end() ) return true; @@ -133,8 +173,9 @@ namespace vm { return reg::compare( op.reg.value, reg ); } + default: + break; } - return false; }; @@ -151,7 +192,7 @@ namespace vm for ( ; itr >= routine.begin(); --itr ) { const auto instruction = &itr->instr; - bool stop = false; + auto stop = false; if ( instruction->mnemonic == ZYDIS_MNEMONIC_JMP ) continue; @@ -172,10 +213,8 @@ namespace vm if ( opcode_size < 32 && op->size > opcode_size ) continue; - if ( op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE ) - op->actions &= ~ZYDIS_OPERAND_ACTION_MASK_WRITE; - else - stop = true; + op->actions &ZYDIS_OPERAND_ACTION_MASK_WRITE ? op->actions &= ~ZYDIS_OPERAND_ACTION_MASK_WRITE + : stop = true; } if ( !_writes( *instruction ) ) diff --git a/vmprofiler.vcxproj b/vmprofiler.vcxproj index e357f76..e3f447f 100644 --- a/vmprofiler.vcxproj +++ b/vmprofiler.vcxproj @@ -146,10 +146,15 @@ + + + + - + + diff --git a/vmprofiler.vcxproj.filters b/vmprofiler.vcxproj.filters index 7c02f19..f07da1c 100644 --- a/vmprofiler.vcxproj.filters +++ b/vmprofiler.vcxproj.filters @@ -164,7 +164,22 @@ Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files