Merge branch '_xeroxz' into 'master'

updated to vmp2 file format version 4... can devirt multiple routines at once now....

See merge request vmp2/vmdevirt!6
merge-requests/7/merge
_xeroxz 3 years ago
commit 547d1a78d6

@ -40,6 +40,7 @@ set(CMKR_TARGET vmdevirt)
set(vmdevirt_SOURCES "")
list(APPEND vmdevirt_SOURCES
"src/devirt_t.cpp"
"src/lifters/add.cpp"
"src/lifters/div.cpp"
"src/lifters/jmp.cpp"
@ -56,9 +57,10 @@ list(APPEND vmdevirt_SOURCES
"src/lifters/sreg.cpp"
"src/lifters/vmexit.cpp"
"src/main.cpp"
"src/vmp_rtn.cpp"
"src/vmp_rtn_t.cpp"
"include/devirt_t.hpp"
"include/vm_lifters.hpp"
"include/vmp_rtn.hpp"
"include/vmp_rtn_t.hpp"
)
list(APPEND vmdevirt_SOURCES
@ -86,6 +88,8 @@ target_compile_definitions(vmdevirt PRIVATE
target_include_directories(vmdevirt PRIVATE
include
"dependencies/llvm/llvm/include/"
"dependencies/llvm/llvm/lib/Target/X86/"
"build/dependencies/llvm/llvm/lib/Target/X86"
"build/dependencies/llvm/llvm/include/"
)
@ -96,6 +100,12 @@ target_link_libraries(vmdevirt PRIVATE
LLVMCodeGen
LLVMSupport
LLVMLinker
LLVMX86CodeGen
LLVMX86AsmParser
LLVMX86Desc
LLVMX86Disassembler
LLVMX86Info
LLVMAsmParser
)
unset(CMKR_TARGET)

@ -12,6 +12,8 @@ sources = [
include-directories = [
"include",
"dependencies/llvm/llvm/include/",
"dependencies/llvm/llvm/lib/Target/X86/",
"build/dependencies/llvm/llvm/lib/Target/X86",
"build/dependencies/llvm/llvm/include/",
]
link-libraries = [
@ -20,7 +22,13 @@ link-libraries = [
"LLVMCore",
"LLVMCodeGen",
"LLVMSupport",
"LLVMLinker"
"LLVMLinker",
"LLVMX86CodeGen",
"LLVMX86AsmParser",
"LLVMX86Desc",
"LLVMX86Disassembler",
"LLVMX86Info",
"LLVMAsmParser"
]
compile-definitions = [
"NOMINMAX"

@ -1 +1 @@
Subproject commit f06bf5f6b72b1d52a6156d6828675c77ceaee7e2
Subproject commit 05c98b1ef57c3375ffd455221a3be1be6110d4eb

@ -0,0 +1,72 @@
#pragma once
#include <vmp_rtn_t.hpp>
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Mangler.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Transforms/InstCombine/InstCombine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/GVN.h"
#include "X86TargetMachine.h"
#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
namespace llvm
{
extern "C" void LLVMInitializeX86TargetInfo();
extern "C" void LLVMInitializeX86Target();
extern "C" void LLVMInitializeX86TargetMC();
extern "C" void LLVMInitializeX86AsmParser();
extern "C" void LLVMInitializeX86AsmPrinter();
} // namespace llvm
namespace vm
{
class devirt_t
{
friend class lifters_t;
public:
explicit devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module );
llvm::Function *lift( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks );
bool compile( std::vector< std::uint8_t > &obj );
private:
llvm::LLVMContext *llvm_ctx;
llvm::Module *llvm_module;
std::shared_ptr< llvm::IRBuilder<> > ir_builder;
std::vector< std::shared_ptr< vm::vmp_rtn_t > > vmp_rtns;
void create_routine( void );
void push( std::uint8_t byte_size, llvm::Value *input_val );
llvm::Value *pop( std::uint8_t byte_size );
llvm::Value *load_value( std::uint8_t byte_size, llvm::GlobalValue *global );
llvm::Value *load_value( std::uint8_t byte_size, llvm::AllocaInst *var );
llvm::Value *compute_sf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *compute_zf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *compute_pf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *combine_flags( llvm::Value *cf, llvm::Value *pf, llvm::Value *af, llvm::Value *zf, llvm::Value *sf,
llvm::Value *of );
};
} // namespace vm

@ -1,7 +1,5 @@
#pragma once
#include <functional>
#include <vmp_rtn.hpp>
#include <vmprofiler.hpp>
#include <devirt_t.hpp>
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
@ -27,7 +25,7 @@ namespace vm
}
using lifter_callback_t =
std::function< void( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
std::function< void( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) >;
static lifter_callback_t lconstq, lconstdwsxq, lconstwsxq, lconstbzxw, lconstbsxq, lconstwsxdw, lconstdw,
@ -70,11 +68,10 @@ namespace vm
{ vm::handler::JMP, &jmp },
{ vm::handler::VMEXIT, &vmexit } };
static vm::llvm_value_t *and_flags( vm::vmp_rtn_t *rtn, std::uint8_t byte_size, vm::llvm_value_t *result );
static vm::llvm_value_t *add_flags( vm::vmp_rtn_t *rtn, std::uint8_t byte_size, vm::llvm_value_t *lhs,
vm::llvm_value_t *rhs );
static vm::llvm_value_t *shr_flags( vm::vmp_rtn_t *rtn, std::uint8_t byte_size, vm::llvm_value_t *lhs,
vm::llvm_value_t *rhs, vm::llvm_value_t *result );
static llvm::Value *and_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *result );
static llvm::Value *add_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs );
static llvm::Value *shr_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs,
llvm::Value *result );
public:
static lifters_t *get_instance( void )
@ -83,7 +80,7 @@ namespace vm
return &obj;
}
bool lift( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
bool lift( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder )
{
if ( vinstr.mnemonic_t == vm::handler::INVALID || lifters.find( vinstr.mnemonic_t ) == lifters.end() )

@ -1,71 +0,0 @@
#pragma once
#include <vmprofiler.hpp>
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
namespace vm
{
// Obsessive-compulsive disorder is characterized by unreasonable
// thoughts and fears (obsessions) that lead to compulsive behaviors.
using llvm_type_t = llvm::Type;
using llvm_value_t = llvm::Value;
using llvm_module_t = llvm::Module;
using llvm_context_t = llvm::LLVMContext;
using llvm_function_t = llvm::Function;
using llvm_irbuilder_t = llvm::IRBuilder<>;
using llvm_alloca_inst_t = llvm::AllocaInst;
using llvm_basic_block_t = llvm::BasicBlock;
using llvm_global_value_t = llvm::GlobalValue;
class vmp_rtn_t
{
friend class lifters_t;
public:
explicit vmp_rtn_t( llvm_context_t *llvm_ctx, llvm_module_t *llvm_module, vm::ctx_t *vm_ctx,
std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks );
llvm_function_t *lift( void );
private:
llvm_context_t *llvm_ctx;
llvm_module_t *llvm_module;
llvm_function_t *llvm_fptr;
llvm_alloca_inst_t *flags, *stack;
vm::ctx_t *vm_ctx;
std::uintptr_t rtn_begin;
std::shared_ptr< llvm_irbuilder_t > ir_builder;
std::vector< llvm_alloca_inst_t * > virtual_registers;
std::vector< std::pair< std::uintptr_t, llvm_basic_block_t * > > llvm_code_blocks;
std::vector< vm::instrs::code_block_t > vmp2_code_blocks;
void push( std::uint8_t byte_size, llvm_value_t *input_val );
llvm::Value *pop( std::uint8_t byte_size );
llvm::Value *load_value( std::uint8_t byte_size, llvm_global_value_t *global );
llvm::Value *load_value( std::uint8_t byte_size, llvm_alloca_inst_t *var );
llvm_value_t *compute_sf( std::uint8_t byte_size, llvm_value_t *val );
llvm_value_t *compute_zf( std::uint8_t byte_size, llvm_value_t *val );
llvm_value_t *compute_pf( std::uint8_t byte_size, llvm_value_t *val );
llvm_value_t *combine_flags( llvm_value_t *cf, llvm_value_t *pf, llvm_value_t *af, llvm_value_t *zf,
llvm_value_t *sf, llvm_value_t *of );
void create_virtual_registers( void );
void create_routine( void );
};
} // namespace vm

@ -0,0 +1,41 @@
#pragma once
#include <vmprofiler.hpp>
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Mangler.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
namespace vm
{
class vmp_rtn_t
{
llvm::Function *llvm_fptr;
llvm::AllocaInst *flags, *stack;
llvm::Module *llvm_module;
std::uintptr_t rtn_begin;
std::shared_ptr< llvm::IRBuilder<> > ir_builder;
std::vector< llvm::AllocaInst * > virtual_registers;
std::vector< std::pair< std::uintptr_t, llvm::BasicBlock * > > llvm_code_blocks;
std::vector< vm::instrs::code_block_t > vmp2_code_blocks;
void create_virtual_registers( void );
void create_routine( void );
friend class devirt_t;
friend class lifters_t;
public:
explicit vmp_rtn_t( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks,
std::shared_ptr< llvm::IRBuilder<> > &ir_builder, llvm::Module *llvm_module );
};
} // namespace vm

@ -0,0 +1,141 @@
#include <devirt_t.hpp>
#include <vm_lifters.hpp>
namespace vm
{
devirt_t::devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module )
: llvm_ctx( llvm_ctx ), llvm_module( llvm_module )
{
ir_builder = std::make_shared< llvm::IRBuilder<> >( *llvm_ctx );
}
void devirt_t::push( std::uint8_t num_bytes, llvm::Value *val )
{
// sub rsp, num_bytes
auto current_rtn = vmp_rtns.back();
auto rsp_addr = ir_builder->CreateLoad( current_rtn->stack );
auto sub_rsp_val =
ir_builder->CreateGEP( ir_builder->getInt8Ty(), rsp_addr, ir_builder->getInt8( 0 - num_bytes ) );
ir_builder->CreateStore( sub_rsp_val, current_rtn->stack );
// mov [rsp], val
auto resized_new_rsp_addr = ir_builder->CreateBitCast(
sub_rsp_val, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), 0ull ) );
ir_builder->CreateStore( val, resized_new_rsp_addr );
}
llvm::Value *devirt_t::pop( std::uint8_t num_bytes )
{
// mov rax, [rsp]
auto current_rtn = vmp_rtns.back();
auto rsp_addr = ir_builder->CreateLoad( current_rtn->stack );
auto new_rsp_addr =
ir_builder->CreateGEP( ir_builder->getInt8Ty(), rsp_addr, ir_builder->getInt8( num_bytes ) );
auto resized_new_rsp_addr = ir_builder->CreateBitCast(
rsp_addr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), 0ull ) );
auto pop_val = ir_builder->CreateLoad( resized_new_rsp_addr );
ir_builder->CreateStore( new_rsp_addr, current_rtn->stack );
ir_builder->CreateStore( llvm::UndefValue::get( ir_builder->getInt8Ty() ), rsp_addr );
return pop_val;
}
llvm::Value *devirt_t::load_value( std::uint8_t byte_size, llvm::GlobalValue *var )
{
if ( byte_size * 8 != var->getType()->getPrimitiveSizeInBits() )
{
auto cast_ptr = ir_builder->CreatePointerCast(
var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr );
}
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
}
llvm::Value *devirt_t::load_value( std::uint8_t byte_size, llvm::AllocaInst *var )
{
if ( byte_size * 8 != var->getType()->getPrimitiveSizeInBits() )
{
auto cast_ptr = ir_builder->CreatePointerCast(
var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr );
}
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
}
bool devirt_t::compile( std::vector< std::uint8_t > &obj )
{
return true;
}
llvm::Function *devirt_t::lift( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > code_blocks )
{
vmp_rtns.push_back( std::make_shared< vm::vmp_rtn_t >( rtn_begin, code_blocks, ir_builder, llvm_module ) );
auto &vmp_rtn = vmp_rtns.back();
auto lifters = vm::lifters_t::get_instance();
for ( auto idx = 0u; idx < vmp_rtn->vmp2_code_blocks.size(); ++idx )
{
ir_builder->SetInsertPoint( vmp_rtn->llvm_code_blocks[ idx ].second );
for ( auto &vinstr : vmp_rtn->vmp2_code_blocks[ idx ].vinstrs )
{
if ( !lifters->lift( this, vmp_rtn->vmp2_code_blocks[ idx ], vinstr, ir_builder.get() ) )
{
std::printf(
"> failed to devirtualize virtual instruction with opcode = %d, handler table rva = 0x%x\n",
vinstr.opcode, vinstr.trace_data.regs.r12 - vinstr.trace_data.regs.r13 );
return nullptr;
}
}
}
return vmp_rtn->llvm_fptr;
}
llvm::Value *devirt_t::compute_sf( std::uint8_t byte_size, llvm::Value *val )
{
auto op_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto msb = ir_builder->CreateLShr( val, ( byte_size * 8 ) - 1 );
return ir_builder->CreateZExt( msb, llvm::IntegerType::get( *llvm_ctx, 64 ) );
}
llvm::Value *devirt_t::compute_zf( std::uint8_t byte_size, llvm::Value *val )
{
auto op_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto is_zero = ir_builder->CreateICmpEQ( val, llvm::ConstantInt::get( op_size, 0 ) );
return ir_builder->CreateZExt( is_zero, llvm::IntegerType::get( *llvm_ctx, 64 ) );
}
llvm::Value *devirt_t::compute_pf( std::uint8_t byte_size, llvm::Value *val )
{
auto operand_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto popcount_intrinsic = llvm::Intrinsic::getDeclaration( llvm_module, llvm::Intrinsic::ctpop,
{ llvm::IntegerType::get( *llvm_ctx, 64 ) } );
auto lower_bits = ir_builder->CreateIntCast( val, llvm::IntegerType::get( *llvm_ctx, 8 ), false );
auto extended_bits = ir_builder->CreateZExt( lower_bits, llvm::IntegerType::get( *llvm_ctx, 64 ) );
return ir_builder->CreateCall( popcount_intrinsic, { extended_bits } );
}
llvm::Value *devirt_t::combine_flags( llvm::Value *cf, llvm::Value *pf, llvm::Value *af, llvm::Value *zf,
llvm::Value *sf, llvm::Value *of )
{
auto shifted_pf = ir_builder->CreateShl( pf, 2, "shifted_pf", true, true );
auto shifted_af = ir_builder->CreateShl( llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 0 ),
4, "shifted_af", true, true ); // treat af as zero
auto shifted_zf = ir_builder->CreateShl( zf, 6, "shifted_zf", true, true );
auto shifted_sf = ir_builder->CreateShl( sf, 7, "shifted_sf", true, true );
auto shifted_of = ir_builder->CreateShl( of, 11, "shifted_of", true, true );
auto or1 = ir_builder->CreateOr( cf, shifted_of );
auto or2 = ir_builder->CreateOr( or1, shifted_zf );
auto or3 = ir_builder->CreateOr( or2, shifted_sf );
auto or4 = ir_builder->CreateOr( or3, shifted_af );
auto or5 = ir_builder->CreateOr( or4, shifted_pf );
return ir_builder->CreateXor( or5, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 514 ) );
}
} // namespace vm

