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