updated vmexit so that it gens an exit stub... also handed vm enters...

just need to append a new ".devirt" section to the original binary...
merge-requests/8/head
_xeroxz 3 years ago
parent d94ff6126d
commit 28f44ed761

@ -22,8 +22,8 @@
#include "X86TargetMachine.h"
#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/OptimizationLevel.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetRegistry.h"
@ -48,17 +48,17 @@ namespace vm
friend class lifters_t;
public:
explicit devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module );
explicit devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module, vmp2::v4::file_header * vmp2_file );
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;
vmp2::v4::file_header *vmp2_file;
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 );

@ -1,4 +1,9 @@
#include <vmprofiler.hpp>
#include <coff/image.hpp>
#define VM_ENTER_NAME "vmenter_"
#define VM_EXIT_NAME "vmexit_"
#define VM_RTN_NAME "rtn_"
namespace devirt
{
@ -15,4 +20,12 @@ namespace devirt
std::vector< std::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > &virt_rtns,
std::vector< std::uint8_t > &vmp2file );
} // namespace util
/// <summary>
/// append devirtualized functions to the original binary... patches vm enter jmps to devirtualized code...
/// </summary>
/// <param name="obj">compiled obj file... generated by llvm...</param>
/// <param name="bin">original binary in a vector... this binary gets patched...</param>
/// <returns>returns true if new .devirt section has been appended and all linking was successful...</returns>
bool append( std::vector< std::uint8_t > &obj, std::vector< std::uint8_t > &bin );
} // namespace devirt

@ -1,6 +1,7 @@
#pragma once
#include <vmprofiler.hpp>
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
@ -22,12 +23,14 @@ namespace vm
llvm::Function *llvm_fptr;
llvm::AllocaInst *flags, *stack;
llvm::Module *llvm_module;
vmp2::v4::file_header *vmp2_file;
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 );
@ -36,6 +39,7 @@ namespace vm
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 );
std::shared_ptr< llvm::IRBuilder<> > &ir_builder, llvm::Module *llvm_module,
vmp2::v4::file_header *vmp2_file );
};
} // namespace vm

@ -3,8 +3,8 @@
namespace vm
{
devirt_t::devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module )
: llvm_ctx( llvm_ctx ), llvm_module( llvm_module )
devirt_t::devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module, vmp2::v4::file_header *vmp2_file )
: llvm_ctx( llvm_ctx ), llvm_module( llvm_module ), vmp2_file( vmp2_file )
{
ir_builder = std::make_shared< llvm::IRBuilder<> >( *llvm_ctx );
}
@ -106,7 +106,8 @@ namespace vm
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 ) );
vmp_rtns.push_back(
std::make_shared< vm::vmp_rtn_t >( rtn_begin, code_blocks, ir_builder, llvm_module, vmp2_file ) );
auto &vmp_rtn = vmp_rtns.back();
auto lifters = vm::lifters_t::get_instance();
@ -154,8 +155,8 @@ namespace vm
return ir_builder->CreateCall( popcount_intrinsic, { extended_bits } );
}
llvm::Value *devirt_t::flags( llvm::Value *cf, llvm::Value *pf, llvm::Value *af, llvm::Value *zf,
llvm::Value *sf, llvm::Value *of )
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 );
auto shifted_af = ir_builder->CreateShl( llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 0 ),

@ -51,4 +51,18 @@ namespace devirt
return true;
}
} // namespace util
bool append( std::vector< std::uint8_t > &obj, std::vector< std::uint8_t > &bin )
{
if ( obj.empty() || bin.empty() )
return false;
// TODO: append a new section to the pe file...
// put the entire obj file into this new section...
// fix up the vmenter functions to jmp to their respective devirtualized routines...
//
// move relocation directory to the end of the module and append new relocations...
// export all of the new devirtualized/llvm generated functions...
return true;
}
} // namespace devirt

