|
|
|
@ -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
|