still adding doxygen comments...

merge-requests/11/head
_xeroxz 3 years ago
parent 099a7e9c58
commit 08635457a7

@ -2,11 +2,22 @@
#include <transform.hpp>
#include <vmp2.hpp>
namespace vm
{
namespace calc_jmp
namespace vm::calc_jmp
{
/// <summary>
/// extracts calc_jmp out of vm_entry... you can learn about calc_jmp <a
/// href="https://back.engineering/17/05/2021/#calc_jmp">here</a>.
/// </summary>
/// <param name="vm_entry">pass by reference vm entry...</param>
/// <param name="calc_jmp">zydis_routine_t filled up with native instructions by this routine...</param>
/// <returns>returns truee if no errors happen...</returns>
bool get( zydis_routine_t &vm_entry, zydis_routine_t &calc_jmp );
/// <summary>
/// gets the advancement of the virtual instruction pointer... iterates over calc_jmp for LEA, MOV, INC, DEC, SUB,
/// ADD, ETC instructions and then decides which way VIP advances based upon this information...
/// </summary>
/// <param name="calc_jmp"></param>
/// <returns></returns>
std::optional< vmp2::exec_type_t > get_advancement( const zydis_routine_t &calc_jmp );
} // namespace calc_jmp
} // namespace vm
} // namespace vm::calc_jmp

@ -5,11 +5,15 @@
#include <stdexcept>
#include <vmutils.hpp>
namespace vm
namespace vm::transform
{
namespace transform
{
// taken from ida...
/// <summary>
/// rotate left template function take from IDA SDK...
/// </summary>
/// <typeparam name="T">type of data to rotate left...</typeparam>
/// <param name="value">value to rotate left</param>
/// <param name="count">number of bits to rotate left...</param>
/// <returns>returns the rotated value...</returns>
template < class T > inline T __ROL__( T value, int count )
{
const unsigned int nbits = sizeof( T ) * 8;
@ -33,42 +37,103 @@ namespace vm
return value;
}
// taken from ida...
/// <summary>
/// rotate left a one byte value...
/// </summary>
/// <param name="value">byte value</param>
/// <param name="count">number of bits to rotate</param>
/// <returns>return rotated value...</returns>
inline u8 __ROL1__( u8 value, int count )
{
return __ROL__( ( u8 )value, count );
}
/// <summary>
/// rotate left a two byte value...
/// </summary>
/// <param name="value">two byte value to rotate...</param>
/// <param name="count">number of bits to rotate...</param>
/// <returns>return rotated value...</returns>
inline u16 __ROL2__( u16 value, int count )
{
return __ROL__( ( u16 )value, count );
}
/// <summary>
/// rotate left a four byte value...
/// </summary>
/// <param name="value">four byte value to rotate...</param>
/// <param name="count">number of bits to shift...</param>
/// <returns>return rotated value...</returns>
inline u32 __ROL4__( u32 value, int count )
{
return __ROL__( ( u32 )value, count );
}
/// <summary>
/// rotate left an eight byte value...
/// </summary>
/// <param name="value">eight byte value...</param>
/// <param name="count">number of bits to shift...</param>
/// <returns>return rotated value...</returns>
inline u64 __ROL8__( u64 value, int count )
{
return __ROL__( ( u64 )value, count );
}
/// <summary>
/// rotate right a one byte value...
/// </summary>
/// <param name="value">one byte value...</param>
/// <param name="count">number of bits to shift...</param>
/// <returns>return rotated value...</returns>
inline u8 __ROR1__( u8 value, int count )
{
return __ROL__( ( u8 )value, -count );
}
/// <summary>
/// rotate right a two byte value...
/// </summary>
/// <param name="value">two byte value to rotate...</param>
/// <param name="count">number of bits to shift...</param>
/// <returns></returns>
inline u16 __ROR2__( u16 value, int count )
{
return __ROL__( ( u16 )value, -count );
}
/// <summary>
/// rotate right a four byte value...
/// </summary>
/// <param name="value">four byte value to rotate...</param>
/// <param name="count">number of bits to rotate...</param>
/// <returns>return rotated value...</returns>
inline u32 __ROR4__( u32 value, int count )
{
return __ROL__( ( u32 )value, -count );
}
/// <summary>
/// rotate right an eight byte value...
/// </summary>
/// <param name="value">eight byte value</param>
/// <param name="count">number of bits to rotate...</param>
/// <returns>return rotated value...</returns>
inline u64 __ROR8__( u64 value, int count )
{
return __ROL__( ( u64 )value, -count );
}
/// <summary>
/// transform function, such as ADD, SUB, BSWAP... etc...
/// </summary>
/// <typeparam name="T">returns the transform result...</typeparam>
template < typename T > using transform_t = std::function< T( T, T ) >;
/// <summary>
/// type of transformation...
/// </summary>
enum class type
{
generic0,
@ -79,6 +144,9 @@ namespace vm
update_key
};
/// <summary>
/// map of transform type to zydis decoded instruction of the transform...
/// </summary>
using map_t = std::map< transform::type, zydis_decoded_instr_t >;
template < class T >
@ -215,5 +283,4 @@ namespace vm
{
return instr->operand_count > 1 && ( instr->operands[ 1 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE );
}
} // namespace transform
} // namespace vm
} // namespace vm::transform

