From 28f44ed761816336801ef0b45f08930bb657ad5e Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Tue, 17 Aug 2021 00:15:15 -0700 Subject: [PATCH] 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... --- include/devirt_t.hpp | 8 +++--- include/devirt_utils.hpp | 13 +++++++++ include/vmp_rtn_t.hpp | 6 +++- src/devirt_t.cpp | 11 +++---- src/devirt_utils.cpp | 14 +++++++++ src/lifters/vmexit.cpp | 62 ++++++++++++++++++++++++++++++++++++---- src/main.cpp | 14 +++++++-- src/vmp_rtn_t.cpp | 53 ++++++++++++++++++++++++++++++++-- 8 files changed, 160 insertions(+), 21 deletions(-) diff --git a/include/devirt_t.hpp b/include/devirt_t.hpp index a6184de..574123f 100644 --- a/include/devirt_t.hpp +++ b/include/devirt_t.hpp @@ -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 ); @@ -69,6 +69,6 @@ namespace vm 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 ); + llvm::Value *of ); }; } // namespace vm \ No newline at end of file diff --git a/include/devirt_utils.hpp b/include/devirt_utils.hpp index 90b122a..e3f34b0 100644 --- a/include/devirt_utils.hpp +++ b/include/devirt_utils.hpp @@ -1,4 +1,9 @@ #include +#include + +#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 + + /// + /// append devirtualized functions to the original binary... patches vm enter jmps to devirtualized code... + /// + /// compiled obj file... generated by llvm... + /// original binary in a vector... this binary gets patched... + /// returns true if new .devirt section has been appended and all linking was successful... + bool append( std::vector< std::uint8_t > &obj, std::vector< std::uint8_t > &bin ); } // namespace devirt \ No newline at end of file diff --git a/include/vmp_rtn_t.hpp b/include/vmp_rtn_t.hpp index a4aa92d..f3ebc31 100644 --- a/include/vmp_rtn_t.hpp +++ b/include/vmp_rtn_t.hpp @@ -1,6 +1,7 @@ #pragma once #include +#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 \ No newline at end of file diff --git a/src/devirt_t.cpp b/src/devirt_t.cpp index 4804b97..c13c4d7 100644 --- a/src/devirt_t.cpp +++ b/src/devirt_t.cpp @@ -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 ), diff --git a/src/devirt_utils.cpp b/src/devirt_utils.cpp index a810ec2..0caaa82 100644 --- a/src/devirt_utils.cpp +++ b/src/devirt_utils.cpp @@ -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 \ No newline at end of file diff --git a/src/lifters/vmexit.cpp b/src/lifters/vmexit.cpp index 8bc011d..1d60373 100644 --- a/src/lifters/vmexit.cpp +++ b/src/lifters/vmexit.cpp @@ -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, - llvm::IRBuilder<> *ir_builder ) { - auto &vmp_rtn = rtn->vmp_rtns.back(); - ir_builder->CreateRet( ir_builder->CreateLoad( vmp_rtn->stack ) ); - }; + 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(); + auto stack_ptr = ir_builder->CreateLoad( vmp_rtn->stack ); + ir_builder->CreateCall( exit_func, stack_ptr ); + ir_builder->CreateRet( stack_ptr ); + }; } diff --git a/src/main.cpp b/src/main.cpp index c300f99..01210f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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() ); } \ No newline at end of file diff --git a/src/vmp_rtn_t.cpp b/src/vmp_rtn_t.cpp index 2f7d910..5bca12e 100644 --- a/src/vmp_rtn_t.cpp +++ b/src/vmp_rtn_t.cpp @@ -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 \ No newline at end of file