@ -2,8 +2,7 @@
namespace vm
{
vm::llvm_value_t *lifters_t::add_flags( vm::vmp_rtn_t *rtn, std::uint8_t byte_size, vm::llvm_value_t *lhs,
vm::llvm_value_t *rhs )
llvm::Value *lifters_t::add_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs )
{
auto op_size = llvm::IntegerType::get( *rtn->llvm_ctx, byte_size * 8 );
std::vector< llvm::Type * > intrinsic_arg_types;
@ -37,42 +36,45 @@ namespace vm
}
lifters_t::lifter_callback_t lifters_t::addq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t3 = ir_builder->CreateAdd( t1, t2 );
rtn->push( 8, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::add_flags( rtn, 8, t1, t2 );
ir_builder->CreateStore( flags, rtn->flags );
rtn->push( 8, rtn->load_value( 8, rtn->flags ) );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::adddw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
auto t3 = ir_builder->CreateAdd( t1, t2 );
rtn->push( 4, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::add_flags( rtn, 4, t1, t2 );
ir_builder->CreateStore( flags, rtn->flags );
rtn->push( 8, rtn->load_value( 8, rtn->flags ) );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::addw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateAdd( t1, t2 );
rtn->push( 2, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::add_flags( rtn, 2, t1, t2 );
ir_builder->CreateStore( flags, rtn->flags );
rtn->push( 8, rtn->load_value( 8, rtn->flags ) );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -2,13 +2,13 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::jmp = [ & ]( vm::vmp_rtn_t *rtn,
const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr,
lifters_t::lifter_callback_t lifters_t::jmp =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
assert( vm_code_block.jcc.has_jcc, "[!] fatal error in vmemu... virtual block ending with jmp marked as"
" has_jcc = false... debug time!\n" );
auto &vmp_rtn = rtn->vmp_rtns.back();
if ( vm_code_block.jcc.type == vm::instrs::jcc_type::branching )
{
auto rva = rtn->pop( 8 );
@ -19,22 +19,22 @@ namespace vm
// find the first branch basic block...
auto bb1 =
std::find_if( rtn->llvm_code_blocks.begin(), rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm_basic_block_t * > &block_data ) -> bool {
std::find_if( vmp_rtn->llvm_code_blocks.begin(), vmp_rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm::BasicBlock * > &block_data ) -> bool {
return block_data.first == vm_code_block.jcc.block_addr[ 0 ];
} );
assert( bb1 != rtn->llvm_code_blocks.end(),
assert( bb1 != vmp_rtn->llvm_code_blocks.end(),
"[!] fatal error... unable to locate basic block for branching...\n" );
// find the second branch basic block...
auto bb2 =
std::find_if( rtn->llvm_code_blocks.begin(), rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm_basic_block_t * > &block_data ) -> bool {
std::find_if( vmp_rtn->llvm_code_blocks.begin(), vmp_rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm::BasicBlock * > &block_data ) -> bool {
return block_data.first == vm_code_block.jcc.block_addr[ 1 ];
} );
assert( bb2 != rtn->llvm_code_blocks.end(),
assert( bb2 != vmp_rtn->llvm_code_blocks.end(),
"[!] fatal error... unable to locate basic block for branching...\n" );
ir_builder->CreateCondBr( cmp, bb1->second, bb2->second );
@ -43,12 +43,14 @@ namespace vm
{
auto rva = rtn->pop( 8 );
auto bb_data =
std::find_if( rtn->llvm_code_blocks.begin(), rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm_basic_block_t * > &block_data ) -> bool {
std::find_if( vmp_rtn->llvm_code_blocks.begin(), vmp_rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm::BasicBlock * > &block_data ) -> bool {
return block_data.first == vm_code_block.jcc.block_addr[ 0 ];
} );
assert( bb_data != rtn->llvm_code_blocks.end(), "[!] fatal error... unable to locate basic block...\n" );
assert( bb_data != vmp_rtn->llvm_code_blocks.end(),
"[!] fatal error... unable to locate basic block...\n" );
ir_builder->CreateBr( bb_data->second );
}
};

@ -3,49 +3,49 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::lconstq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstdwsxq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstwsxq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstbsxq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstdw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstwsxdw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstbsxdw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstbzxw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 2, llvm::ConstantInt::get( ir_builder->getInt16Ty(), vinstr.operand.imm.u ) );
};

@ -3,9 +3,10 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::lflagsq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = rtn->pop( 8 );
ir_builder->CreateStore( flags, rtn->flags );
ir_builder->CreateStore( flags, vmp_rtn->flags );
};
}

@ -3,16 +3,18 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::lregq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto vreg = rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
rtn->push( 8, rtn->load_value( 8, vreg ) );
};
lifters_t::lifter_callback_t lifters_t::lregdw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto vreg = rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
rtn->push( 4, rtn->load_value( 4, vreg ) );
};
} // namespace vm