@ -1,20 +1,47 @@
#pragma once
#include <transform.hpp>
#include <vmp2.hpp>
#include <vmhandlers.hpp>
#include <vmp2.hpp>
namespace vm
{
/// <summary>
/// vm::ctx_t class is used to auto generate vm_entry, calc_jmp, and other per-vm entry information...
/// creating a vm::ctx_t object can make it easier to pass around information pertaining to a given vm entry...
/// </summary>
class ctx_t
{
public:
/// <summary>
/// default constructor for vm::ctx_t... all information for a given vm entry must be provided...
/// </summary>
/// <param name="module_base">the linear virtual address of the module base...</param>
/// <param name="image_base">image base from optional nt header... <a
/// href="https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64">IMAGE_OPTIONAL_HEADER64</a>...</param>
/// <param name="image_size">image size from optional nt header... <a
/// href="https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64">IMAGE_OPTIONAL_HEADER64</a>...</param>
/// <param name="vm_entry_rva">relative virtual address from the module base address to the first push prior to
/// a vm entry...</param>
explicit ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uintptr_t image_size,
std::uintptr_t vm_entry_rva );
/// <summary>
/// init all per-vm entry data such as vm_entry, calc_jmp, and vm handlers...
/// </summary>
/// <returns>returns true if no errors...</returns>
bool init();
const std::uintptr_t module_base, image_base, vm_entry_rva, image_size;
/// <summary>
/// the order in which VIP advances...
/// </summary>
vmp2::exec_type_t exec_type;
zydis_routine_t vm_entry, calc_jmp;
/// <summary>
/// all the vm handlers for the given vm entry...
/// </summary>
std::vector< vm::handler::handler_t > vm_handlers;
};
} // namespace vm

