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/devirt_utils.cpp

201 lines
10 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 v4...\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 &section ) {
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 &section ) {
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;
}
} );
// replace bin vector with map_buff vector...
bin.clear();
bin.insert( bin.begin(), map_buff.begin(), map_buff.end() );
return true;
}
} // namespace devirt