@ -2,7 +2,7 @@
namespace vm
{
vm::llvm_value_t *lifters_t::and_flags( vm::vmp_rtn_t *rtn, std::uint8_t byte_size, vm::llvm_value_t *result )
llvm::Value *lifters_t::and_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *result )
{
auto cf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
auto of = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
@ -16,7 +16,7 @@ namespace vm
}
lifters_t::lifter_callback_t lifters_t::nandq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
@ -27,13 +27,14 @@ namespace vm
auto t3 = ir_builder->CreateAnd( { t1_not, t2_not } );
rtn->push( 8, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = and_flags( rtn, 8, t3 );
ir_builder->CreateStore( flags, rtn->flags );
rtn->push( 8, rtn->load_value( 8, rtn->flags ) );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::nanddw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
@ -44,9 +45,10 @@ namespace vm
auto t3 = ir_builder->CreateAnd( { t1_not, t2_not } );
rtn->push( 4, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = and_flags( rtn, 4, t3 );
ir_builder->CreateStore( flags, rtn->flags );
rtn->push( 8, rtn->load_value( 8, rtn->flags ) );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -3,9 +3,10 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::pushvsp =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto stack = ir_builder->CreateLoad( rtn->stack );
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto stack = ir_builder->CreateLoad( vmp_rtn->stack );
auto stack_ptr = ir_builder->CreatePtrToInt( stack, ir_builder->getInt64Ty() );
rtn->push( 8, stack_ptr );
};

@ -3,7 +3,7 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::readq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt64Ty(), 0ull ) );
@ -12,7 +12,7 @@ namespace vm
};
lifters_t::lifter_callback_t lifters_t::readdw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt32Ty(), 0ull ) );

