/*************************************************************************/ /* 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. */ /*************************************************************************/ #pragma once #include #include #include #include #include #include "pe_exception.h" #include "pe_structures.h" #include "utils.h" #include "pe_section.h" #include "pe_properties.h" //Please don't remove this information from header //PEBliss 1.0.0 //(c) DX 2011 - 2012, http://kaimi.ru //Free to use for commertial and non-commertial purposes, modification and distribution // == more important == //TODO: compact import rebuilder //TODO: remove sections in the middle //== less important == //TODO: relocations that take more than one element (seems to be not possible in Windows PE, but anyway) //TODO: delay import directory //TODO: write message tables //TODO: write string tables //TODO: read security information //TODO: read full .NET information namespace pe_bliss { //Portable executable class class pe_base { public: //CONSTRUCTORS //Constructor from stream pe_base(std::istream& file, const pe_properties& props, bool read_debug_raw_data = true); //Constructor of empty PE-file explicit pe_base(const pe_properties& props, uint32_t section_alignment = 0x1000, bool dll = false, uint16_t subsystem = pe_win::image_subsystem_windows_gui); pe_base(const pe_base& pe); pe_base& operator=(const pe_base& pe); public: ~pe_base(); public: //STUB //Strips stub MSVS overlay, if any void strip_stub_overlay(); //Fills stub MSVS overlay with specified byte void fill_stub_overlay(char c); //Sets stub MSVS overlay void set_stub_overlay(const std::string& data); //Returns stub overlay contents const std::string& get_stub_overlay() const; public: //DIRECTORIES //Returns true if directory exists bool directory_exists(uint32_t id) const; //Removes directory void remove_directory(uint32_t id); //Returns directory RVA uint32_t get_directory_rva(uint32_t id) const; //Returns directory size uint32_t get_directory_size(uint32_t id) const; //Sets directory RVA (just a value of PE header, no moving occurs) void set_directory_rva(uint32_t id, uint32_t rva); //Sets directory size (just a value of PE header, no moving occurs) void set_directory_size(uint32_t id, uint32_t 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 strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); //Returns true if image has import directory bool has_imports() const; //Returns true if image has export directory bool has_exports() const; //Returns true if image has resource directory bool has_resources() const; //Returns true if image has security directory bool has_security() const; //Returns true if image has relocations bool has_reloc() const; //Returns true if image has TLS directory bool has_tls() const; //Returns true if image has config directory bool has_config() const; //Returns true if image has bound import directory bool has_bound_import() const; //Returns true if image has delay import directory bool has_delay_import() const; //Returns true if image has COM directory bool is_dotnet() const; //Returns true if image has exception directory bool has_exception_directory() const; //Returns true if image has debug directory bool has_debug() const; //Returns subsystem value uint16_t get_subsystem() const; //Sets subsystem value void set_subsystem(uint16_t subsystem); //Returns true if image has console subsystem bool is_console() const; //Returns true if image has Windows GUI subsystem bool is_gui() const; //Sets required operation system version void set_os_version(uint16_t major, uint16_t minor); //Returns required operation system version (minor word) uint16_t get_minor_os_version() const; //Returns required operation system version (major word) uint16_t get_major_os_version() const; //Sets required subsystem version void set_subsystem_version(uint16_t major, uint16_t minor); //Returns required subsystem version (minor word) uint16_t get_minor_subsystem_version() const; //Returns required subsystem version (major word) uint16_t get_major_subsystem_version() const; public: //PE HEADER //Returns DOS header const pe_win::image_dos_header& get_dos_header() const; pe_win::image_dos_header& get_dos_header(); //Returns PE header start (e_lfanew) int32_t get_pe_header_start() const; //Returns file alignment uint32_t get_file_alignment() const; //Sets file alignment, checking the correctness of its value void set_file_alignment(uint32_t alignment); //Returns size of image uint32_t get_size_of_image() const; //Returns image entry point uint32_t get_ep() const; //Sets image entry point (just a value of PE header) void set_ep(uint32_t new_ep); //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) uint32_t get_number_of_rvas_and_sizes() const; //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) void set_number_of_rvas_and_sizes(uint32_t number); //Returns PE characteristics uint16_t get_characteristics() const; //Sets PE characteristics (a value inside header) void set_characteristics(uint16_t ch); //Clears PE characteristics flag void clear_characteristics_flags(uint16_t flags); //Sets PE characteristics flag void set_characteristics_flags(uint16_t flags); //Returns true if PE characteristics flag set bool check_characteristics_flag(uint16_t flag) const; //Returns DLL Characteristics uint16_t get_dll_characteristics() const; //Sets DLL Characteristics void set_dll_characteristics(uint16_t characteristics); //Returns size of headers uint32_t get_size_of_headers() const; //Returns size of optional header uint16_t get_size_of_optional_header() const; //Returns PE signature uint32_t get_pe_signature() const; //Returns magic value uint32_t get_magic() const; //Returns image base for PE32 and PE64 respectively uint32_t get_image_base_32() const; void get_image_base(uint32_t& base) const; //Sets image base for PE32 and PE64 respectively uint64_t get_image_base_64() const; void get_image_base(uint64_t& base) const; //Sets new image base void set_image_base(uint32_t base); void set_image_base_64(uint64_t base); //Sets heap size commit for PE32 and PE64 respectively void set_heap_size_commit(uint32_t size); void set_heap_size_commit(uint64_t size); //Sets heap size reserve for PE32 and PE64 respectively void set_heap_size_reserve(uint32_t size); void set_heap_size_reserve(uint64_t size); //Sets stack size commit for PE32 and PE64 respectively void set_stack_size_commit(uint32_t size); void set_stack_size_commit(uint64_t size); //Sets stack size reserve for PE32 and PE64 respectively void set_stack_size_reserve(uint32_t size); void set_stack_size_reserve(uint64_t size); //Returns heap size commit for PE32 and PE64 respectively uint32_t get_heap_size_commit_32() const; void get_heap_size_commit(uint32_t& size) const; uint64_t get_heap_size_commit_64() const; void get_heap_size_commit(uint64_t& size) const; //Returns heap size reserve for PE32 and PE64 respectively uint32_t get_heap_size_reserve_32() const; void get_heap_size_reserve(uint32_t& size) const; uint64_t get_heap_size_reserve_64() const; void get_heap_size_reserve(uint64_t& size) const; //Returns stack size commit for PE32 and PE64 respectively uint32_t get_stack_size_commit_32() const; void get_stack_size_commit(uint32_t& size) const; uint64_t get_stack_size_commit_64() const; void get_stack_size_commit(uint64_t& size) const; //Returns stack size reserve for PE32 and PE64 respectively uint32_t get_stack_size_reserve_32() const; void get_stack_size_reserve(uint32_t& size) const; uint64_t get_stack_size_reserve_64() const; void get_stack_size_reserve(uint64_t& size) const; //Updates virtual size of image corresponding to section virtual sizes void update_image_size(); //Returns checksum of PE file from header uint32_t get_checksum() const; //Sets checksum of PE file void set_checksum(uint32_t checksum); //Returns timestamp of PE file from header uint32_t get_time_date_stamp() const; //Sets timestamp of PE file void set_time_date_stamp(uint32_t timestamp); //Returns Machine field value of PE file from header uint16_t get_machine() const; //Sets Machine field value of PE file void set_machine(uint16_t machine); //Returns data from the beginning of image //Size = SizeOfHeaders const std::string& get_full_headers_data() const; typedef std::multimap debug_data_list; //Returns raw list of debug data const debug_data_list& get_raw_debug_data_list() const; //Reads and checks DOS header static void read_dos_header(std::istream& file, pe_win::image_dos_header& header); //Returns sizeof() nt headers uint32_t get_sizeof_nt_header() const; //Returns sizeof() optional headers uint32_t get_sizeof_opt_headers() const; //Returns raw nt headers data pointer const char* get_nt_headers_ptr() const; //Sets size of headers (to NT headers) void set_size_of_headers(uint32_t size); //Sets size of optional headers (to NT headers) void set_size_of_optional_header(uint16_t size); //Sets base of code void set_base_of_code(uint32_t base); //Returns base of code uint32_t get_base_of_code() const; public: //ADDRESS CONVERTIONS //Virtual Address (VA) to Relative Virtual Address (RVA) convertions //for PE32 and PE64 respectively //bound_check checks integer overflow uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; uint32_t va_to_rva(uint64_t va, bool bound_check = true) const; //Relative Virtual Address (RVA) to Virtual Address (VA) convertions //for PE32 and PE64 respectively uint32_t rva_to_va_32(uint32_t rva) const; void rva_to_va(uint32_t rva, uint32_t& va) const; uint64_t rva_to_va_64(uint32_t rva) const; void rva_to_va(uint32_t rva, uint64_t& va) const; //RVA to RAW file offset convertion (4gb max) uint32_t rva_to_file_offset(uint32_t rva) const; //RAW file offset to RVA convertion (4gb max) uint32_t file_offset_to_rva(uint32_t offset) const; //RVA from section raw data offset static uint32_t rva_from_section_offset(const section& s, uint32_t raw_offset_from_section_start); public: //IMAGE SECTIONS //Returns number of sections from PE header uint16_t get_number_of_sections() const; //Updates number of sections in PE header uint16_t update_number_of_sections(); //Returns section alignment uint32_t get_section_alignment() const; //Returns section list section_list& get_image_sections(); const section_list& get_image_sections() const; //Realigns all sections, if you made any changes to sections or alignments void realign_all_sections(); //Resligns section with specified index void realign_section(uint32_t index); //Returns section from RVA inside it section& section_from_rva(uint32_t rva); const section& section_from_rva(uint32_t rva) const; //Returns section from directory ID section& section_from_directory(uint32_t directory_id); const section& section_from_directory(uint32_t directory_id) const; //Returns section from VA inside it for PE32 and PE64 respectively section& section_from_va(uint32_t va); const section& section_from_va(uint32_t va) const; section& section_from_va(uint64_t va); const section& section_from_va(uint64_t va) const; //Returns section from file offset (4gb max) section& section_from_file_offset(uint32_t offset); const section& section_from_file_offset(uint32_t offset) const; //Returns section TOTAL RAW/VIRTUAL data length from RVA inside section //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too uint32_t section_data_length_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const; //Returns section TOTAL RAW/VIRTUAL data length from VA inside section for PE32 and PE64 respectively //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too uint32_t section_data_length_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; uint32_t section_data_length_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; //Returns section remaining RAW/VIRTUAL data length from RVA to the end of section "s" (checks bounds) uint32_t section_data_length_from_rva(const section& s, uint32_t rva_inside, section_data_type datatype = section_data_raw) const; //Returns section remaining RAW/VIRTUAL data length from VA to the end of section "s" for PE32 and PE64 respectively (checks bounds) uint32_t section_data_length_from_va(const section& s, uint64_t va_inside, section_data_type datatype = section_data_raw) const; uint32_t section_data_length_from_va(const section& s, uint32_t va_inside, section_data_type datatype = section_data_raw) const; //Returns section remaining RAW/VIRTUAL data length from RVA "rva_inside" to the end of section containing RVA "rva" //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too uint32_t section_data_length_from_rva(uint32_t rva, uint32_t rva_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; //Returns section remaining RAW/VIRTUAL data length from VA "va_inside" to the end of section containing VA "va" for PE32 and PE64 respectively //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too uint32_t section_data_length_from_va(uint32_t va, uint32_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; uint32_t section_data_length_from_va(uint64_t va, uint64_t va_inside, section_data_type datatype = section_data_raw, bool include_headers = false) const; //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too //Returns corresponding section data pointer from RVA inside section char* section_data_from_rva(uint32_t rva, bool include_headers = false); const char* section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const; //Returns corresponding section data pointer from VA inside section for PE32 and PE64 respectively char* section_data_from_va(uint32_t va, bool include_headers = false); const char* section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; char* section_data_from_va(uint64_t va, bool include_headers = false); const char* section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const; //Returns corresponding section data pointer from RVA inside section "s" (checks bounds) char* section_data_from_rva(section& s, uint32_t rva); const char* section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const; //Returns corresponding section data pointer from VA inside section "s" for PE32 and PE64 respectively (checks bounds) char* section_data_from_va(section& s, uint32_t va); //Always returns raw data const char* section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const; char* section_data_from_va(section& s, uint64_t va); //Always returns raw data const char* section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const; //Returns corresponding section data pointer from RVA inside section "s" (checks bounds, checks sizes, the most safe function) template T section_data_from_rva(const section& s, uint32_t rva, section_data_type datatype = section_data_raw) const { if(rva >= s.get_virtual_address() && rva < s.get_virtual_address() + s.get_aligned_virtual_size(get_section_alignment()) && pe_utils::is_sum_safe(rva, sizeof(T))) { const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); //Don't check for underflow here, comparsion is unsigned if(data.size() < rva - s.get_virtual_address() + sizeof(T)) throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); return *reinterpret_cast(data.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 (checks rva, checks sizes, the most safe function) //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too template T section_data_from_rva(uint32_t rva, section_data_type datatype = section_data_raw, bool include_headers = false) const { //if RVA is inside of headers and we're searching them too... if(include_headers && pe_utils::is_sum_safe(rva, sizeof(T)) && (rva + sizeof(T) < full_headers_data_.length())) return *reinterpret_cast(&full_headers_data_[rva]); const section& s = section_from_rva(rva); const std::string& data = datatype == section_data_raw ? s.get_raw_data() : s.get_virtual_data(get_section_alignment()); //Don't check for underflow here, comparsion is unsigned if(data.size() < rva - s.get_virtual_address() + sizeof(T)) throw pe_exception("RVA and requested data size does not exist inside section", pe_exception::rva_not_exists); return *reinterpret_cast(data.data() + rva - s.get_virtual_address()); } //Returns corresponding section data pointer from VA inside section "s" (checks bounds, checks sizes, the most safe function) template T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const { return section_data_from_rva(s, va_to_rva(va), datatype); } template T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const { return section_data_from_rva(s, va_to_rva(va), datatype); } //Returns corresponding section data pointer from VA inside section (checks rva, checks sizes, the most safe function) //If include_headers = true, data from the beginning of PE file to SizeOfHeaders will be searched, too template T section_data_from_va(uint32_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const { return section_data_from_rva(va_to_rva(va), datatype, include_headers); } template T section_data_from_va(uint64_t va, section_data_type datatype = section_data_raw, bool include_headers = false) const { return section_data_from_rva(va_to_rva(va), datatype, include_headers); } //Returns section and offset (raw data only) from its start from RVA const std::pair section_and_offset_from_rva(uint32_t rva) const; //Sets virtual size of section "s" //Section must be free (not bound to any image) //or the last section of this image //Function calls update_image_size automatically in second case void set_section_virtual_size(section& s, uint32_t vsize); //Represents section expand type for expand_section function enum section_expand_type { expand_section_raw, //Section raw data size will be expanded expand_section_virtual //Section virtual data size will be expanded }; //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 //Returns true if section was expanded bool expand_section(section& s, uint32_t needed_rva, uint32_t needed_size, section_expand_type expand); //Adds section to image //Returns last section section& add_section(section s); //Prepares section to later add it to image (checks and recalculates virtual and raw section size) //Section must be prepared by this function before calling add_section void prepare_section(section& s); //Returns true if sectios "s" is already attached to this PE file bool section_attached(const section& s) const; public: //IMAGE //Returns PE type (PE or PE+) from pe_type enumeration (minimal correctness checks) static pe_type get_pe_type(std::istream& file); //Returns PE type of this image pe_type get_pe_type() const; //Returns true if image has overlay data at the end of file bool has_overlay() const; //Realigns file (changes file alignment) void realign_file(uint32_t new_file_alignment); //Helper function to recalculate RAW and virtual section sizes and strip it, if necessary //auto_strip = strip section, if necessary void recalculate_section_sizes(section& s, bool auto_strip); // ========== END OF PUBLIC MEMBERS AND STRUCTURES ========== // private: //Image DOS header pe_win::image_dos_header dos_header_; //Rich (stub) overlay data (for MSVS) std::string rich_overlay_; //List of image sections section_list sections_; //True if image has overlay bool has_overlay_; //Raw SizeOfHeaders-sized data from the beginning of image std::string full_headers_data_; //Raw debug data for all directories //PointerToRawData; Data debug_data_list debug_data_; //PE or PE+ related properties pe_properties* props_; //Reads and checks DOS header void read_dos_header(std::istream& file); //Reads and checks PE headers and section headers, data void read_pe(std::istream& file, bool read_debug_raw_data); //Sets number of sections void set_number_of_sections(uint16_t number); //Sets size of image void set_size_of_image(uint32_t size); //Sets file alignment (no checks) void set_file_alignment_unchecked(uint32_t alignment); //Returns needed magic of image uint32_t get_needed_magic() const; //Returns nt headers data pointer char* get_nt_headers_ptr(); private: static const uint16_t maximum_number_of_sections = 0x60; static const uint32_t minimum_file_alignment = 512; private: //RAW file offset to section convertion helpers (4gb max) section_list::const_iterator file_offset_to_section(uint32_t offset) const; section_list::iterator file_offset_to_section(uint32_t offset); }; }