/*************************************************************************/ /* Copyright (c) 2015 dx, http://kaimi.ru */ /* */ /* Permission is hereby granted, free of charge, to any person */ /* obtaining a copy of this software and associated documentation */ /* files (the "Software"), to deal in the Software without */ /* restriction, including without limitation the rights to use, */ /* copy, modify, merge, publish, distribute, sublicense, and/or */ /* sell copies of the Software, and to permit persons to whom the */ /* Software is furnished to do so, subject to the following conditions: */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include #include #include #include #include #include #include #include #include "pe_exception.h" #include "pe_base.h" namespace pe_bliss { using namespace pe_win; //Constructor pe_base::pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data) { props_ = props.duplicate().release(); //Save istream state std::ios_base::iostate state = file.exceptions(); std::streamoff old_offset = file.tellg(); try { file.exceptions(std::ios::goodbit); //Read DOS header, PE headers and section data read_dos_header(file); read_pe(file, read_debug_raw_data); } catch(const std::exception&) { //If something went wrong, restore istream state file.seekg(old_offset); file.exceptions(state); file.clear(); //Rethrow throw; } //Restore istream state file.seekg(old_offset); file.exceptions(state); file.clear(); } pe_base::pe_base(const pe_properties& props, uint32_t section_alignment, bool dll, uint16_t subsystem) { props_ = props.duplicate().release(); props_->create_pe(section_alignment, subsystem); has_overlay_ = false; memset(&dos_header_, 0, sizeof(dos_header_)); dos_header_.e_magic = 0x5A4D; //"MZ" //Magic numbers from MSVC++ build dos_header_.e_maxalloc = 0xFFFF; dos_header_.e_cblp = 0x90; dos_header_.e_cp = 3; dos_header_.e_cparhdr = 4; dos_header_.e_sp = 0xB8; dos_header_.e_lfarlc = 64; set_characteristics(image_file_executable_image | image_file_relocs_stripped); if(get_pe_type() == pe_type_32) set_characteristics_flags(image_file_32bit_machine); if(dll) set_characteristics_flags(image_file_dll); set_subsystem_version(5, 1); //WinXP set_os_version(5, 1); //WinXP } pe_base::pe_base(const pe_base& pe) :dos_header_(pe.dos_header_), rich_overlay_(pe.rich_overlay_), sections_(pe.sections_), has_overlay_(pe.has_overlay_), full_headers_data_(pe.full_headers_data_), debug_data_(pe.debug_data_), props_(0) { props_ = pe.props_->duplicate().release(); } pe_base& pe_base::operator=(const pe_base& pe) { dos_header_ = pe.dos_header_; rich_overlay_ = pe.rich_overlay_; sections_ = pe.sections_; has_overlay_ = pe.has_overlay_; full_headers_data_ = pe.full_headers_data_; debug_data_ = pe.debug_data_; delete props_; props_ = 0; props_ = pe.props_->duplicate().release(); return *this; } pe_base::~pe_base() { delete props_; } //Returns dos header const image_dos_header& pe_base::get_dos_header() const { return dos_header_; } //Returns dos header image_dos_header& pe_base::get_dos_header() { return dos_header_; } //Returns PE headers start position (e_lfanew) int32_t pe_base::get_pe_header_start() const { return dos_header_.e_lfanew; } //Strips MSVC stub overlay void pe_base::strip_stub_overlay() { rich_overlay_.clear(); } //Fills MSVC stub overlay with character c void pe_base::fill_stub_overlay(char c) { if(rich_overlay_.length()) rich_overlay_.assign(rich_overlay_.length(), c); } //Sets stub MSVS overlay void pe_base::set_stub_overlay(const std::string& data) { rich_overlay_ = data; } //Returns stub overlay const std::string& pe_base::get_stub_overlay() const { return rich_overlay_; } //Realigns all sections void pe_base::realign_all_sections() { for(unsigned int i = 0; i < sections_.size(); i++) realign_section(i); } //Returns number of sections from PE header uint16_t pe_base::get_number_of_sections() const { return props_->get_number_of_sections(); } //Updates number of sections in PE header uint16_t pe_base::update_number_of_sections() { uint16_t new_number = static_cast(sections_.size()); props_->set_number_of_sections(new_number); return new_number; } //Returns section alignment uint32_t pe_base::get_section_alignment() const { return props_->get_section_alignment(); } //Returns image sections list section_list& pe_base::get_image_sections() { return sections_; } //Returns image sections list const section_list& pe_base::get_image_sections() const { return sections_; } //Realigns section by index void pe_base::realign_section(uint32_t index) { //Check index if(sections_.size() <= index) throw pe_exception("Section not found", pe_exception::section_not_found); //Get section iterator section_list::iterator it = sections_.begin() + index; section& s = *it; //Calculate, how many null bytes we have in the end of raw section data std::size_t strip = 0; for(std::size_t i = (*it).get_raw_data().length(); i >= 1; --i) { if(s.get_raw_data()[i - 1] == 0) strip++; else break; } if(it == sections_.end() - 1) //If we're realigning the last section { //We can strip ending null bytes s.set_size_of_raw_data(static_cast(s.get_raw_data().length() - strip)); s.get_raw_data().resize(s.get_raw_data().length() - strip, 0); } else { //Else just set size of raw data uint32_t raw_size_aligned = s.get_aligned_raw_size(get_file_alignment()); s.set_size_of_raw_data(raw_size_aligned); s.get_raw_data().resize(raw_size_aligned, 0); } } //Returns file alignment uint32_t pe_base::get_file_alignment() const { return props_->get_file_alignment(); } //Sets file alignment void pe_base::set_file_alignment(uint32_t alignment) { //Check alignment if(alignment < minimum_file_alignment) throw pe_exception("File alignment can't be less than 512", pe_exception::incorrect_file_alignment); if(!pe_utils::is_power_of_2(alignment)) throw pe_exception("File alignment must be a power of 2", pe_exception::incorrect_file_alignment); if(alignment > get_section_alignment()) throw pe_exception("File alignment must be <= section alignment", pe_exception::incorrect_file_alignment); //Set file alignment without any additional checks set_file_alignment_unchecked(alignment); } //Returns size of image uint32_t pe_base::get_size_of_image() const { return props_->get_size_of_image(); } //Returns image entry point uint32_t pe_base::get_ep() const { return props_->get_ep(); } //Sets image entry point (just a value of PE header) void pe_base::set_ep(uint32_t new_ep) { props_->set_ep(new_ep); } //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) uint32_t pe_base::get_number_of_rvas_and_sizes() const { return props_->get_number_of_rvas_and_sizes(); } //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) void pe_base::set_number_of_rvas_and_sizes(uint32_t number) { props_->set_number_of_rvas_and_sizes(number); } //Returns PE characteristics uint16_t pe_base::get_characteristics() const { return props_->get_characteristics(); } //Sets PE characteristics (a value inside header) void pe_base::set_characteristics(uint16_t ch) { props_->set_characteristics(ch); } //Returns section from RVA section& pe_base::section_from_rva(uint32_t rva) { //Search for section for(section_list::iterator i = sections_.begin(); i != sections_.end(); ++i) { section& s = *i; //Return section if found if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) return s; } throw pe_exception("No section found by presented address", pe_exception::no_section_found); } //Returns section from RVA const section& pe_base::section_from_rva(uint32_t rva) const { //Search for section for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) { const section& s = *i; //Return section if found if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) return s; } throw pe_exception("No section found by presented address", pe_exception::no_section_found); } //Returns section from directory ID section& pe_base::section_from_directory(uint32_t directory_id) { return section_from_rva(get_directory_rva(directory_id)); } //Returns section from directory ID const section& pe_base::section_from_directory(uint32_t directory_id) const { return section_from_rva(get_directory_rva(directory_id)); } //Sets section virtual size (actual for the last one of this PE or for unbound section) void pe_base::set_section_virtual_size(section& s, uint32_t vsize) { //Check if we're changing virtual size of the last section //Of course, we can change virtual size of section that's not bound to this PE file if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1) throw pe_exception("Can't change virtual size of any section, except last one", pe_exception::error_changing_section_virtual_size); //If we're setting virtual size to zero if(vsize == 0) { //Check if section is empty if(s.empty()) throw pe_exception("Cannot set virtual size of empty section to zero", pe_exception::error_changing_section_virtual_size); //Set virtual size equal to aligned size of raw data s.set_virtual_size(s.get_size_of_raw_data()); } else { s.set_virtual_size(vsize); } //Update image size if we're changing virtual size for the last section of this PE if(!sections_.empty() || &s == &(*(sections_.end() - 1))) update_image_size(); } //Expands section raw or virtual size to hold data from specified RVA with specified size //Section must be free (not bound to any image) //or the last section of this image bool pe_base::expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand) { //Check if we're changing the last section //Of course, we can change the section that's not bound to this PE file if(sections_.empty() || std::find_if(sections_.begin(), sections_.end() - 1, section_ptr_finder(s)) != sections_.end() - 1) throw pe_exception("Can't expand any section, except last one", pe_exception::error_expanding_section); //Check if we should expand our section if(expand == expand_section_raw && section_data_length_from_rva(s, needed_rva, section_data_raw) < needed_size) { //Expand section raw data s.get_raw_data().resize(needed_rva - s.get_virtual_address() + needed_size); recalculate_section_sizes(s, false); return true; } else if(expand == expand_section_virtual && section_data_length_from_rva(s, needed_rva, section_data_virtual) < needed_size) { //Expand section virtual data set_section_virtual_size(s, needed_rva - s.get_virtual_address() + needed_size); return true; } return false; } //Updates image virtual size void pe_base::update_image_size() { //Write virtual size of image to headers if(!sections_.empty()) set_size_of_image(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment())); else set_size_of_image(get_size_of_headers()); } //Returns checksum of PE file from header uint32_t pe_base::get_checksum() const { return props_->get_checksum(); } //Sets checksum of PE file void pe_base::set_checksum(uint32_t checksum) { props_->set_checksum(checksum); } //Returns timestamp of PE file from header uint32_t pe_base::get_time_date_stamp() const { return props_->get_time_date_stamp(); } //Sets timestamp of PE file void pe_base::set_time_date_stamp(uint32_t timestamp) { props_->set_time_date_stamp(timestamp); } //Returns Machine field value of PE file from header uint16_t pe_base::get_machine() const { return props_->get_machine(); } //Sets Machine field value of PE file void pe_base::set_machine(uint16_t machine) { props_->set_machine(machine); } //Prepares section before attaching it void pe_base::prepare_section(section& s) { //Calculate its size of raw data s.set_size_of_raw_data(static_cast(pe_utils::align_up(s.get_raw_data().length(), get_file_alignment()))); //Check section virtual and raw size if(!s.get_size_of_raw_data() && !s.get_virtual_size()) throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); //If section virtual size is zero if(!s.get_virtual_size()) { s.set_virtual_size(s.get_size_of_raw_data()); } else { //Else calculate its virtual size s.set_virtual_size( std::max(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()), pe_utils::align_up(s.get_virtual_size(), get_section_alignment()))); } } //Adds section to image section& pe_base::add_section(section s) { if(sections_.size() >= maximum_number_of_sections) throw pe_exception("Maximum number of sections has been reached", pe_exception::no_more_sections_can_be_added); //Prepare section before adding it prepare_section(s); //Calculate section virtual address if(!sections_.empty()) { s.set_virtual_address(pe_utils::align_up(sections_.back().get_virtual_address() + sections_.back().get_aligned_virtual_size(get_section_alignment()), get_section_alignment())); //We should align last section raw size, if it wasn't aligned section& last = sections_.back(); last.set_size_of_raw_data(static_cast(pe_utils::align_up(last.get_raw_data().length(), get_file_alignment()))); } else { s.set_virtual_address( s.get_virtual_address() == 0 ? pe_utils::align_up(get_size_of_headers(), get_section_alignment()) : pe_utils::align_up(s.get_virtual_address(), get_section_alignment())); } //Add section to the end of section list sections_.push_back(s); //Set number of sections in PE header set_number_of_sections(static_cast(sections_.size())); //Recalculate virtual size of image set_size_of_image(get_size_of_image() + s.get_aligned_virtual_size(get_section_alignment())); //Return last section return sections_.back(); } //Returns true if sectios "s" is already attached to this PE file bool pe_base::section_attached(const section& s) const { return sections_.end() != std::find_if(sections_.begin(), sections_.end(), section_ptr_finder(s)); } //Returns true if directory exists bool pe_base::directory_exists(uint32_t id) const { return props_->directory_exists(id); } //Removes directory void pe_base::remove_directory(uint32_t id) { props_->remove_directory(id); } //Returns directory RVA uint32_t pe_base::get_directory_rva(uint32_t id) const { return props_->get_directory_rva(id); } //Returns directory size uint32_t pe_base::get_directory_size(uint32_t id) const { return props_->get_directory_size(id); } //Sets directory RVA (just a value of PE header, no moving occurs) void pe_base::set_directory_rva(uint32_t id, uint32_t rva) { return props_->set_directory_rva(id, rva); } //Sets directory size (just a value of PE header, no moving occurs) void pe_base::set_directory_size(uint32_t id, uint32_t size) { return props_->set_directory_size(id, size); } //Strips only zero DATA_DIRECTORY entries to count = min_count //Returns resulting number of data directories //strip_iat_directory - if true, even not empty IAT directory will be stripped uint32_t pe_base::strip_data_directories(uint32_t min_count, bool strip_iat_directory) { return props_->strip_data_directories(min_count, strip_iat_directory); } //Returns true if image has import directory bool pe_base::has_imports() const { return directory_exists(image_directory_entry_import); } //Returns true if image has export directory bool pe_base::has_exports() const { return directory_exists(image_directory_entry_export); } //Returns true if image has resource directory bool pe_base::has_resources() const { return directory_exists(image_directory_entry_resource); } //Returns true if image has security directory bool pe_base::has_security() const { return directory_exists(image_directory_entry_security); } //Returns true if image has relocations bool pe_base::has_reloc() const { return directory_exists(image_directory_entry_basereloc) && !(get_characteristics() & image_file_relocs_stripped); } //Returns true if image has TLS directory bool pe_base::has_tls() const { return directory_exists(image_directory_entry_tls); } //Returns true if image has config directory bool pe_base::has_config() const { return directory_exists(image_directory_entry_load_config); } //Returns true if image has bound import directory bool pe_base::has_bound_import() const { return directory_exists(image_directory_entry_bound_import); } //Returns true if image has delay import directory bool pe_base::has_delay_import() const { return directory_exists(image_directory_entry_delay_import); } //Returns true if image has COM directory bool pe_base::is_dotnet() const { return directory_exists(image_directory_entry_com_descriptor); } //Returns true if image has exception directory bool pe_base::has_exception_directory() const { return directory_exists(image_directory_entry_exception); } //Returns true if image has debug directory bool pe_base::has_debug() const { return directory_exists(image_directory_entry_debug); } //Returns corresponding section data pointer from RVA inside section "s" (checks bounds) char* pe_base::section_data_from_rva(section& s, uint32_t rva) { //Check if RVA is inside section "s" if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) { if(s.get_raw_data().empty()) throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); return &s.get_raw_data()[rva - s.get_virtual_address()]; } throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); } //Returns corresponding section data pointer from RVA inside section "s" (checks bounds) const char* pe_base::section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype) const { //Check if RVA is inside section "s" if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); } //Returns section TOTAL RAW/VIRTUAL data length from RVA inside section uint32_t pe_base::section_data_length_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const { //if RVA is inside of headers and we're searching them too... if(include_headers && rva < full_headers_data_.length()) return static_cast(full_headers_data_.length()); const section& s = section_from_rva(rva); return static_cast(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())); } //Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 uint32_t pe_base::section_data_length_from_va(uint32_t va, section_data_type datatype, bool include_headers) const { return section_data_length_from_rva(va_to_rva(va), datatype, include_headers); } //Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32/PE64 uint32_t pe_base::section_data_length_from_va(uint64_t va, section_data_type datatype, bool include_headers) const { return section_data_length_from_rva(va_to_rva(va), datatype, include_headers); } //Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva" uint32_t pe_base::section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype, bool include_headers) const { //if RVAs are inside of headers and we're searching them too... if(include_headers && rva < full_headers_data_.length() && rva_inside < full_headers_data_.length()) return static_cast(full_headers_data_.length() - rva_inside); const section& s = section_from_rva(rva); if(rva_inside < s.get_virtual_address()) throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); //Calculate remaining length of section data from "rva" address long length = static_cast(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + s.get_virtual_address() - rva_inside; if(length < 0) return 0; return static_cast(length); } //Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 uint32_t pe_base::section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype, bool include_headers) const { return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers); } //Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32/PE64 uint32_t pe_base::section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype, bool include_headers) const { return section_data_length_from_rva(va_to_rva(va), va_to_rva(va_inside), datatype, include_headers); } //Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) uint32_t pe_base::section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype) const { //Check rva_inside if(rva_inside >= s.get_virtual_address() && rva_inside < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment())) { //Calculate remaining length of section data from "rva" address int32_t length = static_cast(datatype == section_data_raw ? s.get_raw_data().length() /* instead of SizeOfRawData */ : s.get_aligned_virtual_size(get_section_alignment())) + s.get_virtual_address() - rva_inside; if(length < 0) return 0; return static_cast(length); } throw pe_exception("RVA not found inside section", pe_exception::rva_not_exists); } //Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 (checks bounds) uint32_t pe_base::section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype) const { return section_data_length_from_rva(s, va_to_rva(va_inside), datatype); } //Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32/PE64 (checks bounds) uint32_t pe_base::section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype) const { return section_data_length_from_rva(s, va_to_rva(va_inside), datatype); } //Returns corresponding section data pointer from RVA inside section char* pe_base::section_data_from_rva(uint32_t rva, bool include_headers) { //if RVA is inside of headers and we're searching them too... if(include_headers && rva < full_headers_data_.length()) return &full_headers_data_[rva]; section& s = section_from_rva(rva); if(s.get_raw_data().empty()) throw pe_exception("Section raw data is empty and cannot be changed", pe_exception::section_is_empty); return &s.get_raw_data()[rva - s.get_virtual_address()]; } //Returns corresponding section data pointer from RVA inside section const char* pe_base::section_data_from_rva(uint32_t rva, section_data_type datatype, bool include_headers) const { //if RVA is inside of headers and we're searching them too... if(include_headers && rva < full_headers_data_.length()) return &full_headers_data_[rva]; const section& s = section_from_rva(rva); return (datatype == section_data_raw ? s.get_raw_data().data() : s.get_virtual_data(get_section_alignment()).c_str()) + rva - s.get_virtual_address(); } //Reads DOS headers from istream void pe_base::read_dos_header(std::istream& file, image_dos_header& header) { //Check istream flags if(file.bad() || file.eof()) throw pe_exception("PE file stream is bad or closed.", pe_exception::bad_pe_file); //Read DOS header and check istream file.read(reinterpret_cast(&header), sizeof(image_dos_header)); if(file.bad() || file.eof()) throw pe_exception("Unable to read IMAGE_DOS_HEADER", pe_exception::bad_dos_header); //Check DOS header magic if(header.e_magic != 0x5a4d) //"MZ" throw pe_exception("IMAGE_DOS_HEADER signature is incorrect", pe_exception::bad_dos_header); } //Reads DOS headers from istream void pe_base::read_dos_header(std::istream& file) { read_dos_header(file, dos_header_); } //Reads PE image from istream void pe_base::read_pe(std::istream& file, bool read_debug_raw_data) { //Get istream size std::streamoff filesize = pe_utils::get_file_size(file); //Check if PE header is DWORD-aligned if((dos_header_.e_lfanew % sizeof(uint32_t)) != 0) throw pe_exception("PE header is not DWORD-aligned", pe_exception::bad_dos_header); //Seek to NT headers file.seekg(dos_header_.e_lfanew); if(file.bad() || file.fail()) throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); //Read NT headers file.read(get_nt_headers_ptr(), get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries); if(file.bad() || file.eof()) throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); //Check PE signature if(get_pe_signature() != 0x4550) //"PE" throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); //Check number of directories if(get_number_of_rvas_and_sizes() > image_numberof_directory_entries) set_number_of_rvas_and_sizes(image_numberof_directory_entries); if(get_number_of_rvas_and_sizes() > 0) { //Read data directory headers, if any file.read(get_nt_headers_ptr() + (get_sizeof_nt_header() - sizeof(image_data_directory) * image_numberof_directory_entries), sizeof(image_data_directory) * get_number_of_rvas_and_sizes()); if(file.bad() || file.eof()) throw pe_exception("Error reading DATA_DIRECTORY headers", pe_exception::error_reading_data_directories); } //Check section number //Images with zero section number accepted if(get_number_of_sections() > maximum_number_of_sections) throw pe_exception("Incorrect number of sections", pe_exception::section_number_incorrect); //Check PE magic if(get_magic() != get_needed_magic()) throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); //Check section alignment if(!pe_utils::is_power_of_2(get_section_alignment())) throw pe_exception("Incorrect section alignment", pe_exception::incorrect_section_alignment); //Check file alignment if(!pe_utils::is_power_of_2(get_file_alignment())) throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); if(get_file_alignment() != get_section_alignment() && (get_file_alignment() < minimum_file_alignment || get_file_alignment() > get_section_alignment())) throw pe_exception("Incorrect file alignment", pe_exception::incorrect_file_alignment); //Check size of image if(pe_utils::align_up(get_size_of_image(), get_section_alignment()) == 0) throw pe_exception("Incorrect size of image", pe_exception::incorrect_size_of_image); //Read rich data overlay / DOS stub (if any) if(static_cast(dos_header_.e_lfanew) > sizeof(image_dos_header)) { rich_overlay_.resize(dos_header_.e_lfanew - sizeof(image_dos_header)); file.seekg(sizeof(image_dos_header)); file.read(&rich_overlay_[0], dos_header_.e_lfanew - sizeof(image_dos_header)); if(file.bad() || file.eof()) throw pe_exception("Error reading 'Rich' & 'DOS stub' overlay", pe_exception::error_reading_overlay); } //Calculate first section raw position //Sum is safe here uint32_t first_section = dos_header_.e_lfanew + get_size_of_optional_header() + sizeof(image_file_header) + sizeof(uint32_t) /* Signature */; if(get_number_of_sections() > 0) { //Go to first section file.seekg(first_section); if(file.bad() || file.fail()) throw pe_exception("Cannot reach section headers", pe_exception::image_section_headers_not_found); } uint32_t last_raw_size = 0; //Read all sections for(int i = 0; i < get_number_of_sections(); i++) { section s; //Read section header file.read(reinterpret_cast(&s.get_raw_header()), sizeof(image_section_header)); if(file.bad() || file.eof()) throw pe_exception("Error reading section header", pe_exception::error_reading_section_header); //Save next section header position std::streamoff next_sect = file.tellg(); //Check section virtual and raw sizes if(!s.get_size_of_raw_data() && !s.get_virtual_size()) throw pe_exception("Virtual and Physical sizes of section can't be 0 at the same time", pe_exception::zero_section_sizes); //Check for adequate values of section fields if(!pe_utils::is_sum_safe(s.get_virtual_address(), s.get_virtual_size()) || s.get_virtual_size() > pe_utils::two_gb || !pe_utils::is_sum_safe(s.get_pointer_to_raw_data(), s.get_size_of_raw_data()) || s.get_size_of_raw_data() > pe_utils::two_gb) throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); if(s.get_size_of_raw_data() != 0) { //If section has raw data //If section raw data size is greater than virtual, fix it last_raw_size = s.get_size_of_raw_data(); if(pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment()) > pe_utils::align_up(s.get_virtual_size(), get_section_alignment())) s.set_size_of_raw_data(s.get_virtual_size()); //Check virtual and raw section sizes and addresses if(s.get_virtual_address() + pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment()) || pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment()) + s.get_size_of_raw_data() > static_cast(filesize)) throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); //Seek to section raw data file.seekg(pe_utils::align_down(s.get_pointer_to_raw_data(), get_file_alignment())); if(file.bad() || file.fail()) throw pe_exception("Cannot reach section data", pe_exception::image_section_data_not_found); //Read section raw data s.get_raw_data().resize(s.get_size_of_raw_data()); file.read(&s.get_raw_data()[0], s.get_size_of_raw_data()); if(file.bad() || file.fail()) throw pe_exception("Error reading section data", pe_exception::image_section_data_not_found); } //Check virtual address and size of section if(s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) > pe_utils::align_up(get_size_of_image(), get_section_alignment())) throw pe_exception("Incorrect section address or size", pe_exception::section_incorrect_addr_or_size); //Save section sections_.push_back(s); //Seek to the next section header file.seekg(next_sect); } //Check size of headers: SizeOfHeaders can't be larger than first section VA if(!sections_.empty() && get_size_of_headers() > sections_.front().get_virtual_address()) throw pe_exception("Incorrect size of headers", pe_exception::incorrect_size_of_headers); //If image has more than two sections if(sections_.size() >= 2) { //Check sections virtual sizes for(section_list::const_iterator i = sections_.begin() + 1; i != sections_.end(); ++i) { if((*i).get_virtual_address() != (*(i - 1)).get_virtual_address() + (*(i - 1)).get_aligned_virtual_size(get_section_alignment())) throw pe_exception("Section table is incorrect", pe_exception::image_section_table_incorrect); } } //Check if image has overlay in the end of file has_overlay_ = !sections_.empty() && filesize > static_cast(sections_.back().get_pointer_to_raw_data() + last_raw_size); { //Additionally, read data from the beginning of istream to size of headers file.seekg(0); uint32_t size_of_headers = std::min(get_size_of_headers(), static_cast(filesize)); if(!sections_.empty()) { for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) { if(!(*i).empty()) { size_of_headers = std::min(get_size_of_headers(), (*i).get_pointer_to_raw_data()); break; } } } full_headers_data_.resize(size_of_headers); file.read(&full_headers_data_[0], size_of_headers); if(file.bad() || file.eof()) throw pe_exception("Error reading file", pe_exception::error_reading_file); } //Moreover, if there's debug directory, read its raw data for some debug info types while(read_debug_raw_data && has_debug()) { try { //Check the length in bytes of the section containing debug directory if(section_data_length_from_rva(get_directory_rva(image_directory_entry_debug), get_directory_rva(image_directory_entry_debug), section_data_virtual, true) < sizeof(image_debug_directory)) break; unsigned long current_pos = get_directory_rva(image_directory_entry_debug); //First IMAGE_DEBUG_DIRECTORY table image_debug_directory directory = section_data_from_rva(current_pos, section_data_virtual, true); //Iterate over all IMAGE_DEBUG_DIRECTORY directories while(directory.PointerToRawData && current_pos < get_directory_rva(image_directory_entry_debug) + get_directory_size(image_directory_entry_debug)) { //If we have something to read if((directory.Type == image_debug_type_codeview || directory.Type == image_debug_type_misc || directory.Type == image_debug_type_coff) && directory.SizeOfData) { std::string data; data.resize(directory.SizeOfData); file.seekg(directory.PointerToRawData); file.read(&data[0], directory.SizeOfData); if(file.bad() || file.eof()) throw pe_exception("Error reading file", pe_exception::error_reading_file); debug_data_.insert(std::make_pair(directory.PointerToRawData, data)); } //Go to next debug entry current_pos += sizeof(image_debug_directory); directory = section_data_from_rva(current_pos, section_data_virtual, true); } break; } catch(const pe_exception&) { //Don't throw any exception here, if debug info is corrupted or incorrect break; } catch(const std::bad_alloc&) { //Don't throw any exception here, if debug info is corrupted or incorrect break; } } } //Returns PE type of this image pe_type pe_base::get_pe_type() const { return props_->get_pe_type(); } //Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) pe_type pe_base::get_pe_type(std::istream& file) { //Save state of the istream std::ios_base::iostate state = file.exceptions(); std::streamoff old_offset = file.tellg(); image_nt_headers32 nt_headers; image_dos_header header; try { //Read dos header file.exceptions(std::ios::goodbit); read_dos_header(file, header); //Seek to the NT headers start file.seekg(header.e_lfanew); if(file.bad() || file.fail()) throw pe_exception("Cannot reach IMAGE_NT_HEADERS", pe_exception::image_nt_headers_not_found); //Read NT headers (we're using 32-bit version, because there's no significant differencies between 32 and 64 bit version structures) file.read(reinterpret_cast(&nt_headers), sizeof(image_nt_headers32) - sizeof(image_data_directory) * image_numberof_directory_entries); if(file.bad() || file.eof()) throw pe_exception("Error reading IMAGE_NT_HEADERS", pe_exception::error_reading_image_nt_headers); //Check NT headers signature if(nt_headers.Signature != 0x4550) //"PE" throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); //Check NT headers magic if(nt_headers.OptionalHeader.Magic != image_nt_optional_hdr32_magic && nt_headers.OptionalHeader.Magic != image_nt_optional_hdr64_magic) throw pe_exception("Incorrect PE signature", pe_exception::pe_signature_incorrect); } catch(const std::exception&) { //If something went wrong, restore istream state file.exceptions(state); file.seekg(old_offset); file.clear(); //Retrhow exception throw; } //Restore stream state file.exceptions(state); file.seekg(old_offset); file.clear(); //Determine PE type and return it return nt_headers.OptionalHeader.Magic == image_nt_optional_hdr64_magic ? pe_type_64 : pe_type_32; } //Returns true if image has overlay data at the end of file bool pe_base::has_overlay() const { return has_overlay_; } //Clears PE characteristics flag void pe_base::clear_characteristics_flags(uint16_t flags) { set_characteristics(get_characteristics() & ~flags); } //Sets PE characteristics flag void pe_base::set_characteristics_flags(uint16_t flags) { set_characteristics(get_characteristics() | flags); } //Returns true if PE characteristics flag set bool pe_base::check_characteristics_flag(uint16_t flag) const { return (get_characteristics() & flag) ? true : false; } //Returns subsystem value uint16_t pe_base::get_subsystem() const { return props_->get_subsystem(); } //Sets subsystem value void pe_base::set_subsystem(uint16_t subsystem) { props_->set_subsystem(subsystem); } //Returns true if image has console subsystem bool pe_base::is_console() const { return get_subsystem() == image_subsystem_windows_cui; } //Returns true if image has Windows GUI subsystem bool pe_base::is_gui() const { return get_subsystem() == image_subsystem_windows_gui; } //Sets required operation system version void pe_base::set_os_version(uint16_t major, uint16_t minor) { props_->set_os_version(major, minor); } //Returns required operation system version (minor word) uint16_t pe_base::get_minor_os_version() const { return props_->get_minor_os_version(); } //Returns required operation system version (major word) uint16_t pe_base::get_major_os_version() const { return props_->get_major_os_version(); } //Sets required subsystem version void pe_base::set_subsystem_version(uint16_t major, uint16_t minor) { props_->set_subsystem_version(major, minor); } //Returns required subsystem version (minor word) uint16_t pe_base::get_minor_subsystem_version() const { return props_->get_minor_subsystem_version(); } //Returns required subsystem version (major word) uint16_t pe_base::get_major_subsystem_version() const { return props_->get_major_subsystem_version(); } //Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds) char* pe_base::section_data_from_va(section& s, uint32_t va) //Always returns raw data { return section_data_from_rva(s, va_to_rva(va)); } //Returns corresponding section data pointer from VA inside section "s" for PE32 (checks bounds) const char* pe_base::section_data_from_va(const section& s, uint32_t va, section_data_type datatype) const { return section_data_from_rva(s, va_to_rva(va), datatype); } //Returns corresponding section data pointer from VA inside section for PE32 char* pe_base::section_data_from_va(uint32_t va, bool include_headers) //Always returns raw data { return section_data_from_rva(va_to_rva(va), include_headers); } //Returns corresponding section data pointer from VA inside section for PE32 const char* pe_base::section_data_from_va(uint32_t va, section_data_type datatype, bool include_headers) const { return section_data_from_rva(va_to_rva(va), datatype, include_headers); } //Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds) char* pe_base::section_data_from_va(section& s, uint64_t va) //Always returns raw data { return section_data_from_rva(s, va_to_rva(va)); } //Returns corresponding section data pointer from VA inside section "s" for PE32/PE64 (checks bounds) const char* pe_base::section_data_from_va(const section& s, uint64_t va, section_data_type datatype) const { return section_data_from_rva(s, va_to_rva(va), datatype); } //Returns corresponding section data pointer from VA inside section for PE32/PE64 char* pe_base::section_data_from_va(uint64_t va, bool include_headers) //Always returns raw data { return section_data_from_rva(va_to_rva(va), include_headers); } //Returns corresponding section data pointer from VA inside section for PE32/PE64 const char* pe_base::section_data_from_va(uint64_t va, section_data_type datatype, bool include_headers) const { return section_data_from_rva(va_to_rva(va), datatype, include_headers); } //Returns section from VA inside it for PE32 section& pe_base::section_from_va(uint32_t va) { return section_from_rva(va_to_rva(va)); } //Returns section from VA inside it for PE32/PE64 section& pe_base::section_from_va(uint64_t va) { return section_from_rva(va_to_rva(va)); } //Returns section from RVA inside it for PE32 const section& pe_base::section_from_va(uint32_t va) const { return section_from_rva(va_to_rva(va)); } //Returns section from RVA inside it for PE32/PE64 const section& pe_base::section_from_va(uint64_t va) const { return section_from_rva(va_to_rva(va)); } uint32_t pe_base::va_to_rva(uint32_t va, bool bound_check) const { return props_->va_to_rva(va, bound_check); } uint32_t pe_base::va_to_rva(uint64_t va, bool bound_check) const { return props_->va_to_rva(va, bound_check); } uint32_t pe_base::rva_to_va_32(uint32_t rva) const { return props_->rva_to_va_32(rva); } uint64_t pe_base::rva_to_va_64(uint32_t rva) const { return props_->rva_to_va_64(rva); } //Relative Virtual Address (RVA) to Virtual Address (VA) convertion for PE32 void pe_base::rva_to_va(uint32_t rva, uint32_t& va) const { va = rva_to_va_32(rva); } //Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64 void pe_base::rva_to_va(uint32_t rva, uint64_t& va) const { va = rva_to_va_64(rva); } //Returns section from file offset (4gb max) section& pe_base::section_from_file_offset(uint32_t offset) { return *file_offset_to_section(offset); } //Returns section from file offset (4gb max) const section& pe_base::section_from_file_offset(uint32_t offset) const { return *file_offset_to_section(offset); } //Returns section and offset (raw data only) from its start from RVA const std::pair pe_base::section_and_offset_from_rva(uint32_t rva) const { const section& s = section_from_rva(rva); return std::make_pair(rva - s.get_virtual_address(), &s); } //Returns DLL Characteristics uint16_t pe_base::get_dll_characteristics() const { return props_->get_dll_characteristics(); } //Sets DLL Characteristics void pe_base::set_dll_characteristics(uint16_t characteristics) { props_->set_dll_characteristics(characteristics); } //Returns size of headers uint32_t pe_base::get_size_of_headers() const { return props_->get_size_of_headers(); } //Returns size of optional header uint16_t pe_base::get_size_of_optional_header() const { return props_->get_size_of_optional_header(); } //Returns PE signature uint32_t pe_base::get_pe_signature() const { return props_->get_pe_signature(); } //Returns magic value uint32_t pe_base::get_magic() const { return props_->get_magic(); } //Returns image base for PE32 void pe_base::get_image_base(uint32_t& base) const { base = get_image_base_32(); } //Returns image base for PE32 and PE64 respectively uint32_t pe_base::get_image_base_32() const { return props_->get_image_base_32(); } //Sets image base for PE32 and PE64 respectively uint64_t pe_base::get_image_base_64() const { return props_->get_image_base_64(); } //RVA to RAW file offset convertion (4gb max) uint32_t pe_base::rva_to_file_offset(uint32_t rva) const { //Maybe, RVA is inside PE headers if(rva < get_size_of_headers()) return rva; const section& s = section_from_rva(rva); return s.get_pointer_to_raw_data() + rva - s.get_virtual_address(); } //RAW file offset to RVA convertion (4gb max) uint32_t pe_base::file_offset_to_rva(uint32_t offset) const { //Maybe, offset is inside PE headers if(offset < get_size_of_headers()) return offset; const section_list::const_iterator it = file_offset_to_section(offset); return offset - (*it).get_pointer_to_raw_data() + (*it).get_virtual_address(); } //RAW file offset to section convertion helper (4gb max) section_list::const_iterator pe_base::file_offset_to_section(uint32_t offset) const { section_list::const_iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); if(it == sections_.end()) throw pe_exception("No section found by presented file offset", pe_exception::no_section_found); return it; } //RAW file offset to section convertion helper (4gb max) section_list::iterator pe_base::file_offset_to_section(uint32_t offset) { section_list::iterator it = std::find_if(sections_.begin(), sections_.end(), section_by_raw_offset(offset)); if(it == sections_.end()) throw pe_exception("No section found by presented file offset", pe_exception::no_section_found); return it; } //RVA from section raw data offset uint32_t pe_base::rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start) { return s.get_virtual_address() + raw_offset_from_section_start; } //Returns image base for PE32/PE64 void pe_base::get_image_base(uint64_t& base) const { base = get_image_base_64(); } //Sets new image base void pe_base::set_image_base(uint32_t base) { props_->set_image_base(base); } void pe_base::set_image_base_64(uint64_t base) { props_->set_image_base_64(base); } //Sets heap size commit for PE32 and PE64 respectively void pe_base::set_heap_size_commit(uint32_t size) { props_->set_heap_size_commit(size); } void pe_base::set_heap_size_commit(uint64_t size) { props_->set_heap_size_commit(size); } //Sets heap size reserve for PE32 and PE64 respectively void pe_base::set_heap_size_reserve(uint32_t size) { props_->set_heap_size_reserve(size); } void pe_base::set_heap_size_reserve(uint64_t size) { props_->set_heap_size_reserve(size); } //Sets stack size commit for PE32 and PE64 respectively void pe_base::set_stack_size_commit(uint32_t size) { props_->set_stack_size_commit(size); } void pe_base::set_stack_size_commit(uint64_t size) { props_->set_stack_size_commit(size); } //Sets stack size reserve for PE32 and PE64 respectively void pe_base::set_stack_size_reserve(uint32_t size) { props_->set_stack_size_reserve(size); } void pe_base::set_stack_size_reserve(uint64_t size) { props_->set_stack_size_reserve(size); } //Returns heap size commit for PE32 and PE64 respectively uint32_t pe_base::get_heap_size_commit_32() const { return props_->get_heap_size_commit_32(); } uint64_t pe_base::get_heap_size_commit_64() const { return props_->get_heap_size_commit_64(); } //Returns heap size reserve for PE32 and PE64 respectively uint32_t pe_base::get_heap_size_reserve_32() const { return props_->get_heap_size_reserve_32(); } uint64_t pe_base::get_heap_size_reserve_64() const { return props_->get_heap_size_reserve_64(); } //Returns stack size commit for PE32 and PE64 respectively uint32_t pe_base::get_stack_size_commit_32() const { return props_->get_stack_size_commit_32(); } uint64_t pe_base::get_stack_size_commit_64() const { return props_->get_stack_size_commit_64(); } //Returns stack size reserve for PE32 and PE64 respectively uint32_t pe_base::get_stack_size_reserve_32() const { return props_->get_stack_size_reserve_32(); } uint64_t pe_base::get_stack_size_reserve_64() const { return props_->get_stack_size_reserve_64(); } //Returns heap size commit for PE32 void pe_base::get_heap_size_commit(uint32_t& size) const { size = get_heap_size_commit_32(); } //Returns heap size commit for PE32/PE64 void pe_base::get_heap_size_commit(uint64_t& size) const { size = get_heap_size_commit_64(); } //Returns heap size reserve for PE32 void pe_base::get_heap_size_reserve(uint32_t& size) const { size = get_heap_size_reserve_32(); } //Returns heap size reserve for PE32/PE64 void pe_base::get_heap_size_reserve(uint64_t& size) const { size = get_heap_size_reserve_64(); } //Returns stack size commit for PE32 void pe_base::get_stack_size_commit(uint32_t& size) const { size = get_stack_size_commit_32(); } //Returns stack size commit for PE32/PE64 void pe_base::get_stack_size_commit(uint64_t& size) const { size = get_stack_size_commit_64(); } //Returns stack size reserve for PE32 void pe_base::get_stack_size_reserve(uint32_t& size) const { size = get_stack_size_reserve_32(); } //Returns stack size reserve for PE32/PE64 void pe_base::get_stack_size_reserve(uint64_t& size) const { size = get_stack_size_reserve_64(); } //Realigns file (changes file alignment) void pe_base::realign_file(uint32_t new_file_alignment) { //Checks alignment for correctness set_file_alignment(new_file_alignment); realign_all_sections(); } //Helper function to recalculate RAW and virtual section sizes and strip it, if necessary void pe_base::recalculate_section_sizes(section& s, bool auto_strip) { prepare_section(s); //Recalculate section raw addresses //Strip RAW size of section, if it is the last one //For all others it must be file-aligned and calculated by prepare_section() call if(auto_strip && !(sections_.empty() || &s == &*(sections_.end() - 1))) { //Strip ending raw data nullbytes to optimize size std::string& raw_data = s.get_raw_data(); if(!raw_data.empty()) { std::string::size_type i = raw_data.length(); for(; i != 1; --i) { if(raw_data[i - 1] != 0) break; } raw_data.resize(i); } s.set_size_of_raw_data(static_cast(raw_data.length())); } //Can occur only for last section if(pe_utils::align_up(s.get_virtual_size(), get_section_alignment()) < pe_utils::align_up(s.get_size_of_raw_data(), get_file_alignment())) set_section_virtual_size(s, pe_utils::align_up(s.get_size_of_raw_data(), get_section_alignment())); //Recalculate section virtual size } //Returns data from the beginning of image //Size = SizeOfHeaders const std::string& pe_base::get_full_headers_data() const { return full_headers_data_; } const pe_base::debug_data_list& pe_base::get_raw_debug_data_list() const { return debug_data_; } //Sets number of sections void pe_base::set_number_of_sections(uint16_t number) { props_->set_number_of_sections(number); } //Sets size of image void pe_base::set_size_of_image(uint32_t size) { props_->set_size_of_image(size); } //Sets size of headers void pe_base::set_size_of_headers(uint32_t size) { props_->set_size_of_headers(size); } //Sets size of optional headers void pe_base::set_size_of_optional_header(uint16_t size) { props_->set_size_of_optional_header(size); } //Returns nt headers data pointer char* pe_base::get_nt_headers_ptr() { return props_->get_nt_headers_ptr(); } //Returns nt headers data pointer const char* pe_base::get_nt_headers_ptr() const { return props_->get_nt_headers_ptr(); } //Returns sizeof() nt headers uint32_t pe_base::get_sizeof_nt_header() const { return props_->get_sizeof_nt_header(); } //Returns sizeof() optional headers uint32_t pe_base::get_sizeof_opt_headers() const { return props_->get_sizeof_opt_headers(); } //Sets file alignment (no checks) void pe_base::set_file_alignment_unchecked(uint32_t alignment) { props_->set_file_alignment_unchecked(alignment); } //Sets base of code void pe_base::set_base_of_code(uint32_t base) { props_->set_base_of_code(base); } //Returns base of code uint32_t pe_base::get_base_of_code() const { return props_->get_base_of_code(); } //Returns needed magic of image uint32_t pe_base::get_needed_magic() const { return props_->get_needed_magic(); } }