@ -3,8 +3,8 @@
namespace vm
{
// our undefined behavior is that we don't model cases where the shift count is zero...
vm::llvm_value_t *lifters_t::shr_flags( vm::vmp_rtn_t *rtn, std::uint8_t byte_size, vm::llvm_value_t *lhs,
vm::llvm_value_t *rhs, vm::llvm_value_t *result )
llvm::Value *lifters_t::shr_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs,
llvm::Value *result )
{
auto op_size = llvm::IntegerType::get( *rtn->llvm_ctx, byte_size * 8 );
auto msb = rtn->ir_builder->CreateLShr( lhs, ( byte_size * 8 ) - 1 );
@ -21,16 +21,17 @@ namespace vm
}
lifters_t::lifter_callback_t lifters_t::shrq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t2, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), false );
auto t4 = ir_builder->CreateLShr( t1, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::shr_flags( rtn, 8, t1, t3, t4 );
ir_builder->CreateStore( flags, rtn->flags );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, t4 );
rtn->push( 8, rtn->load_value( 8, rtn->flags ) );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -3,18 +3,20 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::sregq =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto vreg = rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
ir_builder->CreateStore( t1, vreg );
};
lifters_t::lifter_callback_t lifters_t::sregdw =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto vreg = rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
auto vregdw = ir_builder->CreatePointerCast(
vreg, llvm::PointerType::get( llvm::IntegerType::get( *rtn->llvm_ctx, 32 ), 0 ) );
ir_builder->CreateStore( t1, vregdw );

