You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vmdevirt/src/vmp_rtn.cpp

295 lines
13 KiB

#include <vm_lifters.hpp>
#include <vmp_rtn.hpp>
namespace vm
{
vmp_rtn_t::vmp_rtn_t( llvm::LLVMContext *llvm_ctx, llvm::Module *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 )
{
// create llvm::Function and llvm::BasicBlock's...
create_routine();
// set the insert point to the first code block...
ir_builder = std::make_shared< llvm::IRBuilder<> >( *llvm_ctx );
ir_builder->SetInsertPoint( llvm_code_blocks[ 0 ] );
// create native registers...
create_native_registers();
// create stack and stack pointer...
create_virtual_stack();
// lift vm enter pushes to llvm ir...
lift_vm_entry();
// create virtual registers...
create_virtual_registers();
}
void vmp_rtn_t::lift_vm_entry( void )
{
for ( const auto &instr : vm_ctx->vm_entry )
{
if ( instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSH )
{
// push [xxxxx] we know this is zero and the next push is the image base...
if ( instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_MEMORY )
{
push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), 0ull ) );
push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vm_ctx->image_base ) );
break; // dont make these if statements a switch case because we need to use this break...
}
else if ( instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE )
{
push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), instr.instr.operands[ 0 ].imm.value.u,
false ) );
}
else if ( instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER )
{
push( 8, load_value( 8, native_registers[ instr.instr.operands[ 0 ].reg.value ] ) );
}
}
else if ( instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ )
{
// just push 0 as the value itself wont matter...
push( 8, load_value( 8, native_registers[ ZYDIS_REGISTER_RFLAGS ] ) );
}
}
}
void vmp_rtn_t::create_native_registers( void )
{
llvm_module->getOrInsertGlobal( "rax", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RAX ] = llvm_module->getGlobalVariable( "rax" );
llvm_module->getOrInsertGlobal( "rbx", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RBX ] = llvm_module->getGlobalVariable( "rbx" );
llvm_module->getOrInsertGlobal( "rcx", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RCX ] = llvm_module->getGlobalVariable( "rcx" );
llvm_module->getOrInsertGlobal( "rdx", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RDX ] = llvm_module->getGlobalVariable( "rdx" );
llvm_module->getOrInsertGlobal( "rsi", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RSI ] = llvm_module->getGlobalVariable( "rsi" );
llvm_module->getOrInsertGlobal( "rdi", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RDI ] = llvm_module->getGlobalVariable( "rdi" );
llvm_module->getOrInsertGlobal( "rbp", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RBP ] = llvm_module->getGlobalVariable( "rbp" );
llvm_module->getOrInsertGlobal( "rsp", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RSP ] = llvm_module->getGlobalVariable( "rsp" );
llvm_module->getOrInsertGlobal( "r8", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R8 ] = llvm_module->getGlobalVariable( "r8" );
llvm_module->getOrInsertGlobal( "r9", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R9 ] = llvm_module->getGlobalVariable( "r9" );
llvm_module->getOrInsertGlobal( "r10", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R10 ] = llvm_module->getGlobalVariable( "r10" );
llvm_module->getOrInsertGlobal( "r11", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R11 ] = llvm_module->getGlobalVariable( "r11" );
llvm_module->getOrInsertGlobal( "r12", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R12 ] = llvm_module->getGlobalVariable( "r12" );
llvm_module->getOrInsertGlobal( "r13", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R13 ] = llvm_module->getGlobalVariable( "r13" );
llvm_module->getOrInsertGlobal( "r14", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R14 ] = llvm_module->getGlobalVariable( "r14" );
llvm_module->getOrInsertGlobal( "r15", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R15 ] = llvm_module->getGlobalVariable( "r15" );
llvm_module->getOrInsertGlobal( "rflags", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RFLAGS ] = llvm_module->getGlobalVariable( "rflags" );
}
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 ), 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( llvm::BasicBlock::Create( *llvm_ctx, blk_name.str().c_str(), llvm_fptr ) );
}
}
void vmp_rtn_t::push( std::uint8_t byte_size, llvm::Value *input_value )
{
// Load the current stack index into a temporary variable
auto current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), stack_ptr, false );
// Subtract the input value size from the current stack index
auto new_stack_index = ir_builder->CreateSub(
current_stack_index, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), byte_size ) );
// Store the newly calculated stack index into VSP
ir_builder->CreateStore( new_stack_index, stack_ptr );
// Get a pointer to the top byte of the stack
llvm::Value *i64_zero = llvm::ConstantInt::get( *llvm_ctx, llvm::APInt( 64, 0 ) );
llvm::Value *indices[ 2 ] = { i64_zero, new_stack_index };
auto stack_ptr = ir_builder->CreateInBoundsGEP( virtual_stack, llvm::ArrayRef< llvm::Value * >( indices, 2 ) );
// Store the input value at the calculated stack address
if ( byte_size == 1 )
{
ir_builder->CreateStore( input_value, stack_ptr );
}
else
{
// Cast the pointer so the stack item width matches the width of the input value
auto casted_ptr = ir_builder->CreatePointerCast(
stack_ptr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
ir_builder->CreateStore( input_value, casted_ptr );
}
}
llvm::Value *vmp_rtn_t::pop( std::uint8_t byte_size )
{
// Load the current stack index into a temporary variable
auto current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), stack_ptr, false );
// Get a pointer to the top byte of the stack
llvm::Value *i64_zero = llvm::ConstantInt::get( *llvm_ctx, llvm::APInt( 64, 0 ) );
llvm::Value *indices[ 2 ] = { i64_zero, current_stack_index };
auto stack_ptr = ir_builder->CreateInBoundsGEP( virtual_stack, llvm::ArrayRef< llvm::Value * >( indices, 2 ) );
// Read the value at the top of the stack
llvm::Value *output_value = nullptr;
if ( byte_size == 1 )
{
output_value = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 8 ), stack_ptr );
}
else
{
// Cast the current stack pointer so the stack item width matches the width of the output value
auto casted_ptr = ir_builder->CreatePointerCast(
stack_ptr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
output_value = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), casted_ptr );
}
// Subtract the input value size from the current stack index
auto new_stack_index = ir_builder->CreateAdd(
current_stack_index, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), byte_size ) );
// Store the newly calculated stack index into VSP
ir_builder->CreateStore( new_stack_index, this->stack_ptr );
return output_value;
}
llvm::Value *vmp_rtn_t::peek( std::uint8_t byte_size, std::uint8_t byte_offset )
{
// Load the current stack index into a temporary variable
auto current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), stack_ptr, false );
if ( byte_offset )
{
auto t1 = ir_builder->CreateAdd(
current_stack_index, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), byte_offset ) );
current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), t1, false );
}
// Get a pointer to the top byte of the stack + byte_offset (if any)
llvm::Value *i64_zero = llvm::ConstantInt::get( *llvm_ctx, llvm::APInt( 64, 0 ) );
llvm::Value *indices[ 2 ] = { i64_zero, current_stack_index };
auto stack_ptr = ir_builder->CreateInBoundsGEP( virtual_stack, llvm::ArrayRef< llvm::Value * >( indices, 2 ) );
if ( byte_size != 1 )
{
auto casted_ptr = ir_builder->CreatePointerCast(
stack_ptr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), casted_ptr );
}
else
{
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), stack_ptr );
}
}
llvm::Value *vmp_rtn_t::load_value( std::uint8_t byte_size, llvm::GlobalValue *global )
{
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), global );
}
llvm::Value *vmp_rtn_t::load_value( std::uint8_t byte_size, llvm::AllocaInst *var )
{
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
}
void vmp_rtn_t::create_virtual_stack( void )
{
// allocate stack space...
virtual_stack = ir_builder->CreateAlloca( llvm::ArrayType::get( llvm::IntegerType::get( *llvm_ctx, 8 ), 1024 ),
nullptr, "stack" );
// allocate stack pointer...
stack_ptr = ir_builder->CreateAlloca( llvm::IntegerType::get( *llvm_ctx, 64 ), nullptr, "sp" );
ir_builder->CreateStore( llvm::ConstantInt::get( llvm::IntegerType::getInt64Ty( *llvm_ctx ), 1024 ),
stack_ptr );
}
llvm::Function *vmp_rtn_t::lift( void )
{
auto &code_blocks = llvm_fptr->getBasicBlockList();
// retrieve ADD insn
auto targetInsn = vmp2_code_blocks[ 0 ].vinstrs.at( 0x2d );
vmp2::lifters::lift( this, vmp2_code_blocks[ 0 ], targetInsn, ir_builder.get() );
std::printf( "lifted add insn" );
llvm_module->print( llvm::outs(), nullptr );
std::printf( "alogged adda insn function" );
return llvm_fptr;
for ( auto idx = 0u; idx < code_blocks.size(); ++idx )
{
for ( auto &vinstr : vmp2_code_blocks[ idx ].vinstrs )
{
if ( !vmp2::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;
}
} // namespace vm