From 92bd54c3681449f8c1578d08d39a5ba767db6e55 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Tue, 17 Aug 2021 19:14:43 -0700 Subject: [PATCH] added all of the code to append obj files to pe files... MessageBoxA - hello world doesnt work because top 32bits of registers are not being extended... --- include/devirt_utils.hpp | 8 ++ src/devirt_t.cpp | 36 ++++++--- src/devirt_utils.cpp | 169 +++++++++++++++++++++++++++++++++++++-- src/lifters/lconst.cpp | 32 ++++---- src/lifters/sreg.cpp | 4 +- src/main.cpp | 13 ++- src/vmp_rtn_t.cpp | 2 +- 7 files changed, 227 insertions(+), 37 deletions(-) diff --git a/include/devirt_utils.hpp b/include/devirt_utils.hpp index e3f34b0..0ae00e2 100644 --- a/include/devirt_utils.hpp +++ b/include/devirt_utils.hpp @@ -1,10 +1,18 @@ #include + #include +#include +#include +#include #define VM_ENTER_NAME "vmenter_" #define VM_EXIT_NAME "vmexit_" #define VM_RTN_NAME "rtn_" +#define FIX_MAKE_ZERO_OFFSET 0x25 +#define FIX_MAKE_RELOC_OFFSET 0x30 +#define FIX_MAKE_JMP_OFFSET 0x43 + namespace devirt { namespace util diff --git a/src/devirt_t.cpp b/src/devirt_t.cpp index c13c4d7..169536f 100644 --- a/src/devirt_t.cpp +++ b/src/devirt_t.cpp @@ -14,15 +14,17 @@ namespace vm // sub rsp, num_bytes auto current_rtn = vmp_rtns.back(); auto rsp_addr = ir_builder->CreateLoad( current_rtn->stack ); + rsp_addr->setAlignment( llvm::Align( 1 ) ); + auto sub_rsp_val = ir_builder->CreateGEP( ir_builder->getInt8Ty(), rsp_addr, ir_builder->getInt8( 0 - num_bytes ) ); - ir_builder->CreateStore( sub_rsp_val, current_rtn->stack ); + ir_builder->CreateStore( sub_rsp_val, current_rtn->stack )->setAlignment( llvm::Align( 1 ) ); // mov [rsp], val auto resized_new_rsp_addr = ir_builder->CreateBitCast( sub_rsp_val, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), 0ull ) ); - ir_builder->CreateStore( val, resized_new_rsp_addr ); + ir_builder->CreateStore( val, resized_new_rsp_addr )->setAlignment( llvm::Align( 1 ) ); } llvm::Value *devirt_t::pop( std::uint8_t num_bytes ) @@ -30,14 +32,20 @@ namespace vm // mov rax, [rsp] auto current_rtn = vmp_rtns.back(); auto rsp_addr = ir_builder->CreateLoad( current_rtn->stack ); + rsp_addr->setAlignment( llvm::Align( 1 ) ); + auto new_rsp_addr = ir_builder->CreateGEP( ir_builder->getInt8Ty(), rsp_addr, ir_builder->getInt8( num_bytes ) ); auto resized_new_rsp_addr = ir_builder->CreateBitCast( rsp_addr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), 0ull ) ); + auto pop_val = ir_builder->CreateLoad( resized_new_rsp_addr ); - ir_builder->CreateStore( new_rsp_addr, current_rtn->stack ); - ir_builder->CreateStore( llvm::UndefValue::get( ir_builder->getInt8Ty() ), rsp_addr ); + pop_val->setAlignment( llvm::Align( 1 ) ); + + ir_builder->CreateStore( new_rsp_addr, current_rtn->stack )->setAlignment( llvm::Align( 1 ) ); + ir_builder->CreateStore( llvm::UndefValue::get( ir_builder->getInt8Ty() ), rsp_addr ) + ->setAlignment( llvm::Align( 1 ) ); return pop_val; } @@ -48,9 +56,14 @@ namespace vm auto cast_ptr = ir_builder->CreatePointerCast( var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) ); - return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr ); + auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr ); + result->setAlignment( llvm::Align( 1 ) ); + return result; } - return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var ); + + auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var ); + result->setAlignment( llvm::Align( 1 ) ); + return result; } llvm::Value *devirt_t::load_value( std::uint8_t byte_size, llvm::AllocaInst *var ) @@ -60,9 +73,14 @@ namespace vm auto cast_ptr = ir_builder->CreatePointerCast( var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) ); - return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr ); + auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr ); + result->setAlignment( llvm::Align( 1 ) ); + return result; } - return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var ); + + auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var ); + result->setAlignment( llvm::Align( 1 ) ); + return result; } bool devirt_t::compile( std::vector< std::uint8_t > &obj ) @@ -170,7 +188,7 @@ namespace vm auto or3 = ir_builder->CreateOr( or2, shifted_sf ); auto or4 = ir_builder->CreateOr( or3, shifted_af ); auto or5 = ir_builder->CreateOr( or4, shifted_pf ); - return ir_builder->CreateXor( or5, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 514 ) ); + return ir_builder->CreateOr( or5, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 514 ) ); } } // namespace vm \ No newline at end of file diff --git a/src/devirt_utils.cpp b/src/devirt_utils.cpp index 0caaa82..8e7a62f 100644 --- a/src/devirt_utils.cpp +++ b/src/devirt_utils.cpp @@ -57,12 +57,169 @@ namespace devirt 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... + std::vector< std::uint8_t > map_buff; + auto bin_img = reinterpret_cast< win::image_t<> * >( bin.data() ); + + // increase the size_image by the size of the obj as the entire obj will be in the .devirt section... + map_buff.resize( + reinterpret_cast< win::image_t<> * >( bin.data() )->get_nt_headers()->optional_header.size_image += + obj.size() ); + + // copy over dos headers, pe headers (section headers, optional header etc)... + std::memcpy( map_buff.data(), bin.data(), bin_img->get_nt_headers()->optional_header.size_headers ); + + // map sections into map_img... also make image offset == virtual offset... + auto map_img = reinterpret_cast< win::image_t<> * >( map_buff.data() ); + std::for_each( bin_img->get_nt_headers()->get_sections(), + bin_img->get_nt_headers()->get_sections() + bin_img->get_file_header()->num_sections, + [ & ]( coff::section_header_t §ion ) { + if ( !section.virtual_address || !section.ptr_raw_data ) + return; + + std::memcpy( map_buff.data() + section.virtual_address, bin.data() + section.ptr_raw_data, + section.size_raw_data ); + } ); + + std::for_each( map_img->get_nt_headers()->get_sections(), + map_img->get_nt_headers()->get_sections() + bin_img->get_file_header()->num_sections, + [ & ]( coff::section_header_t §ion ) { + section.ptr_raw_data = section.virtual_address; + section.size_raw_data = section.virtual_size; + } ); + + auto pe_sections = map_img->get_nt_headers()->get_sections(); + auto num_sections = map_img->get_file_header()->num_sections; + + // append .devirt section to the original binary... + strcpy( pe_sections[ num_sections ].name.short_name, ".devirt" ); + pe_sections[ num_sections ].virtual_size = obj.size(); + pe_sections[ num_sections ].size_raw_data = obj.size(); + pe_sections[ num_sections ].virtual_address = ( pe_sections[ num_sections - 1 ].virtual_address + + pe_sections[ num_sections - 1 ].virtual_size + 0x1000 ) & + ~0xFFFull; + pe_sections[ num_sections ].ptr_raw_data = pe_sections[ num_sections ].virtual_address; + + coff::section_characteristics_t prots; + prots.flags = 0ull; + prots.mem_execute = true; + prots.mem_read = true; + pe_sections[ num_sections ].characteristics = prots; + ++map_img->get_file_header()->num_sections; + std::memcpy( map_buff.data() + pe_sections[ num_sections ].virtual_address, obj.data(), obj.size() ); + + auto obj_img_addr = map_buff.data() + pe_sections[ num_sections ].virtual_address; + auto obj_img = reinterpret_cast< coff::image_t * >( obj_img_addr ); + auto str_table = obj_img->get_strings(); + auto symbols = obj_img->get_symbols(); + auto obj_sections = obj_img->get_sections(); + auto symbol_cnt = obj_img->file_header.num_symbols; + + static const auto get_symbol = [ & ]( std::string symbol_name ) -> auto + { + return std::find_if( symbols, symbols + symbol_cnt, [ & ]( coff::symbol_t &symbol ) { + if ( symbol.derived_type != coff::derived_type_id::function ) + return false; + + return !strcmp( symbol.name.to_string( str_table ).data(), symbol_name.c_str() ); + } ); + }; + + std::vector< std::pair< std::uint32_t, std::uint16_t > > new_relocs; + std::for_each( symbols, symbols + symbol_cnt, [ & ]( coff::symbol_t &symbol ) { + if ( symbol.derived_type != coff::derived_type_id::function ) + return; + + auto symbol_name = symbol.name.to_string( str_table ); + if ( strstr( symbol_name.data(), VM_ENTER_NAME ) ) + { + // fix 0x1000000000000000 to 0x0000000000000000 + // also push back a new relocation for this entry... + auto symbol_addr = reinterpret_cast< std::uintptr_t >( obj_img_addr ) + symbol.value + + obj_sections[ symbol.section_index - 1 ].ptr_raw_data; + + *reinterpret_cast< std::uintptr_t * >( symbol_addr + FIX_MAKE_ZERO_OFFSET ) = 0ull; + *reinterpret_cast< std::uintptr_t * >( symbol_addr + FIX_MAKE_RELOC_OFFSET ) = 0ull; + + auto page = ( symbol.value + obj_sections[ symbol.section_index - 1 ].ptr_raw_data + + FIX_MAKE_RELOC_OFFSET + pe_sections[ num_sections ].virtual_address ) & + ~0xFFFull; + + auto offset = ( symbol.value + obj_sections[ symbol.section_index - 1 ].ptr_raw_data + + FIX_MAKE_RELOC_OFFSET + pe_sections[ num_sections ].virtual_address ) & + 0xFFFull; + + new_relocs.push_back( { page, offset } ); + + // look up the rtn_xxxxxx function symbol for this vm enter and make it jmp to it... + auto rtn_offset = std::stoull( symbol_name.data() + sizeof( VM_ENTER_NAME ) - 1, nullptr, 16 ); + + std::stringstream rtn_name; + rtn_name << "rtn_" << std::hex << rtn_offset; + auto rtn_sym = get_symbol( rtn_name.str() ); + auto relocs = reinterpret_cast< coff::reloc_t * >( + obj_sections[ rtn_sym->section_index - 1 ].ptr_relocs + obj_img_addr ); + + auto rtn_rva = rtn_offset - map_img->get_nt_headers()->optional_header.image_base; + std::int32_t devirt_rtn_rva = symbol_addr - reinterpret_cast< std::uintptr_t >( map_buff.data() ); + + *reinterpret_cast< std::int32_t * >( rtn_rva + map_buff.data() + 1 ) = devirt_rtn_rva - ( rtn_rva + 5 ); + *reinterpret_cast< std::uint8_t * >( rtn_rva + map_buff.data() ) = 0xE9; + + // apply relocations to the rtn_xxxxx... + for ( auto reloc_idx = 0u; reloc_idx < obj_sections[ rtn_sym->section_index - 1 ].num_relocs; + ++reloc_idx ) + { + coff::reloc_t &reloc = relocs[ reloc_idx ]; + + auto vmexit_sym = + get_symbol( std::string( symbols[ reloc.symbol_index ].name.to_string( str_table ) ) ); + + auto vmexit_offset = vmexit_sym->value + obj_sections[ vmexit_sym->section_index - 1 ].ptr_raw_data; + + auto reloc_file_offset = + reloc.virtual_address + obj_sections[ rtn_sym->section_index - 1 ].ptr_raw_data; + + std::int32_t rva = vmexit_offset - ( std::int32_t )( reloc_file_offset + 4 ); + *reinterpret_cast< std::int32_t * >( obj_img_addr + reloc_file_offset ) = rva; + } + + auto rtn_addr = reinterpret_cast< std::uintptr_t >( + obj_img_addr + obj_sections[ rtn_sym->section_index - 1 ].ptr_raw_data + rtn_sym->value ); + + // create jmp to the rtn_xxxxx.... + std::int32_t rva = rtn_addr - ( std::int64_t )( symbol_addr + FIX_MAKE_JMP_OFFSET + 5 ); + *reinterpret_cast< std::uint8_t * >( symbol_addr + FIX_MAKE_JMP_OFFSET ) = 0xE9; + *reinterpret_cast< std::int32_t * >( symbol_addr + FIX_MAKE_JMP_OFFSET + 1 ) = rva; + } + } ); + + auto resize_cnt = new_relocs.size() * ( sizeof( win::reloc_entry_t ) + sizeof( win::reloc_block_t ) ); + map_buff.resize( map_img->get_nt_headers()->optional_header.size_image += resize_cnt ); + + map_img = reinterpret_cast< win::image_t<> * >( map_buff.data() ); + auto basereloc_dir = map_img->get_directory( win::directory_id::directory_entry_basereloc ); + auto reloc_dir = reinterpret_cast< win::reloc_directory_t * >( basereloc_dir->rva + map_buff.data() ); + + basereloc_dir->size += resize_cnt; + for ( const auto &[ reloc_rva, reloc_offset ] : new_relocs ) + { + win::reloc_block_t *reloc_block = &reloc_dir->first_block; + while ( reloc_block->base_rva && reloc_block->size_block ) + reloc_block = reloc_block->next(); + + reloc_block->base_rva = reloc_rva; + reloc_block->size_block = sizeof( win::reloc_entry_t ) + sizeof uint64_t; + + reloc_block->next()->base_rva = 0ull; + reloc_block->next()->size_block = 0ull; + + reloc_block->entries[ 0 ].type = win::reloc_type_id::rel_based_dir64; + reloc_block->entries[ 0 ].offset = reloc_offset; + } + + // replace bin vector with map_buff vector... + bin.clear(); + bin.insert( bin.begin(), map_buff.begin(), map_buff.end() ); return true; } } // namespace devirt \ No newline at end of file diff --git a/src/lifters/lconst.cpp b/src/lifters/lconst.cpp index b4a5e1f..1e7d32e 100644 --- a/src/lifters/lconst.cpp +++ b/src/lifters/lconst.cpp @@ -3,50 +3,50 @@ namespace vm { lifters_t::lifter_callback_t lifters_t::lconstq = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) ); }; lifters_t::lifter_callback_t lifters_t::lconstdwsxq = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) ); }; lifters_t::lifter_callback_t lifters_t::lconstwsxq = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) ); }; lifters_t::lifter_callback_t lifters_t::lconstbsxq = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) ); }; lifters_t::lifter_callback_t lifters_t::lconstdw = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) ); }; lifters_t::lifter_callback_t lifters_t::lconstwsxdw = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) ); }; lifters_t::lifter_callback_t lifters_t::lconstbsxdw = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) ); }; lifters_t::lifter_callback_t lifters_t::lconstbzxw = - [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, - const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) { + [ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr, + llvm::IRBuilder<> *ir_builder ) { rtn->push( 2, llvm::ConstantInt::get( ir_builder->getInt16Ty(), vinstr.operand.imm.u ) ); }; } // namespace vm \ No newline at end of file diff --git a/src/lifters/sreg.cpp b/src/lifters/sreg.cpp index bb656a9..71eed60 100644 --- a/src/lifters/sreg.cpp +++ b/src/lifters/sreg.cpp @@ -8,7 +8,7 @@ namespace vm auto t1 = rtn->pop( 8 ); auto &vmp_rtn = rtn->vmp_rtns.back(); auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ]; - ir_builder->CreateStore( t1, vreg ); + ir_builder->CreateStore( t1, vreg )->setAlignment( llvm::Align( 8 ) ); }; lifters_t::lifter_callback_t lifters_t::sregdw = @@ -19,6 +19,6 @@ namespace vm auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ]; auto vregdw = ir_builder->CreatePointerCast( vreg, llvm::PointerType::get( llvm::IntegerType::get( *rtn->llvm_ctx, 32 ), 0 ) ); - ir_builder->CreateStore( t1, vregdw ); + ir_builder->CreateStore( t1, vregdw )->setAlignment( llvm::Align( 4 ) ); }; } // namespace vm \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 01210f8..3584293 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,8 @@ 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( "--out" ).required( true ).description( "output devirtualized binary name..." ); + parser.add_argument().name( "--genobj" ).description( "write the generated obj file to disk..." ); parser.add_argument().name( "--bin" ).required( true ).description( "path to the image in which to apply devirtualized code too...\n" ); @@ -69,6 +71,8 @@ int main( int argc, const char *argv[] ) vmp2_code_blocks.size() ); } + llvm_module.print( llvm::outs(), nullptr ); + llvm::LLVMInitializeX86TargetInfo(); llvm::LLVMInitializeX86Target(); llvm::LLVMInitializeX86TargetMC(); @@ -77,9 +81,12 @@ 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() ); + + if ( parser.exists( "genobj" ) ) + std::ofstream( parser.get< std::string >( "out" ).append( ".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() ); + std::ofstream( parser.get< std::string >( "out" ), 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 5bca12e..40ec530 100644 --- a/src/vmp_rtn_t.cpp +++ b/src/vmp_rtn_t.cpp @@ -79,7 +79,7 @@ namespace vm } rtn_name.str( "" ); - asm_str.append( "mov rcx, rsp; sub rsp, 0xA00; " ); + asm_str.append( "mov rcx, rsp; sub rsp, 0xA00; int 3; int 3; int 3; int 3; int 3;" ); rtn_name << "vmenter_" << std::hex << rtn_begin; auto entry_func = llvm::Function::Create( llvm::FunctionType::get( ir_builder->getVoidTy(), false ),