@ -2,37 +2,134 @@
#include <transform.hpp>
#include <vmprofiles.hpp>
namespace vm
{
namespace handler
namespace vm::handler
{
/// <summary>
/// handler_t contains all the information for a vm handler such as its immidate value size (zero if there is no
/// imm), the transformations applied to the imm to decrypt it (if any), a pointer to the profile (nullptr if
/// there is none), and other meta data...
/// </summary>
struct handler_t
{
u8 imm_size; // size in bits...
/// <summary>
/// imm size in bits, zero if no imm...
/// </summary>
u8 imm_size;
/// <summary>
/// transformations to decrypt imm...
/// </summary>
vm::transform::map_t transforms;
/// <summary>
/// pointer to the profile, nullptr if none...
/// </summary>
vm::handler::profile_t *profile;
/// <summary>
/// native instructions of the vm handler... (calc_jmp/check_vsp is removed from this)...
/// </summary>
zydis_routine_t instrs;
/// <summary>
/// linear virtual address to the vm handler...
/// </summary>
std::uintptr_t address;
};
/// <summary>
/// given a vm handler returns true if the vm handler decrypts an operand...
/// </summary>
/// <param name="vm_handler">const reference to a vm handler...</param>
/// <returns>returns true if the vm handler decrypts an operand, else false...</returns>
bool has_imm( const zydis_routine_t &vm_handler );
/// <summary>
/// gets the imm size of a vm handler...
/// </summary>
/// <param name="vm_handler">const reference to a vm handler...</param>
/// <returns>returns the imm size, otherwise returns an empty optional value...</returns>
std::optional< std::uint8_t > imm_size( const zydis_routine_t &vm_handler );
/// <summary>
/// gets a vm handler, puts all of the native instructions inside of the vm_handler param...
/// </summary>
/// <param name="vm_entry">reference to a zydis_routine_t containing the native instructions of a vm
/// entry...</param> <param name="vm_handler">reference to a zydis_routine_t that will get filled with the
/// native instructions of the vm handler...</param> <param name="handler_addr">linear virtual address to the
/// first instruction of the vm handler...</param> <returns>returns true if the native instructions of the vm
/// handler was extracted...</returns>
bool get( zydis_routine_t &vm_entry, zydis_routine_t &vm_handler, std::uintptr_t handler_addr );
/// <summary>
/// get all 256 vm handlers...
/// </summary>
/// <param name="module_base">linear virtual address of the module base...</param>
/// <param name="image_base">image base from optional nt header... <a
/// href="https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64">IMAGE_OPTIONAL_HEADER64</a>...</param>
/// <param name="vm_entry">zydis_routine_t containing the deobfuscated and flattened vm entry native
/// instructions...</param> <param name="vm_handler_table">linear virtual address to the vm handler
/// table...</param> <param name="vm_handlers">vector of handler_t's that will be filled with the vm
/// handlers...</param> <returns>returns true if all vm handlers were extracted, else false...</returns>
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 );
/// <summary>
/// get operand decryption instructions given a vm handler...
/// </summary>
/// <param name="vm_handler">reference to a zydis_routine_t containing the deobfuscated and flattened vm handler
/// native instructions...</param> <param name="transforms">reference to a transform::map_t that will get filled
/// up with the transforms needed to decrypt operands...</param> <returns>returns true if the transformations
/// were extracted successfully</returns>
bool get_operand_transforms( zydis_routine_t &vm_handler, transform::map_t &transforms );
/// <summary>
/// get a vm handler profile given a handler_t...
/// </summary>
/// <param name="vm_handler">reference to a handler_t structure that contains all the information of a given vm
/// handler...</param> <returns>returns a pointer to the vm profile, else a nullptr...</returns>
vm::handler::profile_t *get_profile( handler_t &vm_handler );
/// <summary>
/// get a vm handler profile given the mnemonic of the vm handler...
/// </summary>
/// <param name="mnemonic">mnemonic of the vm handler...</param>
/// <returns>returns a pointer to the profile if the given menmonic is implimented, else a nullptr...</returns>
vm::handler::profile_t *get_profile( vm::handler::mnemonic_t mnemonic );
namespace table
{
/// <summary>
/// get the linear virtual address of the vm handler table give a deobfuscated, flattened, vm entry...
/// </summary>
/// <param name="vm_entry">deobfuscated, flattened, vm entry...</param>
/// <returns>returns the linear virtual address of the vm handler table...</returns>
std::uintptr_t *get( const zydis_routine_t &vm_entry );
/// <summary>
/// get the single native instruction used to decrypt vm handler entries...
/// </summary>
/// <param name="vm_entry">reference to the deobfuscated, flattened, vm entry...</param>
/// <param name="transform_instr"></param>
/// <returns></returns>
bool get_transform( const zydis_routine_t &vm_entry, zydis_decoded_instr_t *transform_instr );
/// <summary>
/// encrypt a linear virtual address given the transformation that is used to decrypt the vm handler table
/// entry... this function will apply the inverse of the transformation so you dont need to get the inverse
/// yourself...
/// </summary>
/// <param name="transform_instr">reference to the transformation native instruction...</param>
/// <param name="val">value to be encrypted (linear virtual address)</param>
/// <returns>returns the encrypted value...</returns>
std::uint64_t encrypt( zydis_decoded_instr_t &transform_instr, std::uint64_t val );
/// <summary>
/// decrypts a vm handler table entry...
/// </summary>
/// <param name="transform_instr">transformation extracted from vm_entry that decrypts vm handler table
/// entries...</param> <param name="val">encrypted value to be decrypted...</param> <returns>returns the
/// decrypted value...</returns>
std::uint64_t decrypt( zydis_decoded_instr_t &transform_instr, std::uint64_t val );
} // namespace table
} // namespace handler
} // namespace vm
} // namespace vm::handler

