cleaned the code a bunch, preparing for doxygen...

merge-requests/11/head
_xeroxz 4 years ago
parent 5c2d4397f5
commit 31f934cfb7

@ -0,0 +1,12 @@
#pragma once
#include <transform.hpp>
#include <vmp2.hpp>
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

@ -3,7 +3,7 @@
#include <functional> #include <functional>
#include <map> #include <map>
#include <stdexcept> #include <stdexcept>
#include <vmutils.h> #include <vmutils.hpp>
namespace vm namespace vm
{ {

@ -0,0 +1,20 @@
#pragma once
#include <transform.hpp>
#include <vmp2.hpp>
#include <vmhandlers.hpp>
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

@ -0,0 +1,38 @@
#pragma once
#include <transform.hpp>
#include <vmprofiles.hpp>
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

@ -0,0 +1,69 @@
#pragma once
#include <transform.hpp>
#include <vmctx.hpp>
#include <vmhandlers.hpp>
#include <vmp2.hpp>
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 );
/// <summary>
/// get virt_instr_t filled in with data given a vmp2 trace entry and vm context...
/// </summary>
/// <param name="ctx">current vm context</param>
/// <param name="entry">vmp2 trace entry containing all of the native/virtual register/stack values...</param>
/// <returns>returns a filled in virt_instr_t on success...</returns>
std::optional< virt_instr_t > get( vm::ctx_t &ctx, vmp2::v2::entry_t &entry );
/// <summary>
/// gets the second operand (imm) given vip and vm::ctx_t...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="imm_size">immediate value size in bits...</param>
/// <param name="vip">virtual instruction pointer, linear virtual address...</param>
/// <returns>returns immediate value if imm_size is not 0...</returns>
std::optional< std::uint64_t > get_imm( vm::ctx_t &ctx, std::uint8_t imm_size, std::uintptr_t vip );
/// <summary>
/// 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...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="code_block">code block that does not have its jcc_data yet</param>
/// <returns>if last lconstdw is found, return filled in jcc_data structure...</returns>
std::optional< jcc_data > get_jcc_data( vm::ctx_t &ctx, code_block_t &code_block );
/// <summary>
/// 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...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="entry">current trace entry for virtual JMP instruction</param>
/// <returns>returns linear virtual address of the next code block...</returns>
std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const vmp2::v2::entry_t &entry );
/// <summary>
/// same routine as above except lower_32bits is passed directly and not extracted from the stack...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="lower_32bits">lower 32bits of the relative virtual address...</param>
/// <returns>returns full linear virtual address of code block...</returns>
std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const std::uint32_t lower_32bits );
} // namespace instrs
} // namespace vm

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <transform.hpp> #include <transform.hpp>
#include <vmhandlers.hpp>
#define VMP_MAGIC '2PMV' #define VMP_MAGIC '2PMV'
namespace vmp2 namespace vmp2
@ -139,4 +140,88 @@ namespace vmp2
} vsp; } vsp;
}; };
} // namespace v2 } // 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<virt_instr_t>...
std::uint32_t vinstr_count;
vm::instrs::virt_instr_t vinstr[];
};
} // namespace v3
} // namespace vmp2

