Merge branch '_xeroxz' into 'master'

added another lifter (POPVSP)... renamed compute_zf/pf/etc to just "zf/pf/etc"...

See merge request vmp2/vmdevirt!7
merge-requests/8/merge
_xeroxz 3 years ago
commit 11b6bce49b

@ -41,6 +41,7 @@ set(vmdevirt_SOURCES "")
list(APPEND vmdevirt_SOURCES
"src/devirt_t.cpp"
"src/devirt_utils.cpp"
"src/lifters/add.cpp"
"src/lifters/div.cpp"
"src/lifters/jmp.cpp"
@ -49,6 +50,7 @@ list(APPEND vmdevirt_SOURCES
"src/lifters/lreg.cpp"
"src/lifters/mul.cpp"
"src/lifters/nand.cpp"
"src/lifters/popvsp.cpp"
"src/lifters/pushvsp.cpp"
"src/lifters/read.cpp"
"src/lifters/sflags.cpp"
@ -59,6 +61,7 @@ list(APPEND vmdevirt_SOURCES
"src/main.cpp"
"src/vmp_rtn_t.cpp"
"include/devirt_t.hpp"
"include/devirt_utils.hpp"
"include/vm_lifters.hpp"
"include/vmp_rtn_t.hpp"
)
@ -106,6 +109,7 @@ target_link_libraries(vmdevirt PRIVATE
LLVMX86Disassembler
LLVMX86Info
LLVMAsmParser
LLVMPasses
)
unset(CMKR_TARGET)

@ -28,7 +28,8 @@ link-libraries = [
"LLVMX86Desc",
"LLVMX86Disassembler",
"LLVMX86Info",
"LLVMAsmParser"
"LLVMAsmParser",
"LLVMPasses"
]
compile-definitions = [
"NOMINMAX"

@ -1 +1 @@
Subproject commit 05c98b1ef57c3375ffd455221a3be1be6110d4eb
Subproject commit 9b5f89f4881862598181163069c69f584257d3f0

@ -18,10 +18,12 @@
#include "llvm/Transforms/InstCombine/InstCombine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/GVN.h"
#include "llvm/Transforms/Utils.h"
#include "X86TargetMachine.h"
#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/OptimizationLevel.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetRegistry.h"
@ -63,10 +65,10 @@ namespace vm
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 *sf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *zf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *pf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *flags( llvm::Value *cf, llvm::Value *pf, llvm::Value *af, llvm::Value *zf, llvm::Value *sf,
llvm::Value *of );
};
} // namespace vm

@ -0,0 +1,18 @@
#include <vmprofiler.hpp>
namespace devirt
{
namespace util
{
/// <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 );
} // namespace util
} // namespace devirt

@ -36,6 +36,7 @@ namespace vm
static lifter_callback_t lregq, lregdw;
static lifter_callback_t pushvsp;
static lifter_callback_t popvsp;
static lifter_callback_t readq, readdw;
static lifter_callback_t nandq, nanddw;
static lifter_callback_t shrq;
@ -56,6 +57,7 @@ namespace vm
{ vm::handler::ADDW, &addw },
{ vm::handler::SHRQ, &shrq },
{ vm::handler::PUSHVSP, &pushvsp },
{ vm::handler::POPVSP, &popvsp },
{ vm::handler::SREGQ, &sregq },
{ vm::handler::SREGDW, &sregdw },
{ vm::handler::LREGQ, &lregq },

@ -67,7 +67,41 @@ namespace vm
bool devirt_t::compile( std::vector< std::uint8_t > &obj )
{
return true;
llvm::legacy::FunctionPassManager pass_mgr( llvm_module );
pass_mgr.add( llvm::createPromoteMemoryToRegisterPass() );
pass_mgr.add( llvm::createCFGSimplificationPass() );
pass_mgr.add( llvm::createSROAPass() );
pass_mgr.add( llvm::createLoopSimplifyCFGPass() );
pass_mgr.add( llvm::createNewGVNPass() );
pass_mgr.add( llvm::createReassociatePass() );
pass_mgr.add( llvm::createPartiallyInlineLibCallsPass() );
pass_mgr.add( llvm::createDeadCodeEliminationPass() );
pass_mgr.add( llvm::createCFGSimplificationPass() );
pass_mgr.add( llvm::createInstructionCombiningPass() );
pass_mgr.add( llvm::createFlattenCFGPass() );
for ( auto vmp_rtn : vmp_rtns )
pass_mgr.run( *vmp_rtn->llvm_fptr );
// compile to native x86_64....
llvm::TargetOptions opt;
llvm::SmallVector< char, 128 > buff;
llvm::raw_svector_ostream dest( buff );
llvm::legacy::PassManager pass;
std::string error;
auto target_triple = llvm::sys::getDefaultTargetTriple();
llvm_module->setTargetTriple( target_triple );
auto target = llvm::TargetRegistry::lookupTarget( target_triple, error );
auto reloc_model = llvm::Optional< llvm::Reloc::Model >();
auto target_machine = target->createTargetMachine( target_triple, "generic", "", opt, reloc_model );
llvm_module->setDataLayout( target_machine->createDataLayout() );
target_machine->addPassesToEmitFile( pass, dest, nullptr, llvm::CGFT_ObjectFile );
auto result = pass.run( *llvm_module );
obj.insert( obj.begin(), buff.begin(), buff.end() );
return result;
}
llvm::Function *devirt_t::lift( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > code_blocks )
@ -95,21 +129,21 @@ namespace vm
return vmp_rtn->llvm_fptr;
}
llvm::Value *devirt_t::compute_sf( std::uint8_t byte_size, llvm::Value *val )
llvm::Value *devirt_t::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 )
llvm::Value *devirt_t::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 )
llvm::Value *devirt_t::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,
@ -120,7 +154,7 @@ namespace vm
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 *devirt_t::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 );