@ -4,16 +4,38 @@
#include <vmhandlers.hpp>
#include <vmp2.hpp>
namespace vm
namespace vm::instrs
{
namespace instrs
{
// decrypt transformations for encrypted virtual instruction rva...
/// <summary>
/// gets the native instructions that are used to decrypt the relative virtual address to virtual instructions
/// located on the stack at RSP+0xA0... you can learn about this @link https://back.engineering/17/05/2021/#vm_entry
/// </summary>
/// <param name="vm_entry">pass by reference of the specific vm entry you want to get the decryption instructions
/// from...</param> <param name="transform_instrs">pass by reference vector that will be filled with the decryption
/// instructions...</param> <returns>returns true if the decryption instructions are extracted...</returns>
bool get_rva_decrypt( const zydis_routine_t &vm_entry, std::vector< zydis_decoded_instr_t > &transform_instrs );
/// <summary>
/// decrypt virtual instruction operand given the decryption transformations... you can read about these
/// transformations
/// @link https://back.engineering/17/05/2021/#operand-decryption
/// </summary>
/// <param name="transforms">decryption transformations...</param>
/// <param name="operand">encrypted virtual instruction operand...</param>
/// <param name="rolling_key">the decryption key (RBX)...</param>
/// <returns></returns>
std::pair< std::uint64_t, std::uint64_t > decrypt_operand( transform::map_t &transforms, std::uint64_t operand,
std::uint64_t rolling_key );
/// <summary>
/// encrypt a virtual instructions operand given the transformations to decrypt the operand... the transformations
/// are inversed by this functions so you dont need to worry about doing that.
///
/// you can learn about transformations @link https://back.engineering/17/05/2021/#operand-decryption
/// </summary>
/// <param name="transforms">transformations to decrypt operand, these transformations are inversed by the
/// function...</param> <param name="operand">operand to be encrypted...</param> <param
/// name="rolling_key">encryption key... (RBX)...</param> <returns></returns>
std::pair< std::uint64_t, std::uint64_t > encrypt_operand( transform::map_t &transforms, std::uint64_t operand,
std::uint64_t rolling_key );
@ -65,5 +87,4 @@ namespace vm
/// <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
} // namespace vm::instrs

@ -1,10 +1,14 @@
#pragma once
#include <transform.hpp>
namespace vm
{
namespace handler
/// <summary>
/// contains all information pertaining to vm handler identification...
/// </summary>
namespace vm::handler
{
/// <summary>
/// vm handler mnemonic... so you dont need to compare strings!
/// </summary>
enum mnemonic_t
{
INVALID,
@ -57,8 +61,14 @@ namespace vm
NANDW
};
/// <summary>
/// zydis callback lambda used to pattern match native instructions...
/// </summary>
using zydis_callback_t = std::function< bool( const zydis_decoded_instr_t &instr ) >;
/// <summary>
/// how sign extention is handled...
/// </summary>
enum extention_t
{
none,
@ -66,15 +76,40 @@ namespace vm
zero_extend
};
/// <summary>
/// pre defined vm handler profile containing all compiled time known information about a vm handler...
/// </summary>
struct profile_t
{
/// <summary>
/// name of the vm handler, such as JMP or LCONST...
/// </summary>
const char *name;
/// <summary>
/// the mnemonic of the vm handler... so you dont need to compare strings...
/// </summary>
mnemonic_t mnemonic;
/// <summary>
/// size, in bits, of the operand (imm)... if there is none then this will be zero...
/// </summary>
u8 imm_size;
/// <summary>
/// a vector of signatures used to compare native instructions against zydis aided signatures...
/// </summary>
std::vector< zydis_callback_t > signature;
/// <summary>
/// how sign extention of operands are handled...
/// </summary>
extention_t extention;
};
/// <summary>
/// contains all profiles defined, as well as a vector of all of the defined profiles...
/// </summary>
namespace profile
{
extern vm::handler::profile_t sregq;
@ -124,6 +159,9 @@ namespace vm
extern vm::handler::profile_t jmp;
extern vm::handler::profile_t vmexit;
/// <summary>
/// a vector of pointers to all defined vm handler profiles...
/// </summary>
inline std::vector< vm::handler::profile_t * > all = {
&sregq, &sregdw, &sregw, &lregq, &lregdw, &lconstq, &lconstbzxw, &lconstbsxdw,
&lconstbsxq, &lconstdwsxq, &lconstwsxq, &lconstwsxdw, &lconstdw, &lconstw, &addq, &adddw,
@ -134,5 +172,4 @@ namespace vm
&shrq, &shrw, &readq, &readdw, &mulq, &pushvsp, &divq, &jmp,
&lrflags, &vmexit, &call };
} // namespace profile
} // namespace handler
} // namespace vm
} // namespace vm::handler

