//Get relocation list of pe file, supports one-word sized relocations only //If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries) { relocation_table_list ret; //If image does not have relocations if(!pe.has_reloc()) return ret; //Check the length in bytes of the section containing relocation directory if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_basereloc), pe.get_directory_rva(image_directory_entry_basereloc), section_data_virtual, true) < sizeof(image_base_relocation)) throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); unsigned long current_pos = pe.get_directory_rva(image_directory_entry_basereloc); //First IMAGE_BASE_RELOCATION table image_base_relocation reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); if(reloc_table.SizeOfBlock % 2) throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); unsigned long reloc_size = pe.get_directory_size(image_directory_entry_basereloc); unsigned long read_size = 0; //reloc_table.VirtualAddress is not checked (not so important) while(reloc_table.SizeOfBlock && read_size < reloc_size) { //Create relocation table relocation_table table; //Save RVA table.set_rva(reloc_table.VirtualAddress); if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); //List all relocations for(unsigned long i = sizeof(image_base_relocation); i < reloc_table.SizeOfBlock; i += sizeof(uint16_t)) { relocation_entry entry(pe.section_data_from_rva<uint16_t>(current_pos + i, section_data_virtual, true)); if(list_absolute_entries || entry.get_type() != image_rel_based_absolute) table.add_relocation(entry); } //Save table ret.push_back(table); //Go to next relocation block if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); current_pos += reloc_table.SizeOfBlock; read_size += reloc_table.SizeOfBlock; if (read_size >= reloc_size) break; reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); } return ret; }
void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) { //Get current image base value typename PEClassType::BaseSize image_base; pe.get_image_base(image_base); //ImageBase difference typename PEClassType::BaseSize base_rel = static_cast<typename PEClassType::BaseSize>(static_cast<int64_t>(new_base) - image_base); //We need to fix addresses from relocation tables //Enumerate relocation tables for(relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it) { const relocation_table::relocation_list& relocs = (*it).get_relocations(); uint32_t base_rva = (*it).get_rva(); //Enumerate relocations for(relocation_table::relocation_list::const_iterator rel = relocs.begin(); rel != relocs.end(); ++rel) { //Skip ABSOLUTE entries if((*rel).get_type() == pe_win::image_rel_based_absolute) continue; //Recalculate value by RVA and rewrite it uint32_t current_rva = base_rva + (*rel).get_rva(); typename PEClassType::BaseSize value = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_rva, section_data_raw, true); value += base_rel; memcpy(pe.section_data_from_rva(current_rva, true), &value, sizeof(value)); } } //Finally, save new image base pe.set_image_base_64(new_base); }
//Returns basic .NET information //If image is not native, throws an exception const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe) { //If there's no debug directory, return empty list if(!pe.is_dotnet()) throw pe_exception("Image does not have managed code", pe_exception::image_does_not_have_managed_code); //Return basic .NET information return basic_dotnet_info(pe.section_data_from_rva<image_cor20_header>(pe.get_directory_rva(image_directory_entry_com_descriptor), section_data_virtual, true)); }
//Returns resources from PE file const resource_directory get_resources(const pe_base& pe) { resource_directory ret; if(!pe.has_resources()) return ret; //Get resource directory RVA uint32_t res_rva = pe.get_directory_rva(image_directory_entry_resource); //Store already processed directories to avoid resource loops std::set<uint32_t> processed; //Process all directories (recursion) ret = process_resource_directory(pe, res_rva, 0, processed); return ret; }
const image_config_info get_image_config_base(const pe_base& pe) { //Check if image has config directory if(!pe.has_config()) throw pe_exception("Image does not have load config directory", pe_exception::directory_does_not_exist); //Get load config structure typename PEClassType::ConfigStruct config_info = pe.section_data_from_rva<typename PEClassType::ConfigStruct>(pe.get_directory_rva(image_directory_entry_load_config), section_data_virtual); //Check size of config directory if(config_info.Size != sizeof(config_info)) throw pe_exception("Incorrect (or old) load config directory", pe_exception::incorrect_config_directory); //Fill return structure image_config_info ret(config_info); //Check possible overflow if(config_info.SEHandlerCount >= pe_utils::max_dword / sizeof(uint32_t) || config_info.SEHandlerTable >= static_cast<typename PEClassType::BaseSize>(-1) - config_info.SEHandlerCount * sizeof(uint32_t)) throw pe_exception("Incorrect load config directory", pe_exception::incorrect_config_directory); //Read sorted SE handler RVA list (if any) for(typename PEClassType::BaseSize i = 0; i != config_info.SEHandlerCount; ++i) ret.add_se_handler_rva(pe.section_data_from_va<uint32_t>(static_cast<typename PEClassType::BaseSize>(config_info.SEHandlerTable + i * sizeof(uint32_t)))); if(config_info.LockPrefixTable) { //Read Lock Prefix VA list (if any) unsigned long current = 0; while(true) { typename PEClassType::BaseSize lock_prefix_va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(config_info.LockPrefixTable + current * sizeof(typename PEClassType::BaseSize))); if(!lock_prefix_va) break; ret.add_lock_prefix_rva(pe.va_to_rva(lock_prefix_va)); ++current; } } return ret; }
//Returns exception directory data (exists on PE+ only) //Unwind opcodes are not listed, because their format and list are subject to change const exception_entry_list get_exception_directory_data(const pe_base& pe) { exception_entry_list ret; //If image doesn't have exception directory, return empty list if(!pe.has_exception_directory()) return ret; //Check the length in bytes of the section containing exception directory if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_exception), pe.get_directory_rva(image_directory_entry_exception), section_data_virtual, true) < sizeof(image_runtime_function_entry)) throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); unsigned long current_pos = pe.get_directory_rva(image_directory_entry_exception); //Check if structures are DWORD-aligned if(current_pos % sizeof(uint32_t)) throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); //First IMAGE_RUNTIME_FUNCTION_ENTRY table image_runtime_function_entry exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); //todo: virtual addresses BeginAddress and EndAddress are not checked to be inside image while(exception_table.BeginAddress) { //Check addresses if(exception_table.BeginAddress > exception_table.EndAddress) throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); //Get unwind information unwind_info info = pe.section_data_from_rva<unwind_info>(exception_table.UnwindInfoAddress, section_data_virtual, true); //Create exception entry and save it ret.push_back(exception_entry(exception_table, info)); //Go to next exception entry current_pos += sizeof(image_runtime_function_entry); exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); } return ret; }
//Resources rebuilder //resource_directory - root resource directory //resources_section - section where resource directory will be placed (must be attached to PE image) //offset_from_section_start - offset from resources_section raw data start //resource_directory is non-constant, because it will be sorted //save_to_pe_headers - if true, new resource directory information will be saved to PE image headers //auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped //number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) { //Check that resources_section is attached to this PE image if(!pe.section_attached(resources_section)) throw pe_exception("Resource section must be attached to PE file", pe_exception::section_is_not_attached); //Check resource directory correctness if(info.get_entry_list().empty()) throw pe_exception("Empty resource directory", pe_exception::incorrect_resource_directory); uint32_t aligned_offset_from_section_start = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); uint32_t needed_size_for_structures = aligned_offset_from_section_start - offset_from_section_start; //Calculate needed size for resource tables and data uint32_t needed_size_for_strings = 0; uint32_t needed_size_for_data = 0; calculate_resource_data_space(info, aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); { uint32_t current_data_pos = aligned_offset_from_section_start + needed_size_for_structures + needed_size_for_strings; calculate_resource_data_space(info, needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); } uint32_t needed_size = needed_size_for_structures + needed_size_for_strings + needed_size_for_data; //Check if resources_section is last one. If it's not, check if there's enough place for resource data if(&resources_section != &*(pe.get_image_sections().end() - 1) && (resources_section.empty() || pe_utils::align_up(resources_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + aligned_offset_from_section_start)) throw pe_exception("Insufficient space for resource directory", pe_exception::insufficient_space); std::string& raw_data = resources_section.get_raw_data(); //This will be done only if resources_section is the last section of image or for section with unaligned raw length of data if(raw_data.length() < needed_size + aligned_offset_from_section_start) raw_data.resize(needed_size + aligned_offset_from_section_start); //Expand section raw data uint32_t current_structures_pos = aligned_offset_from_section_start; uint32_t current_strings_pos = current_structures_pos + needed_size_for_structures; uint32_t current_data_pos = current_strings_pos + needed_size_for_strings; rebuild_resource_directory(pe, resources_section, info, current_structures_pos, current_data_pos, current_strings_pos, aligned_offset_from_section_start); //Adjust section raw and virtual sizes pe.recalculate_section_sizes(resources_section, auto_strip_last_section); image_directory ret(pe.rva_from_section_offset(resources_section, aligned_offset_from_section_start), needed_size); //If auto-rewrite of PE headers is required if(save_to_pe_header) { pe.set_directory_rva(image_directory_entry_resource, ret.get_rva()); pe.set_directory_size(image_directory_entry_resource, ret.get_size()); } return ret; }
void test_relocations(const pe_base& image, const relocation_table_list& tables, bool read_absolute_entries) { if(image.get_pe_type() == pe_type_32) { PE_TEST(tables.size() == 30, "Relocation test 1", test_level_critical); PE_TEST(tables[1].get_rva() == 0x2000, "Relocation test 2", test_level_normal); PE_TEST(tables[1].get_relocations().size() == (read_absolute_entries ? 22 : 21), "Relocation test 3", test_level_critical); PE_TEST(tables[1].get_relocations()[1].get_rva() == 0x54, "Relocation test 4", test_level_normal); PE_TEST(tables[1].get_relocations()[1].get_type() == pe_win::image_rel_based_highlow, "Relocation test 5", test_level_normal); } else { PE_TEST(tables.size() == 7, "Relocation test 1", test_level_critical); PE_TEST(tables[1].get_rva() == 0x1C000, "Relocation test 2", test_level_normal); PE_TEST(tables[4].get_relocations().size() == (read_absolute_entries ? 6 : 5), "Relocation test 3", test_level_critical); PE_TEST(tables[1].get_relocations()[1].get_rva() == 0x4E8, "Relocation test 4", test_level_normal); PE_TEST(tables[1].get_relocations()[1].get_type() == pe_win::image_rel_based_dir64, "Relocation test 5", test_level_normal); } }
const bound_import_module_list get_bound_import_module_list(const pe_base& pe) { //Returned bound import modules list bound_import_module_list ret; //If image has no bound imports if(!pe.has_bound_import()) return ret; uint32_t bound_import_data_len = pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); if(bound_import_data_len < pe.get_directory_size(image_directory_entry_bound_import)) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); const char* bound_import_data = pe.section_data_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); //Check read in "read_pe" function raw bound import data size if(bound_import_data_len < sizeof(image_bound_import_descriptor)) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //current bound_import_data_ in-string position unsigned long current_pos = 0; //first bound import descriptor //so, we're working with raw data here, no section helpers available const image_bound_import_descriptor* descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); //Enumerate until zero while(descriptor->OffsetModuleName) { //Check module name offset if(descriptor->OffsetModuleName >= bound_import_data_len) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //Check module name for null-termination if(!pe_utils::is_null_terminated(&bound_import_data[descriptor->OffsetModuleName], bound_import_data_len - descriptor->OffsetModuleName)) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //Create bound import descriptor structure bound_import elem(&bound_import_data[descriptor->OffsetModuleName], descriptor->TimeDateStamp); //Check DWORDs if(descriptor->NumberOfModuleForwarderRefs >= pe_utils::max_dword / sizeof(image_bound_forwarder_ref) || !pe_utils::is_sum_safe(current_pos, 2 /* this descriptor and the next one */ * sizeof(image_bound_import_descriptor) + descriptor->NumberOfModuleForwarderRefs * sizeof(image_bound_forwarder_ref))) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //Move after current descriptor current_pos += sizeof(image_bound_import_descriptor); //Enumerate referenced bound import descriptors for(unsigned long i = 0; i != descriptor->NumberOfModuleForwarderRefs; ++i) { //They're just after parent descriptor //Check size of structure if(current_pos + sizeof(image_bound_forwarder_ref) > bound_import_data_len) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //Get IMAGE_BOUND_FORWARDER_REF pointer const image_bound_forwarder_ref* ref_descriptor = reinterpret_cast<const image_bound_forwarder_ref*>(&bound_import_data[current_pos]); //Check referenced module name if(ref_descriptor->OffsetModuleName >= bound_import_data_len) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //And its null-termination if(!pe_utils::is_null_terminated(&bound_import_data[ref_descriptor->OffsetModuleName], bound_import_data_len - ref_descriptor->OffsetModuleName)) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //Add referenced module to current bound import structure elem.add_module_ref(bound_import_ref(&bound_import_data[ref_descriptor->OffsetModuleName], ref_descriptor->TimeDateStamp)); //Move after referenced bound import descriptor current_pos += sizeof(image_bound_forwarder_ref); } //Check structure size if(current_pos + sizeof(image_bound_import_descriptor) > bound_import_data_len) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); //Move to next bound import descriptor descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); //Save created descriptor structure and references ret.push_back(elem); } //Return result return ret; }
const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) { //Check that image_config_section is attached to this PE image if(!pe.section_attached(image_config_section)) throw pe_exception("Image Config section must be attached to PE file", pe_exception::section_is_not_attached); uint32_t alignment = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)) - offset_from_section_start; uint32_t needed_size = sizeof(typename PEClassType::ConfigStruct); //Calculate needed size for Image Config table uint32_t image_config_data_pos = offset_from_section_start + alignment; uint32_t current_pos_of_se_handlers = 0; uint32_t current_pos_of_lock_prefixes = 0; if(write_se_handlers) { current_pos_of_se_handlers = needed_size + image_config_data_pos; needed_size += static_cast<uint32_t>(info.get_se_handler_rvas().size()) * sizeof(uint32_t); //RVAs of SE Handlers } if(write_lock_prefixes) { current_pos_of_lock_prefixes = needed_size + image_config_data_pos; needed_size += static_cast<uint32_t>((info.get_lock_prefix_rvas().size() + 1) * sizeof(typename PEClassType::BaseSize)); //VAs of Lock Prefixes (and ending null element) } //Check if image_config_section is last one. If it's not, check if there's enough place for Image Config data if(&image_config_section != &*(pe.get_image_sections().end() - 1) && (image_config_section.empty() || pe_utils::align_up(image_config_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + image_config_data_pos)) throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); std::string& raw_data = image_config_section.get_raw_data(); //This will be done only if image_config_section is the last section of image or for section with unaligned raw length of data if(raw_data.length() < needed_size + image_config_data_pos) raw_data.resize(needed_size + image_config_data_pos); //Expand section raw data //Create and fill Image Config structure typename PEClassType::ConfigStruct image_config_section_struct = {0}; image_config_section_struct.Size = sizeof(image_config_section_struct); image_config_section_struct.TimeDateStamp = info.get_time_stamp(); image_config_section_struct.MajorVersion = info.get_major_version(); image_config_section_struct.MinorVersion = info.get_minor_version(); image_config_section_struct.GlobalFlagsClear = info.get_global_flags_clear(); image_config_section_struct.GlobalFlagsSet = info.get_global_flags_set(); image_config_section_struct.CriticalSectionDefaultTimeout = info.get_critical_section_default_timeout(); image_config_section_struct.DeCommitFreeBlockThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_free_block_threshold()); image_config_section_struct.DeCommitTotalFreeThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_total_free_threshold()); image_config_section_struct.MaximumAllocationSize = static_cast<typename PEClassType::BaseSize>(info.get_max_allocation_size()); image_config_section_struct.VirtualMemoryThreshold = static_cast<typename PEClassType::BaseSize>(info.get_virtual_memory_threshold()); image_config_section_struct.ProcessHeapFlags = info.get_process_heap_flags(); image_config_section_struct.ProcessAffinityMask = static_cast<typename PEClassType::BaseSize>(info.get_process_affinity_mask()); image_config_section_struct.CSDVersion = info.get_service_pack_version(); image_config_section_struct.EditList = static_cast<typename PEClassType::BaseSize>(info.get_edit_list_va()); image_config_section_struct.SecurityCookie = static_cast<typename PEClassType::BaseSize>(info.get_security_cookie_va()); image_config_section_struct.SEHandlerCount = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_rvas().size()); if(write_se_handlers) { if(info.get_se_handler_rvas().empty()) { write_se_handlers = false; image_config_section_struct.SEHandlerTable = 0; } else { typename PEClassType::BaseSize va; pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_se_handlers), va); image_config_section_struct.SEHandlerTable = va; } } else { image_config_section_struct.SEHandlerTable = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_table_va()); } if(write_lock_prefixes) { if(info.get_lock_prefix_rvas().empty()) { write_lock_prefixes = false; image_config_section_struct.LockPrefixTable = 0; } else { typename PEClassType::BaseSize va; pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_lock_prefixes), va); image_config_section_struct.LockPrefixTable = va; } } else { image_config_section_struct.LockPrefixTable = static_cast<typename PEClassType::BaseSize>(info.get_lock_prefix_table_va()); } //Write image config section memcpy(&raw_data[image_config_data_pos], &image_config_section_struct, sizeof(image_config_section_struct)); if(write_se_handlers) { //Sort SE Handlers list image_config_info::se_handler_list sorted_list = info.get_se_handler_rvas(); std::sort(sorted_list.begin(), sorted_list.end()); //Write SE Handlers table for(image_config_info::se_handler_list::const_iterator it = sorted_list.begin(); it != sorted_list.end(); ++it) { uint32_t se_handler_rva = *it; memcpy(&raw_data[current_pos_of_se_handlers], &se_handler_rva, sizeof(se_handler_rva)); current_pos_of_se_handlers += sizeof(se_handler_rva); } } if(write_lock_prefixes) { //Write Lock Prefixes VA list for(image_config_info::lock_prefix_rva_list::const_iterator it = info.get_lock_prefix_rvas().begin(); it != info.get_lock_prefix_rvas().end(); ++it) { typename PEClassType::BaseSize lock_prefix_va; pe.rva_to_va(*it, lock_prefix_va); memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); current_pos_of_lock_prefixes += sizeof(lock_prefix_va); } { //Ending null VA typename PEClassType::BaseSize lock_prefix_va = 0; memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); } } //Adjust section raw and virtual sizes pe.recalculate_section_sizes(image_config_section, auto_strip_last_section); image_directory ret(pe.rva_from_section_offset(image_config_section, image_config_data_pos), sizeof(typename PEClassType::ConfigStruct)); //If auto-rewrite of PE headers is required if(save_to_pe_header) { pe.set_directory_rva(image_directory_entry_load_config, ret.get_rva()); pe.set_directory_size(image_directory_entry_load_config, ret.get_size()); } return ret; }
//Image config rebuilder const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) { return pe.get_pe_type() == pe_type_32 ? rebuild_image_config_base<pe_types_class_32>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section) : rebuild_image_config_base<pe_types_class_64>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section); }
//Returns image config info //If image does not have config info, throws an exception const image_config_info get_image_config(const pe_base& pe) { return pe.get_pe_type() == pe_type_32 ? get_image_config_base<pe_types_class_32>(pe) : get_image_config_base<pe_types_class_64>(pe); }
//Export directory rebuilder //info - export information //exported_functions_list - list of exported functions //exports_section - section where export directory will be placed (must be attached to PE image) //offset_from_section_start - offset from exports_section raw data start //save_to_pe_headers - if true, new export directory information will be saved to PE image headers //auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped //number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently //characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure //Returns new export directory information //exported_functions_list is copied intentionally to be sorted by ordinal values later //Name ordinals in exported function don't matter, they will be recalculated const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) { //Check that exports_section is attached to this PE image if(!pe.section_attached(exports_section)) throw pe_exception("Exports section must be attached to PE file", pe_exception::section_is_not_attached); //Needed space for strings uint32_t needed_size_for_strings = static_cast<uint32_t>(info.get_name().length() + 1); uint32_t number_of_names = 0; //Number of named functions uint32_t max_ordinal = 0; //Maximum ordinal number uint32_t ordinal_base = static_cast<uint32_t>(-1); //Minimum ordinal value if(exports.empty()) ordinal_base = info.get_ordinal_base(); uint32_t needed_size_for_function_names = 0; //Needed space for function name strings uint32_t needed_size_for_function_forwards = 0; //Needed space for function forwards names //List all exported functions //Calculate needed size for function list { //Also check that there're no duplicate names and ordinals std::set<std::string> used_function_names; std::set<uint16_t> used_function_ordinals; for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) { const exported_function& func = (*it); //Calculate maximum and minimum ordinal numbers max_ordinal = std::max<uint32_t>(max_ordinal, func.get_ordinal()); ordinal_base = std::min<uint32_t>(ordinal_base, func.get_ordinal()); //Check if ordinal is unique if(!used_function_ordinals.insert(func.get_ordinal()).second) throw pe_exception("Duplicate exported function ordinal", pe_exception::duplicate_exported_function_ordinal); if(func.has_name()) { //If function is named ++number_of_names; needed_size_for_function_names += static_cast<uint32_t>(func.get_name().length() + 1); //Check if it's name and name ordinal are unique if(!used_function_names.insert(func.get_name()).second) throw pe_exception("Duplicate exported function name", pe_exception::duplicate_exported_function_name); } //If function is forwarded to another DLL if(func.is_forwarded()) needed_size_for_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); } } //Sort functions by ordinal value std::sort(exports.begin(), exports.end(), ordinal_sorter()); //Calculate needed space for different things... needed_size_for_strings += needed_size_for_function_names; needed_size_for_strings += needed_size_for_function_forwards; uint32_t needed_size_for_function_name_ordinals = number_of_names * sizeof(uint16_t); uint32_t needed_size_for_function_name_rvas = number_of_names * sizeof(uint32_t); uint32_t needed_size_for_function_addresses = (max_ordinal - ordinal_base + 1) * sizeof(uint32_t); //Export directory header will be placed first uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); uint32_t needed_size = sizeof(image_export_directory); //Calculate needed size for export tables and strings //sizeof(IMAGE_EXPORT_DIRECTORY) = export directory header //Total needed space... needed_size += needed_size_for_function_name_ordinals; //For list of names ordinals needed_size += needed_size_for_function_addresses; //For function RVAs needed_size += needed_size_for_strings; //For all strings needed_size += needed_size_for_function_name_rvas; //For function name strings RVAs //Check if exports_section is last one. If it's not, check if there's enough place for exports data if(&exports_section != &*(pe.get_image_sections().end() - 1) && (exports_section.empty() || pe_utils::align_up(exports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) throw pe_exception("Insufficient space for export directory", pe_exception::insufficient_space); std::string& raw_data = exports_section.get_raw_data(); //This will be done only if exports_section is the last section of image or for section with unaligned raw length of data if(raw_data.length() < needed_size + directory_pos) raw_data.resize(needed_size + directory_pos); //Expand section raw data //Library name will be placed after it uint32_t current_pos_of_function_names = static_cast<uint32_t>(info.get_name().length() + 1 + directory_pos + sizeof(image_export_directory)); //Next - function names uint32_t current_pos_of_function_name_ordinals = current_pos_of_function_names + needed_size_for_function_names; //Next - function name ordinals uint32_t current_pos_of_function_forwards = current_pos_of_function_name_ordinals + needed_size_for_function_name_ordinals; //Finally - function addresses uint32_t current_pos_of_function_addresses = current_pos_of_function_forwards + needed_size_for_function_forwards; //Next - function names RVAs uint32_t current_pos_of_function_names_rvas = current_pos_of_function_addresses + needed_size_for_function_addresses; { //Create export directory and fill it image_export_directory dir = {0}; dir.Characteristics = info.get_characteristics(); dir.MajorVersion = info.get_major_version(); dir.MinorVersion = info.get_minor_version(); dir.TimeDateStamp = info.get_timestamp(); dir.NumberOfFunctions = max_ordinal - ordinal_base + 1; dir.NumberOfNames = number_of_names; dir.Base = ordinal_base; dir.AddressOfFunctions = pe.rva_from_section_offset(exports_section, current_pos_of_function_addresses); dir.AddressOfNameOrdinals = pe.rva_from_section_offset(exports_section, current_pos_of_function_name_ordinals); dir.AddressOfNames = pe.rva_from_section_offset(exports_section, current_pos_of_function_names_rvas); dir.Name = pe.rva_from_section_offset(exports_section, directory_pos + sizeof(image_export_directory)); //Save it memcpy(&raw_data[directory_pos], &dir, sizeof(dir)); } //Sve library name memcpy(&raw_data[directory_pos + sizeof(image_export_directory)], info.get_name().c_str(), info.get_name().length() + 1); //A map to sort function names alphabetically typedef std::map<std::string, uint16_t> funclist; //function name; function name ordinal funclist funcs; uint32_t last_ordinal = ordinal_base; //Enumerate all exported functions for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) { const exported_function& func = (*it); //If we're skipping some ordinals... if(func.get_ordinal() > last_ordinal) { //Fill this function RVAs data with zeros uint32_t len = sizeof(uint32_t) * (func.get_ordinal() - last_ordinal - 1); if(len) { memset(&raw_data[current_pos_of_function_addresses], 0, len); current_pos_of_function_addresses += len; } //Save last encountered ordinal last_ordinal = func.get_ordinal(); } //If function is named, save its name ordinal and name in sorted alphabetically order if(func.has_name()) funcs.insert(std::make_pair(func.get_name(), static_cast<uint16_t>(func.get_ordinal() - ordinal_base))); //Calculate name ordinal //If function is forwarded to another DLL if(func.is_forwarded()) { //Write its forwarded name and its RVA uint32_t function_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_forwards); memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); current_pos_of_function_addresses += sizeof(function_rva); memcpy(&raw_data[current_pos_of_function_forwards], func.get_forwarded_name().c_str(), func.get_forwarded_name().length() + 1); current_pos_of_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); } else { //Write actual function RVA uint32_t function_rva = func.get_rva(); memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); current_pos_of_function_addresses += sizeof(function_rva); } } //Enumerate sorted function names for(funclist::const_iterator it = funcs.begin(); it != funcs.end(); ++it) { //Save function name RVA uint32_t function_name_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_names); memcpy(&raw_data[current_pos_of_function_names_rvas], &function_name_rva, sizeof(function_name_rva)); current_pos_of_function_names_rvas += sizeof(function_name_rva); //Save function name memcpy(&raw_data[current_pos_of_function_names], (*it).first.c_str(), (*it).first.length() + 1); current_pos_of_function_names += static_cast<uint32_t>((*it).first.length() + 1); //Save function name ordinal uint16_t name_ordinal = (*it).second; memcpy(&raw_data[current_pos_of_function_name_ordinals], &name_ordinal, sizeof(name_ordinal)); current_pos_of_function_name_ordinals += sizeof(name_ordinal); } //Adjust section raw and virtual sizes pe.recalculate_section_sizes(exports_section, auto_strip_last_section); image_directory ret(pe.rva_from_section_offset(exports_section, directory_pos), needed_size); //If auto-rewrite of PE headers is required if(save_to_pe_header) { pe.set_directory_rva(image_directory_entry_export, ret.get_rva()); pe.set_directory_size(image_directory_entry_export, ret.get_size()); } return ret; }
//Returns array of exported functions and information about export (if info != 0) const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info) { //Returned exported functions info array std::vector<exported_function> ret; if(pe.has_exports()) { //Check the length in bytes of the section containing export directory if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_export), pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true) < sizeof(image_export_directory)) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); image_export_directory exports = pe.section_data_from_rva<image_export_directory>(pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true); unsigned long max_name_length; if(info) { //Save some export info data info->set_characteristics(exports.Characteristics); info->set_major_version(exports.MajorVersion); info->set_minor_version(exports.MinorVersion); //Get byte count that we have for dll name if((max_name_length = pe.section_data_length_from_rva(exports.Name, exports.Name, section_data_virtual, true)) < 2) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Get dll name pointer const char* dll_name = pe.section_data_from_rva(exports.Name, section_data_virtual, true); //Check for null-termination if(!pe_utils::is_null_terminated(dll_name, max_name_length)) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Save the rest of export information data info->set_name(dll_name); info->set_number_of_functions(exports.NumberOfFunctions); info->set_number_of_names(exports.NumberOfNames); info->set_ordinal_base(exports.Base); info->set_rva_of_functions(exports.AddressOfFunctions); info->set_rva_of_names(exports.AddressOfNames); info->set_rva_of_name_ordinals(exports.AddressOfNameOrdinals); info->set_timestamp(exports.TimeDateStamp); } if(!exports.NumberOfFunctions) return ret; //Check IMAGE_EXPORT_DIRECTORY fields if(exports.NumberOfNames > exports.NumberOfFunctions) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Check some export directory fields if((!exports.AddressOfNameOrdinals && exports.AddressOfNames) || (exports.AddressOfNameOrdinals && !exports.AddressOfNames) || !exports.AddressOfFunctions || exports.NumberOfFunctions >= pe_utils::max_dword / sizeof(uint32_t) || exports.NumberOfNames > pe_utils::max_dword / sizeof(uint32_t) || !pe_utils::is_sum_safe(exports.AddressOfFunctions, exports.NumberOfFunctions * sizeof(uint32_t)) || !pe_utils::is_sum_safe(exports.AddressOfNames, exports.NumberOfNames * sizeof(uint32_t)) || !pe_utils::is_sum_safe(exports.AddressOfNameOrdinals, exports.NumberOfFunctions * sizeof(uint32_t)) || !pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_export), pe.get_directory_size(image_directory_entry_export))) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Check if it is enough bytes to hold AddressOfFunctions table if(pe.section_data_length_from_rva(exports.AddressOfFunctions, exports.AddressOfFunctions, section_data_virtual, true) < exports.NumberOfFunctions * sizeof(uint32_t)) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); if(exports.AddressOfNames) { //Check if it is enough bytes to hold name and ordinal tables if(pe.section_data_length_from_rva(exports.AddressOfNameOrdinals, exports.AddressOfNameOrdinals, section_data_virtual, true) < exports.NumberOfNames * sizeof(uint16_t)) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); if(pe.section_data_length_from_rva(exports.AddressOfNames, exports.AddressOfNames, section_data_virtual, true) < exports.NumberOfNames * sizeof(uint32_t)) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); } for(uint32_t ordinal = 0; ordinal < exports.NumberOfFunctions; ordinal++) { //Get function address //Sum and multiplication are safe (checked above) uint32_t rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfFunctions + ordinal * sizeof(uint32_t), section_data_virtual, true); //If we have a skip if(!rva) continue; exported_function func; func.set_rva(rva); if(!pe_utils::is_sum_safe(exports.Base, ordinal) || exports.Base + ordinal > pe_utils::max_word) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); func.set_ordinal(static_cast<uint16_t>(ordinal + exports.Base)); //Scan for function name ordinal for(uint32_t i = 0; i < exports.NumberOfNames; i++) { uint16_t ordinal2 = pe.section_data_from_rva<uint16_t>(exports.AddressOfNameOrdinals + i * sizeof(uint16_t), section_data_virtual, true); //If function has name (and name ordinal) if(ordinal == ordinal2) { //Get function name //Sum and multiplication are safe (checked above) uint32_t function_name_rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfNames + i * sizeof(uint32_t), section_data_virtual, true); //Get byte count that we have for function name if((max_name_length = pe.section_data_length_from_rva(function_name_rva, function_name_rva, section_data_virtual, true)) < 2) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Get function name pointer const char* func_name = pe.section_data_from_rva(function_name_rva, section_data_virtual, true); //Check for null-termination if(!pe_utils::is_null_terminated(func_name, max_name_length)) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Save function info func.set_name(func_name); func.set_name_ordinal(ordinal2); //If the function is just a redirect, save its name if(rva >= pe.get_directory_rva(image_directory_entry_export) + sizeof(image_directory_entry_export) && rva < pe.get_directory_rva(image_directory_entry_export) + pe.get_directory_size(image_directory_entry_export)) { if((max_name_length = pe.section_data_length_from_rva(rva, rva, section_data_virtual, true)) < 2) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Get forwarded function name pointer const char* forwarded_func_name = pe.section_data_from_rva(rva, section_data_virtual, true); //Check for null-termination if(!pe_utils::is_null_terminated(forwarded_func_name, max_name_length)) throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); //Set the name of forwarded function func.set_forwarded_name(forwarded_func_name); } break; } } //Add function info to output array ret.push_back(func); } } return ret; }
//Rebuild PE image and write it to "out" ostream //If strip_dos_header is true, DOS headers partially will be used for PE headers //If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically //If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) { if(out.bad()) throw pe_exception("Stream is bad", pe_exception::stream_is_bad); if(save_bound_import && pe.has_bound_import()) { if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true) < pe.get_directory_size(image_directory_entry_bound_import)) throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); } //Change ostream state out.exceptions(std::ios::goodbit); out.clear(); uint32_t original_bound_import_rva = pe.has_bound_import() ? pe.get_directory_rva(image_directory_entry_bound_import) : 0; if(original_bound_import_rva && original_bound_import_rva > pe.get_size_of_headers()) { //No need to do anything with bound import directory //if it is placed inside of any section, not headers original_bound_import_rva = 0; save_bound_import = false; } { image_dos_header dos_header; //Rebuild PE image headers rebuild_pe(pe, dos_header, strip_dos_header, change_size_of_headers, save_bound_import); //Write DOS header out.write(reinterpret_cast<const char*>(&dos_header), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)); } //If we have stub overlay, write it too { const std::string& stub = pe.get_stub_overlay(); if(stub.size()) { out.write(stub.data(), stub.size()); size_t aligned_size = pe_utils::align_up(stub.size(), sizeof(uint32_t)); //Align PE header, which is right after rich overlay while(aligned_size > stub.size()) { out.put('\0'); --aligned_size; } } } //Write NT headers out.write(static_cast<const pe_base&>(pe).get_nt_headers_ptr(), pe.get_sizeof_nt_header() - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())); //Write section headers const section_list& sections = pe.get_image_sections(); for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) { out.write(reinterpret_cast<const char*>(&(*it).get_raw_header()), sizeof(image_section_header)); } //Write bound import data if requested if(save_bound_import && pe.has_bound_import()) { out.write(pe.section_data_from_rva(original_bound_import_rva, section_data_raw, true), pe.get_directory_size(image_directory_entry_bound_import)); } //Write section data finally for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) { const section& s = *it; std::streamoff wpos = out.tellp(); //Fill unused overlay data between sections with null bytes for(unsigned int i = 0; i < s.get_pointer_to_raw_data() - wpos; i++) out.put(0); //Write raw section data out.write(s.get_raw_data().data(), s.get_size_of_raw_data()); } }
//Helper function to rebuild resource directory void rebuild_resource_directory(pe_base& pe, section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start) { //Create resource directory image_resource_directory dir = {0}; dir.Characteristics = root.get_characteristics(); dir.MajorVersion = root.get_major_version(); dir.MinorVersion = root.get_minor_version(); dir.TimeDateStamp = root.get_timestamp(); { resource_directory::entry_list& entries = root.get_entry_list(); std::sort(entries.begin(), entries.end(), entry_sorter()); } //Calculate number of named and ID entries for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) { if((*it).is_named()) ++dir.NumberOfNamedEntries; else ++dir.NumberOfIdEntries; } std::string& raw_data = resource_section.get_raw_data(); //Save resource directory memcpy(&raw_data[current_structures_pos], &dir, sizeof(dir)); current_structures_pos += sizeof(dir); uint32_t this_current_structures_pos = current_structures_pos; current_structures_pos += sizeof(image_resource_directory_entry) * (dir.NumberOfNamedEntries + dir.NumberOfIdEntries); //Create all resource directory entries for(resource_directory::entry_list::iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) { image_resource_directory_entry entry; if((*it).is_named()) { entry.NameItem.Name = 0x80000000 | (current_strings_pos - offset_from_section_start); uint16_t unicode_length = static_cast<uint16_t>((*it).get_name().length()); memcpy(&raw_data[current_strings_pos], &unicode_length, sizeof(unicode_length)); current_strings_pos += sizeof(unicode_length); #ifdef PE_BLISS_WINDOWS memcpy(&raw_data[current_strings_pos], (*it).get_name().c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); #else { u16string str(pe_utils::to_ucs2((*it).get_name())); memcpy(&raw_data[current_strings_pos], str.c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); } #endif current_strings_pos += static_cast<uint32_t>((*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); } else { entry.NameItem.Name = (*it).get_id(); } if((*it).includes_data()) { current_data_pos = pe_utils::align_up(current_data_pos, sizeof(uint32_t)); image_resource_data_entry data_entry = {0}; data_entry.CodePage = (*it).get_data_entry().get_codepage(); data_entry.Size = static_cast<uint32_t>((*it).get_data_entry().get_data().length()); data_entry.OffsetToData = pe.rva_from_section_offset(resource_section, current_data_pos + sizeof(data_entry)); entry.DirItem.OffsetToData = current_data_pos - offset_from_section_start; memcpy(&raw_data[current_data_pos], &data_entry, sizeof(data_entry)); current_data_pos += sizeof(data_entry); memcpy(&raw_data[current_data_pos], (*it).get_data_entry().get_data().data(), data_entry.Size); current_data_pos += data_entry.Size; memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); this_current_structures_pos += sizeof(entry); } else { entry.DirItem.OffsetToData = 0x80000000 | (current_structures_pos - offset_from_section_start); memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); this_current_structures_pos += sizeof(entry); rebuild_resource_directory(pe, resource_section, (*it).get_resource_directory(), current_structures_pos, current_data_pos, current_strings_pos, offset_from_section_start); } } }
//imports - bound imported modules list //imports_section - section where export directory will be placed (must be attached to PE image) //offset_from_section_start - offset from imports_section raw data start //save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers //auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) { //Check that exports_section is attached to this PE image if(!pe.section_attached(imports_section)) throw pe_exception("Bound import section must be attached to PE file", pe_exception::section_is_not_attached); uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); uint32_t needed_size = sizeof(image_bound_import_descriptor) /* Ending null descriptor */; uint32_t needed_size_for_strings = 0; //Calculate needed size for bound import data for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) { const bound_import& import = *it; needed_size += sizeof(image_bound_import_descriptor); needed_size_for_strings += static_cast<uint32_t>((*it).get_module_name().length()) + 1 /* nullbyte */; const bound_import::ref_list& refs = import.get_module_ref_list(); for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) { needed_size_for_strings += static_cast<uint32_t>((*ref_it).get_module_name().length()) + 1 /* nullbyte */; needed_size += sizeof(image_bound_forwarder_ref); } } needed_size += needed_size_for_strings; //Check if imports_section is last one. If it's not, check if there's enough place for bound import data if(&imports_section != &*(pe.get_image_sections().end() - 1) && (imports_section.empty() || pe_utils::align_up(imports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) throw pe_exception("Insufficient space for bound import directory", pe_exception::insufficient_space); std::string& raw_data = imports_section.get_raw_data(); //This will be done only if imports_section is the last section of image or for section with unaligned raw length of data if(raw_data.length() < needed_size + directory_pos) raw_data.resize(needed_size + directory_pos); //Expand section raw data uint32_t current_pos_for_structures = directory_pos; uint32_t current_pos_for_strings = current_pos_for_structures + needed_size - needed_size_for_strings; for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) { const bound_import& import = *it; image_bound_import_descriptor descriptor; descriptor.NumberOfModuleForwarderRefs = static_cast<uint16_t>(import.get_module_ref_list().size()); descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); descriptor.TimeDateStamp = import.get_timestamp(); memcpy(&raw_data[current_pos_for_structures], &descriptor, sizeof(descriptor)); current_pos_for_structures += sizeof(descriptor); size_t length = import.get_module_name().length() + 1 /* nullbyte */; memcpy(&raw_data[current_pos_for_strings], import.get_module_name().c_str(), length); current_pos_for_strings += static_cast<uint32_t>(length); const bound_import::ref_list& refs = import.get_module_ref_list(); for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) { const bound_import_ref& ref = *ref_it; image_bound_forwarder_ref ref_descriptor = {0}; ref_descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); ref_descriptor.TimeDateStamp = ref.get_timestamp(); memcpy(&raw_data[current_pos_for_structures], &ref_descriptor, sizeof(ref_descriptor)); current_pos_for_structures += sizeof(ref_descriptor); length = ref.get_module_name().length() + 1 /* nullbyte */; memcpy(&raw_data[current_pos_for_strings], ref.get_module_name().c_str(), length); current_pos_for_strings += static_cast<uint32_t>(length); } } //Adjust section raw and virtual sizes pe.recalculate_section_sizes(imports_section, auto_strip_last_section); image_directory ret(pe.rva_from_section_offset(imports_section, directory_pos), needed_size); //If auto-rewrite of PE headers is required if(save_to_pe_header) { pe.set_directory_rva(image_directory_entry_bound_import, ret.get_rva()); pe.set_directory_size(image_directory_entry_bound_import, ret.get_size()); } return ret; }
//Recalculates image base with the help of relocation tables void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) { pe.get_pe_type() == pe_type_32 ? rebase_image_base<pe_types_class_32>(pe, tables, new_base) : rebase_image_base<pe_types_class_64>(pe, tables, new_base); }
//Simple relocations rebuilder //To keep PE file working, don't remove any of existing relocations in //relocation_table_list returned by a call to get_relocations() function //auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped //offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated //If save_to_pe_header is true, PE header will be modified automatically const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) { //Check that reloc_section is attached to this PE image if(!pe.section_attached(reloc_section)) throw pe_exception("Relocations section must be attached to PE file", pe_exception::section_is_not_attached); uint32_t current_reloc_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); uint32_t needed_size = current_reloc_data_pos - offset_from_section_start; //Calculate needed size for relocation tables uint32_t size_delta = needed_size; uint32_t start_reloc_pos = current_reloc_data_pos; //Enumerate relocation tables for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) { needed_size += static_cast<uint32_t>((*it).get_relocations().size() * sizeof(uint16_t) /* relocations */ + sizeof(image_base_relocation) /* table header */); //End of each table will be DWORD-aligned if((start_reloc_pos + needed_size - size_delta) % sizeof(uint32_t)) needed_size += sizeof(uint16_t); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation } //Check if reloc_section is last one. If it's not, check if there's enough place for relocations data if(&reloc_section != &*(pe.get_image_sections().end() - 1) && (reloc_section.empty() || pe_utils::align_up(reloc_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + current_reloc_data_pos)) throw pe_exception("Insufficient space for relocations directory", pe_exception::insufficient_space); std::string& raw_data = reloc_section.get_raw_data(); //This will be done only if reloc_section is the last section of image or for section with unaligned raw length of data if(raw_data.length() < needed_size + current_reloc_data_pos) raw_data.resize(pe_utils::align_up(needed_size + current_reloc_data_pos, pe.get_file_alignment())); //Expand section raw data //Enumerate relocation tables for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) { //Create relocation table header image_base_relocation reloc; reloc.VirtualAddress = (*it).get_rva(); const relocation_table::relocation_list& reloc_list = (*it).get_relocations(); reloc.SizeOfBlock = static_cast<uint32_t>(sizeof(image_base_relocation) + sizeof(uint16_t) * reloc_list.size()); if((reloc_list.size() * sizeof(uint16_t)) % sizeof(uint32_t)) //If we must align end of relocation table reloc.SizeOfBlock += sizeof(uint16_t); memcpy(&raw_data[current_reloc_data_pos], &reloc, sizeof(reloc)); current_reloc_data_pos += sizeof(reloc); //Enumerate relocations in table for(relocation_table::relocation_list::const_iterator r = reloc_list.begin(); r != reloc_list.end(); ++r) { //Save relocations uint16_t reloc_value = (*r).get_item(); memcpy(&raw_data[current_reloc_data_pos], &reloc_value, sizeof(reloc_value)); current_reloc_data_pos += sizeof(reloc_value); } if(current_reloc_data_pos % sizeof(uint32_t)) //If end of table is not DWORD-aligned { memset(&raw_data[current_reloc_data_pos], 0, sizeof(uint16_t)); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation current_reloc_data_pos += sizeof(uint16_t); } } image_directory ret(pe.rva_from_section_offset(reloc_section, start_reloc_pos), needed_size - size_delta); //Adjust section raw and virtual sizes pe.recalculate_section_sizes(reloc_section, auto_strip_last_section); //If auto-rewrite of PE headers is required if(save_to_pe_header) { pe.set_directory_rva(image_directory_entry_basereloc, ret.get_rva()); pe.set_directory_size(image_directory_entry_basereloc, ret.get_size()); pe.clear_characteristics_flags(image_file_relocs_stripped); pe.set_dll_characteristics(pe.get_dll_characteristics() | image_dllcharacteristics_dynamic_base); } return ret; }
//Rebuilds PE image headers //If strip_dos_header is true, DOS headers partially will be used for PE headers //If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically //If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) void rebuild_pe(pe_base& pe, image_dos_header& dos_header, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) { dos_header = pe.get_dos_header(); if(strip_dos_header) { //Strip stub overlay pe.strip_stub_overlay(); //BaseOfCode NT Headers field now overlaps //e_lfanew field, so we're acrually setting //e_lfanew with this call pe.set_base_of_code(8 * sizeof(uint16_t)); } else { //Set start of PE headers dos_header.e_lfanew = sizeof(image_dos_header) + pe_utils::align_up(static_cast<uint32_t>(pe.get_stub_overlay().size()), sizeof(uint32_t)); } section_list& sections = pe.get_image_sections(); //Calculate pointer to section data size_t ptr_to_section_data = (strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + pe.get_sizeof_nt_header() + pe_utils::align_up(pe.get_stub_overlay().size(), sizeof(uint32_t)) - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()) + sections.size() * sizeof(image_section_header); if(save_bound_import && pe.has_bound_import()) { //It will be aligned to DWORD, because we're aligning to DWORD everything above it pe.set_directory_rva(image_directory_entry_bound_import, static_cast<uint32_t>(ptr_to_section_data)); ptr_to_section_data += pe.get_directory_size(image_directory_entry_bound_import); } ptr_to_section_data = pe_utils::align_up(ptr_to_section_data, pe.get_file_alignment()); //Set size of headers and size of optional header if(change_size_of_headers) { if(!pe.get_image_sections().empty()) { if(static_cast<uint32_t>(ptr_to_section_data) > (*sections.begin()).get_virtual_address()) throw pe_exception("Headers of PE file are too long. Try to strip STUB or don't build bound import", pe_exception::cannot_rebuild_image); } pe.set_size_of_headers(static_cast<uint32_t>(ptr_to_section_data)); } //Set number of sections in PE header pe.update_number_of_sections(); pe.update_image_size(); pe.set_size_of_optional_header(static_cast<uint16_t>(pe.get_sizeof_opt_headers() - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()))); //Recalculate pointer to raw data according to section list for(section_list::iterator it = sections.begin(); it != sections.end(); ++it) { //Save section headers PointerToRawData if((*it).get_size_of_raw_data()) { (*it).set_pointer_to_raw_data(static_cast<uint32_t>(ptr_to_section_data)); ptr_to_section_data += (*it).get_aligned_raw_size(pe.get_file_alignment()); } } }
//Processes resource directory const resource_directory process_resource_directory(const pe_base& pe, uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed) { resource_directory ret; //Check for resource loops if(!processed.insert(offset_to_directory).second) throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); if(!pe_utils::is_sum_safe(res_rva, offset_to_directory)) throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); //Get root IMAGE_RESOURCE_DIRECTORY image_resource_directory directory = pe.section_data_from_rva<image_resource_directory>(res_rva + offset_to_directory, section_data_virtual, true); ret = resource_directory(directory); //Check DWORDs for possible overflows if(!pe_utils::is_sum_safe(directory.NumberOfIdEntries, directory.NumberOfNamedEntries) || directory.NumberOfIdEntries + directory.NumberOfNamedEntries >= pe_utils::max_dword / sizeof(image_resource_directory_entry) + sizeof(image_resource_directory)) throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); if(!pe_utils::is_sum_safe(offset_to_directory, sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry)) || !pe_utils::is_sum_safe(res_rva, offset_to_directory + sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry))) throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); for(uint32_t i = 0; i != static_cast<uint32_t>(directory.NumberOfIdEntries) + directory.NumberOfNamedEntries; ++i) { //Read directory entries one by one image_resource_directory_entry dir_entry = pe.section_data_from_rva<image_resource_directory_entry>( res_rva + sizeof(image_resource_directory) + i * sizeof(image_resource_directory_entry) + offset_to_directory, section_data_virtual, true); //Create directory entry structure resource_directory_entry entry; //If directory is named const auto& nameEntry = dir_entry.NameItem.NameEntry; if(nameEntry.NameIsString) { if(!pe_utils::is_sum_safe(res_rva + sizeof(uint16_t) /* safe */, nameEntry.NameOffset)) throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); //get directory name length uint16_t directory_name_length = pe.section_data_from_rva<uint16_t>(res_rva + nameEntry.NameOffset, section_data_virtual, true); //Check name length if(pe.section_data_length_from_rva(res_rva + nameEntry.NameOffset + sizeof(uint16_t), res_rva + nameEntry.NameOffset + sizeof(uint16_t), section_data_virtual, true) < directory_name_length) throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); #ifdef PE_BLISS_WINDOWS //Set entry UNICODE name entry.set_name(std::wstring( reinterpret_cast<const wchar_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameEntry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), directory_name_length)); #else //Set entry UNICODE name entry.set_name(pe_utils::from_ucs2(u16string( reinterpret_cast<const unicode16_t*>(pe.section_data_from_rva(res_rva + nameEntry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), directory_name_length))); #endif } else { //Else - set directory ID entry.set_id(dir_entry.NameItem.Id); } //If directory entry has another resource directory const auto& dirEntry = dir_entry.DirItem.DirEntry; if(dirEntry.DataIsDirectory) { entry.add_resource_directory(process_resource_directory(pe, res_rva, dirEntry.OffsetToDirectory, processed)); } else { //If directory entry has data image_resource_data_entry data_entry = pe.section_data_from_rva<image_resource_data_entry>( res_rva + dir_entry.DirItem.OffsetToData, section_data_virtual, true); //Check byte count that stated by data entry if(pe.section_data_length_from_rva(data_entry.OffsetToData, data_entry.OffsetToData, section_data_virtual, true) < data_entry.Size) throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); //Add data entry to directory entry entry.add_data_entry(resource_data_entry( std::string(pe.section_data_from_rva(data_entry.OffsetToData, section_data_virtual, true), data_entry.Size), data_entry.CodePage)); } //Save directory entry ret.add_resource_directory_entry(entry); } //Return resource directory return ret; }