@ -0,0 +1,54 @@
#include <devirt_utils.hpp>
namespace devirt
{
namespace util
{
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;
}
} // namespace util
} // namespace devirt

@ -24,12 +24,12 @@ namespace vm
auto s_of_bit = rtn->ir_builder->CreateExtractValue( s_add, { 1 } );
auto of = rtn->ir_builder->CreateZExt( s_of_bit, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ) );
auto sf = rtn->compute_sf( byte_size, u_sum );
auto zf = rtn->compute_zf( byte_size, u_sum );
auto sf = rtn->sf( byte_size, u_sum );
auto zf = rtn->zf( byte_size, u_sum );
auto pf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ),
0 ); // TODO make clean PF bit computation...
auto flags_calc = rtn->combine_flags(
auto flags_calc = rtn->flags(
cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ), zf, sf, of );
return flags_calc;

@ -7,11 +7,11 @@ namespace vm
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 );
auto sf = rtn->compute_sf( byte_size, result );
auto zf = rtn->compute_zf( byte_size, result );
auto sf = rtn->sf( byte_size, result );
auto zf = rtn->zf( byte_size, result );
auto pf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
return rtn->combine_flags( cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ),
return rtn->flags( cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ),
zf, sf, of );
}

@ -0,0 +1,16 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::popvsp =
[ & ]( 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_ptr = ir_builder->CreatePointerCast(
stack, llvm::PointerType::get( llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ), 0ull ) );
auto stack_ptr = ir_builder->CreateLoad( stack_ptr_ptr );
ir_builder->CreateStore( stack_ptr, vmp_rtn->stack );
};
}

@ -11,12 +11,12 @@ namespace vm
auto cf = rtn->ir_builder->CreateZExt( msb, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ) );
auto of =
rtn->compute_sf( byte_size, lhs ); // we reuse the compute_sf helper since the flag expression is the same
auto sf = rtn->compute_sf( byte_size, result );
auto zf = rtn->compute_zf( byte_size, result );
rtn->sf( byte_size, lhs ); // we reuse the compute_sf helper since the flag expression is the same
auto sf = rtn->sf( byte_size, result );
auto zf = rtn->zf( byte_size, result );
auto pf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
return rtn->combine_flags( cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ),
return rtn->flags( cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ),
zf, sf, of );
}

@ -1,64 +1,15 @@
#include <Windows.h>
#include <cli-parser.hpp>
#include <devirt_t.hpp>
#include <devirt_utils.hpp>
#include <xtils.hpp>
/// <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[] )
{
argparse::argument_parser_t parser( "vmdevirt", "virtual instruction pseudo code generator" );
parser.add_argument().name( "--vmp2file" ).required( true ).description( "path to .vmp2 file..." );
parser.add_argument().name( "--bin" ).required( true ).description(
"path to the image in which to apply devirtualized code too...\n" );
parser.enable_help();
auto err = parser.parse( argc, argv );
@ -87,7 +38,7 @@ int main( int argc, const char *argv[] )
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;
if ( !serialize_vmp2( virt_rtns, vmp2file ) )
if ( !devirt::util::serialize_vmp2( virt_rtns, vmp2file ) )
{
std::printf( "> failed to serialize vmp2 file...\n" );
return false;
@ -97,18 +48,30 @@ int main( int argc, const char *argv[] )
llvm::Module llvm_module( "VMProtect 2 Devirtualization", llvm_ctx );
std::vector< std::uint8_t > compiled_obj;
vm::devirt_t vmp_rtn( &llvm_ctx, &llvm_module );
vm::devirt_t vmp_devirt( &llvm_ctx, &llvm_module );
for ( auto &[ vm_enter_offset, vmp2_code_blocks ] : virt_rtns )
{
if ( !vmp_rtn.lift( vm_enter_offset + file_header->image_base, vmp2_code_blocks ) )
if ( !vmp_devirt.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;
}
std::printf( "> lifted rtn_0x%p, number of blocks = %d\n", vm_enter_offset + file_header->image_base,
vmp2_code_blocks.size() );
}
vmp_rtn.compile( compiled_obj );
std::printf( "> compiled obj size = %d\n", compiled_obj.size() );
llvm::LLVMInitializeX86TargetInfo();
llvm::LLVMInitializeX86Target();
llvm::LLVMInitializeX86TargetMC();
llvm::LLVMInitializeX86AsmParser();
llvm::LLVMInitializeX86AsmPrinter();
vmp_devirt.compile( compiled_obj );
std::printf( "> compiled all routines... compiled obj size = %d\n", compiled_obj.size() );
std::ofstream( "devirt.o", std::ios::binary )
.write( reinterpret_cast< const char * >( compiled_obj.data() ), compiled_obj.size() );
}

@ -27,13 +27,10 @@ namespace vm
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,

Loading…
Cancel
Save