@ -2,10 +2,60 @@
namespace vm
{
lifters_t::lifter_callback_t lifters_t::vmexit =
[ & ]( vm::devirt_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::vmexit = [ & ]( vm::devirt_t *rtn,
const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
std::stringstream rtn_name;
llvm::Function *exit_func = nullptr;
rtn_name << "vmexit_" << std::hex << vinstr.trace_data.vm_handler_rva + rtn->vmp2_file->image_base;
if ( !( exit_func = rtn->llvm_module->getFunction( rtn_name.str() ) ) )
{
auto vmexit_func_type = llvm::FunctionType::get(
ir_builder->getVoidTy(), llvm::PointerType::getInt8PtrTy( ir_builder->getContext() ) );
exit_func = llvm::Function::Create( vmexit_func_type, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *rtn->llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", exit_func );
auto vmexit_handler_addr = reinterpret_cast< std::uintptr_t >( rtn->vmp2_file ) +
rtn->vmp2_file->module_offset + vinstr.trace_data.vm_handler_rva;
zydis_routine_t vmexit_handler;
vm::util::flatten( vmexit_handler, vmexit_handler_addr );
vm::util::deobfuscate( vmexit_handler );
std::string asm_str( "mov rsp, rcx; " );
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
for ( const auto &instr_data : vmexit_handler )
{
if ( instr_data.instr.mnemonic == ZYDIS_MNEMONIC_POP ||
instr_data.instr.mnemonic == ZYDIS_MNEMONIC_POPFQ )
{
char buffer[ 256 ];
ZydisFormatterFormatInstruction( &formatter, &instr_data.instr, buffer, sizeof( buffer ), 0ull );
asm_str.append( buffer ).append( "; " );
}
}
asm_str.append( "ret" );
auto ib = ir_builder->GetInsertBlock();
ir_builder->SetInsertPoint( entry_block );
auto exit_stub = llvm::InlineAsm::get( llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str,
"", false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( exit_stub );
ir_builder->CreateRetVoid();
ir_builder->SetInsertPoint( ib );
}
auto &vmp_rtn = rtn->vmp_rtns.back();
ir_builder->CreateRet( ir_builder->CreateLoad( vmp_rtn->stack ) );
auto stack_ptr = ir_builder->CreateLoad( vmp_rtn->stack );
ir_builder->CreateCall( exit_func, stack_ptr );
ir_builder->CreateRet( stack_ptr );
};
}

@ -26,7 +26,7 @@ int main( int argc, const char *argv[] )
return 0;
}
std::vector< std::uint8_t > vmp2file;
std::vector< std::uint8_t > vmp2file, bin;
const auto umtils = xtils::um_t::get_instance();
if ( !umtils->open_binary_file( parser.get< std::string >( "vmp2file" ), vmp2file ) )
@ -35,6 +35,12 @@ int main( int argc, const char *argv[] )
return -1;
}
if ( !umtils->open_binary_file( parser.get< std::string >( "bin" ), bin ) )
{
std::printf( "[!] failed to open original binary file...\n" );
return -1;
}
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;
@ -48,7 +54,7 @@ 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_devirt( &llvm_ctx, &llvm_module );
vm::devirt_t vmp_devirt( &llvm_ctx, &llvm_module, file_header );
for ( auto &[ vm_enter_offset, vmp2_code_blocks ] : virt_rtns )
{
@ -71,7 +77,9 @@ int main( int argc, const char *argv[] )
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() );
devirt::append( compiled_obj, bin );
std::ofstream( "devirt.exe", std::ios::binary ).write( reinterpret_cast< const char * >( bin.data() ), bin.size() );
}

@ -3,9 +3,10 @@
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 )
std::shared_ptr< llvm::IRBuilder<> > &ir_builder, llvm::Module *llvm_module,
vmp2::v4::file_header *vmp2_file )
: ir_builder( ir_builder ), vmp2_code_blocks( vmp2_code_blocks ), rtn_begin( rtn_begin ),
llvm_module( llvm_module )
llvm_module( llvm_module ), vmp2_file( vmp2_file )
{
create_routine();
// create virtual registers in the first code block...
@ -45,5 +46,53 @@ namespace vm
{ vmp2_code_block.vip_begin,
llvm::BasicBlock::Create( ir_builder->getContext(), blk_name.str().c_str(), llvm_fptr ) } );
}
zydis_routine_t vm_enter;
auto vm_enter_addr = reinterpret_cast< std::uintptr_t >( vmp2_file ) + vmp2_file->module_offset +
( rtn_begin - vmp2_file->image_base );
vm::util::flatten( vm_enter, vm_enter_addr );
vm::util::deobfuscate( vm_enter );
std::string asm_str;
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
for ( const auto &instr_data : vm_enter )
{
if ( instr_data.instr.mnemonic == ZYDIS_MNEMONIC_PUSH )
{
if ( instr_data.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_MEMORY )
{
asm_str.append( "mov rax, 0x1000000000000000; push rax; mov rax, 0x1000000000000000; push rax; " );
break;
}
char buffer[ 256 ];
ZydisFormatterFormatInstruction( &formatter, &instr_data.instr, buffer, sizeof( buffer ), 0ull );
asm_str.append( buffer ).append( "; " );
}
else if ( instr_data.instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ )
{
asm_str.append( "pushfq; " );
}
}
rtn_name.str( "" );
asm_str.append( "mov rcx, rsp; sub rsp, 0xA00; " );
rtn_name << "vmenter_" << std::hex << rtn_begin;
auto entry_func = llvm::Function::Create( llvm::FunctionType::get( ir_builder->getVoidTy(), false ),
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", entry_func );
ir_builder->SetInsertPoint( entry_block );
auto entry_stub = llvm::InlineAsm::get( llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str, "",
false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( entry_stub );
ir_builder->CreateRetVoid();
}
} // namespace vm
Loading…
Cancel
Save