@ -3,6 +3,9 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::vmexit =
[ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { ir_builder->CreateRetVoid(); };
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
ir_builder->CreateRet( ir_builder->CreateLoad( vmp_rtn->stack ) );
};
}

@ -1,10 +1,59 @@
#include <Windows.h>
#include <cli-parser.hpp>
#include <vmp_rtn.hpp>
#include <vmprofiler.hpp>
#include <devirt_t.hpp>
#include <xtils.hpp>
using namespace llvm;
/// <summary>
/// helper function to serialize vmp2 file data to vm::instr::code_block's...
/// </summary>
/// <param name="virt_rtns">vector of pairs {vm enter offset, vector of code blocks} which gets filled up with
/// serialized data</param>
/// <param name="vmp2file">a vector of bytes containing the vmp2 file...</param>
/// <returns>returns true if serialization was successful</returns>
bool serialize_vmp2( std::vector< std::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > &virt_rtns,
std::vector< std::uint8_t > &vmp2file )
{
const auto file_header = reinterpret_cast< vmp2::v4::file_header * >( vmp2file.data() );
if ( file_header->version != vmp2::version_t::v4 )
{
std::printf( "[!] invalid vmp2 file version... this build uses v3...\n" );
return false;
}
auto first_rtn = reinterpret_cast< vmp2::v4::rtn_t * >( reinterpret_cast< std::uintptr_t >( file_header ) +
file_header->rtn_offset );
for ( auto [ rtn_block, rtn_idx ] = std::pair{ first_rtn, 0ull }; rtn_idx < file_header->rtn_count;
++rtn_idx, rtn_block = reinterpret_cast< vmp2::v4::rtn_t * >(
reinterpret_cast< std::uintptr_t >( rtn_block ) + rtn_block->size ) )
{
virt_rtns.push_back( { rtn_block->vm_enter_offset, {} } );
for ( auto [ code_block, block_idx ] = std::pair{ &rtn_block->code_blocks[ 0 ], 0ull };
block_idx < rtn_block->code_block_count;
++block_idx, code_block = reinterpret_cast< vmp2::v4::code_block_t * >(
reinterpret_cast< std::uintptr_t >( code_block ) + code_block->next_block_offset ) )
{
auto block_vinstrs = reinterpret_cast< vm::instrs::virt_instr_t * >(
reinterpret_cast< std::uintptr_t >( code_block ) + sizeof vmp2::v4::code_block_t +
( code_block->num_block_addrs * 8 ) );
vm::instrs::code_block_t _code_block{ code_block->vip_begin };
_code_block.jcc.has_jcc = code_block->has_jcc;
_code_block.jcc.type = code_block->jcc_type;
for ( auto idx = 0u; idx < code_block->num_block_addrs; ++idx )
_code_block.jcc.block_addr.push_back( code_block->branch_addr[ idx ] );
for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx )
_code_block.vinstrs.push_back( block_vinstrs[ idx ] );
virt_rtns.back().second.push_back( _code_block );
}
}
return true;
}
int main( int argc, const char *argv[] )
{
@ -35,47 +84,31 @@ int main( int argc, const char *argv[] )
return -1;
}
const auto file_header = reinterpret_cast< vmp2::v3::file_header * >( vmp2file.data() );
if ( file_header->version != vmp2::version_t::v3 )
{
std::printf( "[!] invalid vmp2 file version... this build uses v3...\n" );
return -1;
}
auto first_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( file_header ) +
file_header->code_block_offset );
const auto file_header = reinterpret_cast< vmp2::v4::file_header * >( vmp2file.data() );
std::vector< std::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > virt_rtns;
std::vector< vm::instrs::code_block_t > vmp_code_blocks;
// convert code blocks back to vm::instrs::code_block_t form...
for ( auto [ code_block, code_block_num ] = std::tuple{ first_block, 0u };
code_block_num < file_header->code_block_count;
code_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( code_block ) +
code_block->next_block_offset ),
++code_block_num )
if ( !serialize_vmp2( virt_rtns, vmp2file ) )
{
vm::instrs::code_block_t _code_block{ code_block->vip_begin, code_block->jcc };
std::for_each( code_block->vinstr, code_block->vinstr + code_block->vinstr_count,
[ & ]( const vm::instrs::virt_instr_t &vinstr ) { _code_block.vinstrs.push_back( vinstr ); } );
vmp_code_blocks.push_back( _code_block );
std::printf( "> failed to serialize vmp2 file...\n" );
return false;
}
LLVMContext llvm_ctx;
Module llvm_module( "", llvm_ctx );
llvm::LLVMContext llvm_ctx;
llvm::Module llvm_module( "VMProtect 2 Devirtualization", llvm_ctx );
vm::ctx_t vm_ctx( reinterpret_cast< std::uintptr_t >( file_header ) + file_header->module_offset,
file_header->image_base, file_header->module_size, file_header->vm_entry_rva );
std::vector< std::uint8_t > compiled_obj;
vm::devirt_t vmp_rtn( &llvm_ctx, &llvm_module );
if ( !vm_ctx.init() )
for ( auto &[ vm_enter_offset, vmp2_code_blocks ] : virt_rtns )
{
std::printf( "> failed to init vm::ctx_t... this can be for many reason... make sure you are using the correct "
"vmemu version for this project...\n" );
return false;
if ( !vmp_rtn.lift( vm_enter_offset + file_header->image_base, vmp2_code_blocks ) )
{
std::printf( "[!] failed to lift rtn_0x%p, please review the console...\n",
vm_enter_offset + file_header->image_base );
return -1;
}
}
vm::vmp_rtn_t vmp_rtn( &llvm_ctx, &llvm_module, &vm_ctx, first_block->vip_begin, vmp_code_blocks );
auto func = vmp_rtn.lift();
func->print( llvm::outs(), nullptr );
vmp_rtn.compile( compiled_obj );
std::printf( "> compiled obj size = %d\n", compiled_obj.size() );
}