@ -1,340 +1,9 @@
#pragma once #pragma once
#include <optional>
#include <transform.hpp> #include <transform.hpp>
#include <vmhandlers.hpp>
#include <vmp2.hpp> #include <vmp2.hpp>
#include <vmprofiles.hpp>
namespace vm #include <vminstrs.hpp>
{ #include <vmctx.hpp>
namespace handler #include <vmutils.hpp>
{ #include <calc_jmp.hpp>
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<virt_instr_t>...
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 );
/// <summary>
/// get virt_instr_t filled in with data given a vmp2 trace entry and vm context...
/// </summary>
/// <param name="ctx">current vm context</param>
/// <param name="entry">vmp2 trace entry containing all of the native/virtual register/stack values...</param>
/// <returns>returns a filled in virt_instr_t on success...</returns>
std::optional< virt_instr_t > get( vm::ctx_t &ctx, vmp2::v2::entry_t &entry );
/// <summary>
/// gets the second operand (imm) given vip and vm::ctx_t...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="imm_size">immediate value size in bits...</param>
/// <param name="vip">virtual instruction pointer, linear virtual address...</param>
/// <returns>returns immediate value if imm_size is not 0...</returns>
std::optional< std::uint64_t > get_imm( vm::ctx_t &ctx, std::uint8_t imm_size, std::uintptr_t vip );
/// <summary>
/// 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...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="code_block">code block that does not have its jcc_data yet</param>
/// <returns>if last lconstdw is found, return filled in jcc_data structure...</returns>
std::optional< jcc_data > get_jcc_data( vm::ctx_t &ctx, code_block_t &code_block );
/// <summary>
/// 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...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="entry">current trace entry for virtual JMP instruction</param>
/// <returns>returns linear virtual address of the next code block...</returns>
std::uintptr_t code_block_addr( const vm::ctx_t &ctx, const vmp2::v2::entry_t &entry );
/// <summary>
/// same routine as above except lower_32bits is passed directly and not extracted from the stack...
/// </summary>
/// <param name="ctx">vm context</param>
/// <param name="lower_32bits">lower 32bits of the relative virtual address...</param>
/// <returns>returns full linear virtual address of code block...</returns>
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

@ -0,0 +1,138 @@
#pragma once
#include <transform.hpp>
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<bool( const zydis_decoded_instr_t &instr )>;
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

@ -1,9 +1,14 @@
#pragma once #pragma once
#include <Zydis/Utils.h> #include <Zydis/Utils.h>
#include <Zydis/Zydis.h> #include <Zydis/Zydis.h>
#include <optional>
#include <vector> #include <vector>
#include <xmmintrin.h> #include <xmmintrin.h>
#define NOMINMAX
#include <Windows.h>
using u8 = unsigned char; using u8 = unsigned char;
using u16 = unsigned short; using u16 = unsigned short;
using u32 = unsigned int; using u32 = unsigned int;
@ -34,6 +39,9 @@ namespace vm
bool compare( zydis_register_t a, zydis_register_t b ); bool compare( zydis_register_t a, zydis_register_t b );
} // namespace reg } // 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( zydis_routine_t &routine );
void print( const zydis_decoded_instr_t &instr ); void print( const zydis_decoded_instr_t &instr );
bool is_jmp( const zydis_decoded_instr_t &instr ); bool is_jmp( const zydis_decoded_instr_t &instr );

