Merge branch '_xeroxz' into 'master'

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

See merge request vmp2/vmdevirt!8
merge-requests/9/merge
_xeroxz 3 years ago
commit 8217c63389

@ -22,8 +22,8 @@
#include "X86TargetMachine.h" #include "X86TargetMachine.h"
#include "llvm/Pass.h" #include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/OptimizationLevel.h" #include "llvm/Passes/OptimizationLevel.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/FileSystem.h" #include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h" #include "llvm/Support/Host.h"
#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetRegistry.h"
@ -48,17 +48,17 @@ namespace vm
friend class lifters_t; friend class lifters_t;
public: 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 ); 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 ); bool compile( std::vector< std::uint8_t > &obj );
private: private:
llvm::LLVMContext *llvm_ctx; llvm::LLVMContext *llvm_ctx;
llvm::Module *llvm_module; llvm::Module *llvm_module;
vmp2::v4::file_header *vmp2_file;
std::shared_ptr< llvm::IRBuilder<> > ir_builder; std::shared_ptr< llvm::IRBuilder<> > ir_builder;
std::vector< std::shared_ptr< vm::vmp_rtn_t > > vmp_rtns; 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 ); void push( std::uint8_t byte_size, llvm::Value *input_val );
llvm::Value *pop( std::uint8_t byte_size ); llvm::Value *pop( std::uint8_t byte_size );
@ -69,6 +69,6 @@ namespace vm
llvm::Value *zf( 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 *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 *flags( llvm::Value *cf, llvm::Value *pf, llvm::Value *af, llvm::Value *zf, llvm::Value *sf,
llvm::Value *of ); llvm::Value *of );
}; };
} // namespace vm } // namespace vm

@ -1,4 +1,9 @@
#include <vmprofiler.hpp> #include <vmprofiler.hpp>
#include <coff/image.hpp>
#define VM_ENTER_NAME "vmenter_"
#define VM_EXIT_NAME "vmexit_"
#define VM_RTN_NAME "rtn_"
namespace devirt 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::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > &virt_rtns,
std::vector< std::uint8_t > &vmp2file ); std::vector< std::uint8_t > &vmp2file );
} // namespace util } // 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 } // namespace devirt

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <vmprofiler.hpp> #include <vmprofiler.hpp>
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/BasicBlock.h" #include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h" #include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h" #include "llvm/IR/DataLayout.h"
@ -22,12 +23,14 @@ namespace vm
llvm::Function *llvm_fptr; llvm::Function *llvm_fptr;
llvm::AllocaInst *flags, *stack; llvm::AllocaInst *flags, *stack;
llvm::Module *llvm_module; llvm::Module *llvm_module;
vmp2::v4::file_header *vmp2_file;
std::uintptr_t rtn_begin; std::uintptr_t rtn_begin;
std::shared_ptr< llvm::IRBuilder<> > ir_builder; std::shared_ptr< llvm::IRBuilder<> > ir_builder;
std::vector< llvm::AllocaInst * > virtual_registers; std::vector< llvm::AllocaInst * > virtual_registers;
std::vector< std::pair< std::uintptr_t, llvm::BasicBlock * > > llvm_code_blocks; std::vector< std::pair< std::uintptr_t, llvm::BasicBlock * > > llvm_code_blocks;
std::vector< vm::instrs::code_block_t > vmp2_code_blocks; std::vector< vm::instrs::code_block_t > vmp2_code_blocks;
void create_virtual_registers( void ); void create_virtual_registers( void );
void create_routine( void ); void create_routine( void );
@ -36,6 +39,7 @@ namespace vm
public: public:
explicit vmp_rtn_t( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks, 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 } // namespace vm

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

@ -51,4 +51,18 @@ namespace devirt
return true; return true;
} }
} // namespace util } // 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 } // namespace devirt

@ -2,10 +2,60 @@
namespace vm namespace vm
{ {
lifters_t::lifter_callback_t lifters_t::vmexit = lifters_t::lifter_callback_t lifters_t::vmexit = [ & ]( vm::devirt_t *rtn,
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, const vm::instrs::code_block_t &vm_code_block,
llvm::IRBuilder<> *ir_builder ) { const vm::instrs::virt_instr_t &vinstr,
auto &vmp_rtn = rtn->vmp_rtns.back(); llvm::IRBuilder<> *ir_builder ) {
ir_builder->CreateRet( ir_builder->CreateLoad( vmp_rtn->stack ) ); 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();
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; return 0;
} }
std::vector< std::uint8_t > vmp2file; std::vector< std::uint8_t > vmp2file, bin;
const auto umtils = xtils::um_t::get_instance(); const auto umtils = xtils::um_t::get_instance();
if ( !umtils->open_binary_file( parser.get< std::string >( "vmp2file" ), vmp2file ) ) if ( !umtils->open_binary_file( parser.get< std::string >( "vmp2file" ), vmp2file ) )
@ -35,6 +35,12 @@ int main( int argc, const char *argv[] )
return -1; 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() ); 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< 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 ); llvm::Module llvm_module( "VMProtect 2 Devirtualization", llvm_ctx );
std::vector< std::uint8_t > compiled_obj; 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 ) 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 ); vmp_devirt.compile( compiled_obj );
std::printf( "> compiled all routines... compiled obj size = %d\n", compiled_obj.size() ); std::printf( "> compiled all routines... compiled obj size = %d\n", compiled_obj.size() );
std::ofstream( "devirt.o", std::ios::binary ) std::ofstream( "devirt.o", std::ios::binary )
.write( reinterpret_cast< const char * >( compiled_obj.data() ), compiled_obj.size() ); .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 namespace vm
{ {
vmp_rtn_t::vmp_rtn_t( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks, 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 ), : 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_routine();
// create virtual registers in the first code block... // create virtual registers in the first code block...
@ -45,5 +46,53 @@ namespace vm
{ vmp2_code_block.vip_begin, { vmp2_code_block.vip_begin,
llvm::BasicBlock::Create( ir_builder->getContext(), blk_name.str().c_str(), llvm_fptr ) } ); 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 } // namespace vm
Loading…
Cancel
Save