@ -1,177 +0,0 @@
#include <vm_lifters.hpp>
#include <vmp_rtn.hpp>
namespace vm
{
vmp_rtn_t::vmp_rtn_t( llvm_context_t *llvm_ctx, llvm_module_t *llvm_module, vm::ctx_t *vm_ctx,
std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks )
: llvm_ctx( llvm_ctx ), llvm_module( llvm_module ), vm_ctx( vm_ctx ), rtn_begin( rtn_begin ),
vmp2_code_blocks( vmp2_code_blocks )
{
// do not change the ordering of these function calls...
create_routine();
ir_builder = std::make_shared< llvm_irbuilder_t >( *llvm_ctx );
ir_builder->SetInsertPoint( llvm_code_blocks[ 0 ].second );
flags = ir_builder->CreateAlloca( ir_builder->getInt64Ty(), nullptr, "flags" );
stack = ir_builder->CreateAlloca( llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ), nullptr, "sp" );
ir_builder->CreateStore( llvm_fptr->getArg( 0 ), stack );
create_virtual_registers();
}
void vmp_rtn_t::create_virtual_registers( void )
{
for ( auto idx = 0u; idx < 24; ++idx )
// allocate virtual register space...
virtual_registers.push_back(
ir_builder->CreateAlloca( llvm::IntegerType::get( *llvm_ctx, 64 ), nullptr,
( std::string( "vreg" ) + std::to_string( idx ) ).c_str() ) );
}
void vmp_rtn_t::create_routine( void )
{
// function has no arguments and returns void... maybe change this in the future as i learn
// more and more LLVM...
auto func_ty = llvm::FunctionType::get( llvm::Type::getVoidTy( *llvm_ctx ),
{ llvm::PointerType::getInt8PtrTy( *llvm_ctx ) }, false );
// convert the rtn_begin address to a hex string and prepend "rtn_" to it...
std::stringstream rtn_name;
rtn_name << "rtn_" << std::hex << rtn_begin;
llvm_fptr = llvm::Function::Create( func_ty, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *llvm_module );
for ( const auto &vmp2_code_block : vmp2_code_blocks )
{
// create basic block name... block_xxxxxxxx format...
std::stringstream blk_name;
blk_name << "blk_" << std::hex << vmp2_code_block.vip_begin;
llvm_code_blocks.push_back( { vmp2_code_block.vip_begin,
llvm::BasicBlock::Create( *llvm_ctx, blk_name.str().c_str(), llvm_fptr ) } );
}
}
void vmp_rtn_t::push( std::uint8_t num_bytes, llvm_value_t *val )
{
// sub rsp, num_bytes
auto rsp_addr = ir_builder->CreateLoad( stack );
auto rsp_i64 = ir_builder->CreatePtrToInt( rsp_addr, ir_builder->getInt64Ty() );
auto sub_rsp_val = ir_builder->CreateSub( rsp_i64, ir_builder->getInt64( num_bytes ) );
ir_builder->CreateStore(
ir_builder->CreateIntToPtr( sub_rsp_val, llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ) ), stack );
// mov [rsp], val
rsp_addr = ir_builder->CreateLoad( stack );
auto rsp_cast_ptr = ir_builder->CreatePointerCast(
rsp_addr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), false ) );
ir_builder->CreateStore( val, rsp_cast_ptr );
}
llvm_value_t *vmp_rtn_t::pop( std::uint8_t num_bytes )
{
// mov rax, [rsp]
auto rsp_addr = ir_builder->CreateLoad( stack );
auto rsp_cast_ptr = ir_builder->CreatePointerCast(
rsp_addr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), false ) );
auto pop_val = ir_builder->CreateLoad( rsp_cast_ptr );
// add rsp, num_bytes
auto rsp_i64 = ir_builder->CreatePtrToInt( rsp_addr, ir_builder->getInt64Ty() );
auto sub_rsp_val = ir_builder->CreateAdd( rsp_i64, ir_builder->getInt64( num_bytes ) );
auto sub_rsp_val_ptr =
ir_builder->CreateIntToPtr( sub_rsp_val, llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ) );
ir_builder->CreateStore( sub_rsp_val_ptr, stack );
return pop_val;
}
llvm_value_t *vmp_rtn_t::load_value( std::uint8_t byte_size, llvm_global_value_t *var )
{
if ( byte_size * 8 != var->getType()->getPrimitiveSizeInBits() )
{
auto cast_ptr = ir_builder->CreatePointerCast(
var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr );
}
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
}
llvm_value_t *vmp_rtn_t::load_value( std::uint8_t byte_size, llvm_alloca_inst_t *var )
{
if ( byte_size * 8 != var->getType()->getPrimitiveSizeInBits() )
{
auto cast_ptr = ir_builder->CreatePointerCast(
var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr );
}
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
}
llvm_function_t *vmp_rtn_t::lift( void )
{
auto &code_blocks = llvm_fptr->getBasicBlockList();
auto lifters = vm::lifters_t::get_instance();
for ( auto idx = 0u; idx < code_blocks.size(); ++idx )
{
ir_builder->SetInsertPoint( llvm_code_blocks[ idx ].second );
for ( auto &vinstr : vmp2_code_blocks[ idx ].vinstrs )
{
if ( !lifters->lift( this, vmp2_code_blocks[ idx ], vinstr, ir_builder.get() ) )
{
std::printf( "> failed to devirtualize virtual instruction with opcode = %d\n", vinstr.opcode );
llvm_module->print( llvm::outs(), nullptr );
return nullptr;
}
}
}
return llvm_fptr;
}
llvm_value_t *vmp_rtn_t::compute_sf( std::uint8_t byte_size, llvm_value_t *val )
{
auto op_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto msb = ir_builder->CreateLShr( val, ( byte_size * 8 ) - 1 );
return ir_builder->CreateZExt( msb, llvm::IntegerType::get( *llvm_ctx, 64 ) );
}
llvm_value_t *vmp_rtn_t::compute_zf( std::uint8_t byte_size, llvm_value_t *val )
{
auto op_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto is_zero = ir_builder->CreateICmpEQ( val, llvm::ConstantInt::get( op_size, 0 ) );
return ir_builder->CreateZExt( is_zero, llvm::IntegerType::get( *llvm_ctx, 64 ) );
}
llvm_value_t *vmp_rtn_t::compute_pf( std::uint8_t byte_size, llvm_value_t *val )
{
auto operand_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto popcount_intrinsic = llvm::Intrinsic::getDeclaration( llvm_module, llvm::Intrinsic::ctpop,
{ llvm::IntegerType::get( *llvm_ctx, 64 ) } );
auto lower_bits = ir_builder->CreateIntCast( val, llvm::IntegerType::get( *llvm_ctx, 8 ), false );
auto extended_bits = ir_builder->CreateZExt( lower_bits, llvm::IntegerType::get( *llvm_ctx, 64 ) );
return ir_builder->CreateCall( popcount_intrinsic, { extended_bits } );
}
llvm_value_t *vmp_rtn_t::combine_flags( llvm_value_t *cf, llvm_value_t *pf, llvm_value_t *af, llvm_value_t *zf,
llvm_value_t *sf, llvm_value_t *of )
{
auto shifted_pf = ir_builder->CreateShl( pf, 2, "shifted_pf", true, true );
auto shifted_af = ir_builder->CreateShl( llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 0 ),
4, "shifted_af", true, true ); // treat af as zero
auto shifted_zf = ir_builder->CreateShl( zf, 6, "shifted_zf", true, true );
auto shifted_sf = ir_builder->CreateShl( sf, 7, "shifted_sf", true, true );
auto shifted_of = ir_builder->CreateShl( of, 11, "shifted_of", true, true );
auto or1 = ir_builder->CreateOr( cf, shifted_of );
auto or2 = ir_builder->CreateOr( or1, shifted_zf );
auto or3 = ir_builder->CreateOr( or2, shifted_sf );
auto or4 = ir_builder->CreateOr( or3, shifted_af );
auto or5 = ir_builder->CreateOr( or4, shifted_pf );
return ir_builder->CreateXor( or5, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 514 ) );
}
} // namespace vm