@ -4,25 +4,14 @@ namespace vm
{ {
namespace calc_jmp 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 = auto result = vm::util::get_fetch_operand( vm_entry );
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 );
} );
if ( result == vm_entry.end() ) if ( !result.has_value() )
return false; 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; return true;
} }
@ -39,25 +28,25 @@ namespace vm
if ( result == calc_jmp.end() ) if ( result == calc_jmp.end() )
return {}; return {};
const auto instr = &result->instr; const auto &instr = result->instr;
switch ( instr->mnemonic ) switch ( instr.mnemonic )
{ {
case ZYDIS_MNEMONIC_LEA: case ZYDIS_MNEMONIC_LEA:
// if operand type is memory, then return advancement type // if operand type is memory, then return advancement type
// based off of the disposition value... (neg == backward, pos == forward) // based off of the disposition value... (neg == backward, pos == forward)
if ( instr->operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY ) if ( instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY )
return instr->operands[ 1 ].mem.disp.value > 0 ? vmp2::exec_type_t::forward return instr.operands[ 1 ].mem.disp.value > 0 ? vmp2::exec_type_t::forward
: vmp2::exec_type_t::backward; : vmp2::exec_type_t::backward;
break; break;
case ZYDIS_MNEMONIC_ADD: case ZYDIS_MNEMONIC_ADD:
if ( instr->operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE ) if ( instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE )
return instr->operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::forward return instr.operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::forward
: vmp2::exec_type_t::backward; : vmp2::exec_type_t::backward;
break; break;
case ZYDIS_MNEMONIC_SUB: case ZYDIS_MNEMONIC_SUB:
if ( instr->operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE ) if ( instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE )
return instr->operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::backward return instr.operands[ 1 ].imm.value.s > 0 ? vmp2::exec_type_t::backward
: vmp2::exec_type_t::forward; : vmp2::exec_type_t::forward;
break; break;
case ZYDIS_MNEMONIC_INC: case ZYDIS_MNEMONIC_INC:

@ -86,12 +86,13 @@ namespace vm
const auto has_imm = vm::handler::has_imm( vm_handler_instrs ); const auto has_imm = vm::handler::has_imm( vm_handler_instrs );
const auto imm_size = vm::handler::imm_size( 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; return false;
vm_handler.address = ( decrypt_val - image_base ) + module_base; vm_handler.address = ( decrypt_val - image_base ) + module_base;
vm_handler.instrs = vm_handler_instrs; 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.transforms = transforms;
vm_handler.profile = vm::handler::get_profile( vm_handler ); vm_handler.profile = vm::handler::get_profile( vm_handler );
vm_handlers.push_back( vm_handler ); vm_handlers.push_back( vm_handler );
@ -102,65 +103,30 @@ namespace vm
bool has_imm( const zydis_routine_t &vm_handler ) bool has_imm( const zydis_routine_t &vm_handler )
{ {
const auto result = zydis_instr_t instr_data;
std::find_if( vm_handler.begin(), vm_handler.end(), []( const zydis_instr_t &instr_data ) -> bool { return vm::util::get_fetch_operand( vm_handler, instr_data );
// 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();
} }
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 = zydis_instr_t instr_data;
std::find_if( vm_handler.begin(), vm_handler.end(), []( const zydis_instr_t &instr_data ) -> bool { if ( !vm::util::get_fetch_operand( vm_handler, instr_data ) )
// mov/movsx/movzx rax/eax/ax/al, [rsi] return {};
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() )
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 = auto imm_fetch = vm::util::get_fetch_operand( vm_handler );
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;
} );
if ( imm_fetch == vm_handler.end() ) if ( !imm_fetch.has_value() )
return false; return false;
// this finds the first transformation which looks like: // this finds the first transformation which looks like:
// transform rax, rbx <--- note these registers can be smaller so we to64 them... // transform rax, rbx <--- note these registers can be smaller so we to64 them...
auto transform_instr = 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 ) && return vm::transform::valid( instr_data.instr.mnemonic ) &&
instr_data.instr.operands[ 0 ].actions & ZYDIS_OPERAND_ACTION_WRITE && instr_data.instr.operands[ 0 ].actions & ZYDIS_OPERAND_ACTION_WRITE &&
util::reg::compare( instr_data.instr.operands[ 0 ].reg.value, ZYDIS_REGISTER_RAX ) && util::reg::compare( instr_data.instr.operands[ 0 ].reg.value, ZYDIS_REGISTER_RAX ) &&
@ -171,7 +137,7 @@ namespace vm
return false; return false;
// look for a primer/instruction that alters RAX prior to the 5 transformations... // 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 ) && return vm::transform::valid( instr_data.instr.mnemonic ) &&
instr_data.instr.operands[ 0 ].actions & ZYDIS_OPERAND_ACTION_WRITE && instr_data.instr.operands[ 0 ].actions & ZYDIS_OPERAND_ACTION_WRITE &&
util::reg::compare( instr_data.instr.operands[ 0 ].reg.value, ZYDIS_REGISTER_RAX ) && util::reg::compare( instr_data.instr.operands[ 0 ].reg.value, ZYDIS_REGISTER_RAX ) &&
@ -264,7 +230,7 @@ namespace vm
if ( result == vm_entry.end() ) if ( result == vm_entry.end() )
return nullptr; return nullptr;
std::uintptr_t ptr = 0u; auto ptr = 0ull;
ZydisCalcAbsoluteAddress( &result->instr, &result->instr.operands[ 1 ], result->addr, &ptr ); ZydisCalcAbsoluteAddress( &result->instr, &result->instr.operands[ 1 ], result->addr, &ptr );
return reinterpret_cast< std::uintptr_t * >( ptr ); return reinterpret_cast< std::uintptr_t * >( ptr );

@ -50,10 +50,10 @@ namespace vm
switch ( update_key.operands[ 0 ].size ) switch ( update_key.operands[ 0 ].size )
{ {
case 8: case 8:
rolling_key = ( rolling_key & ~0xFFull ) + result; rolling_key = ( rolling_key & ~std::numeric_limits< u8 >::max() ) + result;
break; break;
case 16: case 16:
rolling_key = ( rolling_key & ~0xFFFFull ) + result; rolling_key = ( rolling_key & ~std::numeric_limits< u16 >::max() ) + result;
break; break;
default: default:
rolling_key = result; rolling_key = result;
@ -79,14 +79,16 @@ namespace vm
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... // 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 ) switch ( update_key.operands[ 0 ].size )
{ {
case 8: case 8:
rolling_key = ( rolling_key & ~0xFFull ) + result; rolling_key = ( rolling_key & ~std::numeric_limits< u8 >::max() ) + result;
break; break;
case 16: case 16:
rolling_key = ( rolling_key & ~0xFFFFull ) + result; rolling_key = ( rolling_key & ~std::numeric_limits< u16 >::max() ) + result;
break; break;
default: default:
rolling_key = result; rolling_key = result;
@ -163,11 +165,10 @@ namespace vm
if ( !imm_size ) if ( !imm_size )
return {}; return {};
std::uint64_t result = 0u; auto result = 0ull;
if ( ctx.exec_type == vmp2::exec_type_t::forward ) 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 )
else // else the second operand is below vip... : std::memcpy( &result, reinterpret_cast< void * >( vip - ( imm_size / 8 ) ), imm_size / 8 );
std::memcpy( &result, reinterpret_cast< void * >( vip - ( imm_size / 8 ) ), imm_size / 8 );
return result; return result;
} }
@ -181,6 +182,7 @@ namespace vm
result.mnemonic_t = profile ? profile->mnemonic : vm::handler::INVALID; result.mnemonic_t = profile ? profile->mnemonic : vm::handler::INVALID;
result.opcode = entry.handler_idx; result.opcode = entry.handler_idx;
result.trace_data = entry; result.trace_data = entry;
result.operand.has_imm = false;
if ( vm_handler.imm_size ) if ( vm_handler.imm_size )
{ {
@ -194,8 +196,6 @@ namespace vm
result.operand.imm.u = result.operand.imm.u =
vm::instrs::decrypt_operand( vm_handler.transforms, imm_val.value(), entry.decrypt_key ).first; vm::instrs::decrypt_operand( vm_handler.transforms, imm_val.value(), entry.decrypt_key ).first;
} }
else
result.operand.has_imm = false;
return result; 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 ) 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 ) 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 instrs
} // namespace vm } // namespace vm

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,4 +1,4 @@
#include "../../include/vmprofiler.hpp" #include <vmprofiler.hpp>
namespace vm namespace vm
{ {

@ -1,5 +1,4 @@
#include "vmutils.h" #include <vmprofiler.hpp>
#include <Windows.h>
namespace vm namespace vm
{ {
@ -18,13 +17,55 @@ namespace vm
} }
} // namespace reg } // 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 ) void print( const zydis_decoded_instr_t &instr )
{ {
char buffer[ 256 ]; char buffer[ 256 ];
ZydisFormatter formatter; ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL ); ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ), 0u ); ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ), 0u );
puts( buffer ); std::puts( buffer );
} }
void print( zydis_routine_t &routine ) void print( zydis_routine_t &routine )
@ -35,9 +76,8 @@ namespace vm
for ( auto [ instr, raw, addr ] : routine ) for ( auto [ instr, raw, addr ] : routine )
{ {
std::printf( "> 0x%p ", addr );
ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ), addr ); ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ), addr );
puts( buffer ); std::printf( "> 0x%p %s", addr, buffer );
} }
} }
@ -83,8 +123,8 @@ namespace vm
while ( ZYAN_SUCCESS( while ( ZYAN_SUCCESS(
ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( routine_addr ), 0x1000, &instr ) ) ) 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 // detect if we have already been at this instruction... if so that means there is a loop and we are
// to just return... // going to just return...
if ( std::find_if( routine.begin(), routine.end(), [ & ]( const zydis_instr_t &zydis_instr ) -> bool { if ( std::find_if( routine.begin(), routine.end(), [ & ]( const zydis_instr_t &zydis_instr ) -> bool {
return zydis_instr.addr == routine_addr; return zydis_instr.addr == routine_addr;
} ) != routine.end() ) } ) != routine.end() )
@ -133,8 +173,9 @@ namespace vm
{ {
return reg::compare( op.reg.value, reg ); return reg::compare( op.reg.value, reg );
} }
default:
break;
} }
return false; return false;
}; };
@ -151,7 +192,7 @@ namespace vm
for ( ; itr >= routine.begin(); --itr ) for ( ; itr >= routine.begin(); --itr )
{ {
const auto instruction = &itr->instr; const auto instruction = &itr->instr;
bool stop = false; auto stop = false;
if ( instruction->mnemonic == ZYDIS_MNEMONIC_JMP ) if ( instruction->mnemonic == ZYDIS_MNEMONIC_JMP )
continue; continue;
@ -172,10 +213,8 @@ namespace vm
if ( opcode_size < 32 && op->size > opcode_size ) if ( opcode_size < 32 && op->size > opcode_size )
continue; continue;
if ( op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE ) op->actions &ZYDIS_OPERAND_ACTION_MASK_WRITE ? op->actions &= ~ZYDIS_OPERAND_ACTION_MASK_WRITE
op->actions &= ~ZYDIS_OPERAND_ACTION_MASK_WRITE; : stop = true;
else
stop = true;
} }
if ( !_writes( *instruction ) ) if ( !_writes( *instruction ) )