@ -28,9 +28,7 @@ struct zydis_instr_t
using zydis_routine_t = std::vector< zydis_instr_t >;
namespace vm
{
namespace util
namespace vm::util
{
namespace reg
{
@ -48,5 +46,4 @@ namespace vm
bool flatten( zydis_routine_t &routine, std::uintptr_t routine_addr, bool keep_jmps = false );
void deobfuscate( zydis_routine_t &routine );
} // namespace util
} // namespace vm
} // namespace vm::util

@ -1,8 +1,6 @@
#include <vmprofiler.hpp>
namespace vm
{
namespace instrs
namespace vm::instrs
{
std::pair< std::uint64_t, std::uint64_t > decrypt_operand( transform::map_t &transforms, std::uint64_t operand,
std::uint64_t rolling_key )
@ -128,8 +126,7 @@ namespace vm
bool get_rva_decrypt( const zydis_routine_t &vm_entry, std::vector< zydis_decoded_instr_t > &transform_instrs )
{
// find mov esi, [rsp+0xA0]
auto result =
std::find_if( vm_entry.begin(), vm_entry.end(), []( const zydis_instr_t &instr_data ) -> bool {
auto result = std::find_if( vm_entry.begin(), vm_entry.end(), []( const zydis_instr_t &instr_data ) -> bool {
return instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr_data.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr_data.instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_ESI &&
@ -223,15 +220,14 @@ namespace vm
//
// look for PUSHVSP virtual instructions with two encrypted virtual
// instruction rva's ontop of the virtual stack...
result = std::find_if( code_block.vinstrs.rbegin(), code_block.vinstrs.rend(),
result = std::find_if(
code_block.vinstrs.rbegin(), code_block.vinstrs.rend(),
[ & ]( const vm::instrs::virt_instr_t &vinstr ) -> bool {
if ( auto profile = vm::handler::get_profile( vinstr.mnemonic_t );
profile && profile->mnemonic == vm::handler::PUSHVSP )
{
const auto possible_block_1 = code_block_addr(
vmctx, vinstr.trace_data.vsp.qword[ 0 ] ^ xor_key ),
possible_block_2 = code_block_addr(
vmctx, vinstr.trace_data.vsp.qword[ 1 ] ^ xor_key );
const auto possible_block_1 = code_block_addr( vmctx, vinstr.trace_data.vsp.qword[ 0 ] ^ xor_key ),
possible_block_2 = code_block_addr( vmctx, vinstr.trace_data.vsp.qword[ 1 ] ^ xor_key );
// if this returns too many false positives we might have to get
// our hands dirty and look into trying to emulate each branch
@ -275,5 +271,4 @@ namespace vm
{
return ( lower_32bits - ( ctx.image_base & std::numeric_limits< u32 >::max() ) ) + ctx.module_base;
}
} // namespace instrs
} // namespace vm
} // namespace vm::instrs
Loading…
Cancel
Save