@ -0,0 +1,52 @@
#include <vmp_rtn_t.hpp>
namespace vm
{
vmp_rtn_t::vmp_rtn_t( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks,
std::shared_ptr< llvm::IRBuilder<> > &ir_builder, llvm::Module *llvm_module )
: ir_builder( ir_builder ), vmp2_code_blocks( vmp2_code_blocks ), rtn_begin( rtn_begin ),
llvm_module( llvm_module )
{
create_routine();
// create virtual registers in the first code block...
ir_builder->SetInsertPoint( llvm_code_blocks[ 0 ].second );
flags = ir_builder->CreateAlloca( ir_builder->getInt64Ty(), nullptr, "flags" );
stack = ir_builder->CreateAlloca( llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ), nullptr, "sp" );
ir_builder->CreateStore( llvm_fptr->getArg( 0 ), stack );
create_virtual_registers();
}
void vmp_rtn_t::create_virtual_registers( void )
{
for ( auto idx = 0u; idx < 24; ++idx )
// allocate virtual register space...
virtual_registers.push_back(
ir_builder->CreateAlloca( llvm::IntegerType::get( ir_builder->getContext(), 64 ), nullptr,
( std::string( "vreg" ) + std::to_string( idx ) ).c_str() ) );
}
void vmp_rtn_t::create_routine( void )
{
// function has no arguments and returns void... maybe change this in the future as i learn
// more and more LLVM...
auto func_ty =
llvm::FunctionType::get( llvm::PointerType::getInt8PtrTy( ir_builder->getContext() ),
{ llvm::PointerType::getInt8PtrTy( ir_builder->getContext() ) }, false );
// convert the rtn_begin address to a hex string and prepend "rtn_" to it...
std::stringstream rtn_name;
rtn_name << "rtn_" << std::hex << rtn_begin;
llvm_fptr = llvm::Function::Create( func_ty, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *llvm_module );
for ( const auto &vmp2_code_block : vmp2_code_blocks )
{
// create basic block name... block_xxxxxxxx format...
std::stringstream blk_name;
blk_name << "blk_" << std::hex << vmp2_code_block.vip_begin;
llvm_code_blocks.push_back(
{ vmp2_code_block.vip_begin,
llvm::BasicBlock::Create( ir_builder->getContext(), blk_name.str().c_str(), llvm_fptr ) } );
}
}
} // namespace vm
Loading…
Cancel
Save