@ -146,10 +146,15 @@
<ClInclude Include="dependencies\zydis\include\Zydis\Status.h" /> <ClInclude Include="dependencies\zydis\include\Zydis\Status.h" />
<ClInclude Include="dependencies\zydis\include\Zydis\Utils.h" /> <ClInclude Include="dependencies\zydis\include\Zydis\Utils.h" />
<ClInclude Include="dependencies\zydis\include\Zydis\Zydis.h" /> <ClInclude Include="dependencies\zydis\include\Zydis\Zydis.h" />
<ClInclude Include="include\calc_jmp.hpp" />
<ClInclude Include="include\transform.hpp" /> <ClInclude Include="include\transform.hpp" />
<ClInclude Include="include\vmctx.hpp" />
<ClInclude Include="include\vmhandlers.hpp" />
<ClInclude Include="include\vminstrs.hpp" />
<ClInclude Include="include\vmp2.hpp" /> <ClInclude Include="include\vmp2.hpp" />
<ClInclude Include="include\vmprofiler.hpp" /> <ClInclude Include="include\vmprofiler.hpp" />
<ClInclude Include="include\vmutils.h" /> <ClInclude Include="include\vmprofiles.hpp" />
<ClInclude Include="include\vmutils.hpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include=".clang-format" /> <None Include=".clang-format" />

@ -164,7 +164,22 @@
<ClInclude Include="include\vmprofiler.hpp"> <ClInclude Include="include\vmprofiler.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\vmutils.h"> <ClInclude Include="include\vmutils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\vmhandlers.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\vmprofiles.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\vmctx.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\vminstrs.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\calc_jmp.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>

Loading…
Cancel
Save