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); }
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; }
//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()); } }
//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; }