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.
225 lines
12 KiB
225 lines
12 KiB
#include <devirt_utils.hpp>
|
|
|
|
namespace devirt
|
|
{
|
|
namespace util
|
|
{
|
|
bool serialize_vmp2(
|
|
std::vector< std::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > &virt_rtns,
|
|
std::vector< std::uint8_t > &vmp2file )
|
|
{
|
|
const auto file_header = reinterpret_cast< vmp2::v4::file_header * >( vmp2file.data() );
|
|
|
|
if ( file_header->version != vmp2::version_t::v4 )
|
|
{
|
|
std::printf( "[!] invalid vmp2 file version... this build uses v3...\n" );
|
|
return false;
|
|
}
|
|
|
|
auto first_rtn = reinterpret_cast< vmp2::v4::rtn_t * >( reinterpret_cast< std::uintptr_t >( file_header ) +
|
|
file_header->rtn_offset );
|
|
|
|
for ( auto [ rtn_block, rtn_idx ] = std::pair{ first_rtn, 0ull }; rtn_idx < file_header->rtn_count;
|
|
++rtn_idx, rtn_block = reinterpret_cast< vmp2::v4::rtn_t * >(
|
|
reinterpret_cast< std::uintptr_t >( rtn_block ) + rtn_block->size ) )
|
|
{
|
|
virt_rtns.push_back( { rtn_block->vm_enter_offset, {} } );
|
|
for ( auto [ code_block, block_idx ] = std::pair{ &rtn_block->code_blocks[ 0 ], 0ull };
|
|
block_idx < rtn_block->code_block_count;
|
|
++block_idx, code_block = reinterpret_cast< vmp2::v4::code_block_t * >(
|
|
reinterpret_cast< std::uintptr_t >( code_block ) +
|
|
code_block->next_block_offset ) )
|
|
{
|
|
auto block_vinstrs = reinterpret_cast< vm::instrs::virt_instr_t * >(
|
|
reinterpret_cast< std::uintptr_t >( code_block ) + sizeof vmp2::v4::code_block_t +
|
|
( code_block->num_block_addrs * 8 ) );
|
|
|
|
vm::instrs::code_block_t _code_block{ code_block->vip_begin };
|
|
_code_block.jcc.has_jcc = code_block->has_jcc;
|
|
_code_block.jcc.type = code_block->jcc_type;
|
|
|
|
for ( auto idx = 0u; idx < code_block->num_block_addrs; ++idx )
|
|
_code_block.jcc.block_addr.push_back( code_block->branch_addr[ idx ] );
|
|
|
|
for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx )
|
|
_code_block.vinstrs.push_back( block_vinstrs[ idx ] );
|
|
|
|
virt_rtns.back().second.push_back( _code_block );
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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
|