diff options
Diffstat (limited to 'tools/pe_bliss/pe_base.h')
-rw-r--r-- | tools/pe_bliss/pe_base.h | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/tools/pe_bliss/pe_base.h b/tools/pe_bliss/pe_base.h new file mode 100644 index 0000000000..b5416cf1e2 --- /dev/null +++ b/tools/pe_bliss/pe_base.h @@ -0,0 +1,544 @@ +/*************************************************************************/ +/* 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 <string> +#include <vector> +#include <istream> +#include <ostream> +#include <map> +#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<uint32_t, std::string> 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<typename T> + 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<const T*>(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<typename T> + 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<const T*>(&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<const T*>(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<typename T> + T section_data_from_va(const section& s, uint32_t va, section_data_type datatype = section_data_raw) const + { + return section_data_from_rva<T>(s, va_to_rva(va), datatype); + } + + template<typename T> + T section_data_from_va(const section& s, uint64_t va, section_data_type datatype = section_data_raw) const + { + return section_data_from_rva<T>(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<typename T> + 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<T>(va_to_rva(va), datatype, include_headers); + } + + template<typename T> + 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<T>(va_to_rva(va), datatype, include_headers); + } + + //Returns section and offset (raw data only) from its start from RVA + const std::pair<uint32_t, const section*> 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); +}; +} |