diff options
Diffstat (limited to 'tools/pe_bliss')
84 files changed, 18126 insertions, 0 deletions
diff --git a/tools/pe_bliss/README b/tools/pe_bliss/README new file mode 100644 index 0000000000..d5d1355444 --- /dev/null +++ b/tools/pe_bliss/README @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ + + +Открытая бесплатная библиотека для работы с PE-файлами PE Bliss. +Бесплатна к использованию, модификации и распространению. +Автор: DX +(c) DX 2011-2012, kaimi.ru + +Совместимость: Windows, Linux + +Возможности: +[+] Создание PE или PE+ файла с нуля +[+] Чтение 32-разрядных и 64-разрядных PE-файлов (PE, PE+) и единообразная работа с ними +[+] Пересборка 32-разрядных и 64-разрядных PE-файлов +[+] Работа с директориями и заголовками +[+] Конвертирование адресов +[+] Чтение и редактирование секций PE-файла +[+] Чтение и редактирование таблицы импортов +[+] Чтение и редактирование таблицы экспортов +[+] Чтение и редактирование таблиц релокаций +[+] Чтение и редактирование ресурсов +[+] Чтение и редактирование TLS +[+] Чтение и редактирование конфигурации образа (image config) +[+] Чтение базовой информации .NET +[+] Чтение и редактирование информации о привязанном импорте +[+] Чтение директории исключений (только PE+) +[+] Чтение отладочной директории с расширенной информацией +[+] Вычисление энтропии +[+] Изменение файлового выравнивания +[+] Изменение базового адреса загрузки +[+] Работа с DOS Stub'ом и Rich overlay +[+] Высокоуровневое чтение ресурсов: картинки, иконки, курсоры, информация о версии, строковые таблицы, таблицы сообщений +[+] Высокоуровневое редактирование ресурсов: картинки, иконки, курсоры, информация о версии + +[English] +Open a free library for working with PE-file PE Bliss. +Free to use, modify, and distribute. +Author: DX +(c) DX 2011-2012, kaimi.ru +Compatibility: Windows, Linux + +### Capabilities: +[+] Creation of PE or PE + file from scratch +[+] Reading the 32-bit and 64-bit PE-file (PE, PE +) and uniform working with them +[+] Rebuild 32-bit and 64-bit PE-files +[+] Working with the directors and titles +[+] Converting addresses +[+] Reading and editing sections of PE-file +[+] Reading and editing the import table +[+] Reading and editing tables exports +[+] Reading and editing tables relocations +[+] Reading and editing resources +[+] Reading and editing TLS +[+] Reading and editing the configuration of the image (image config) +[+] Reading data base .NET +[+] Reading and editing information about tethered import +[+] Read the directory exceptions (only PE +) +[+] Read debug directories with extended information +[+] The calculation of entropy +[+] Changing file alignment +[+] Change the base load address +[+] Support of DOS Stub'om and Rich overlay +[+] High-level reading resources: images, icons, cursors, version information, string tables, message table +[+] High-level editing resources: images, icons, cursors, version information
\ No newline at end of file diff --git a/tools/pe_bliss/SCsub b/tools/pe_bliss/SCsub new file mode 100644 index 0000000000..34524f10ef --- /dev/null +++ b/tools/pe_bliss/SCsub @@ -0,0 +1,5 @@ +Import('env') + +env.add_source_files(env.tool_sources,"*.cpp") + +Export('env') diff --git a/tools/pe_bliss/entropy.cpp b/tools/pe_bliss/entropy.cpp new file mode 100644 index 0000000000..acefa63e83 --- /dev/null +++ b/tools/pe_bliss/entropy.cpp @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* 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 <cmath> +#include "entropy.h" +#include "utils.h" + +namespace pe_bliss +{ +//Calculates entropy for PE image section +double entropy_calculator::calculate_entropy(const section& s) +{ + if(s.get_raw_data().empty()) //Don't count entropy for empty sections + throw pe_exception("Section is empty", pe_exception::section_is_empty); + + return calculate_entropy(s.get_raw_data().data(), s.get_raw_data().length()); +} + +//Calculates entropy for istream (from current position of stream) +double entropy_calculator::calculate_entropy(std::istream& file) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(file.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + std::streamoff pos = file.tellg(); + + std::streamoff length = pe_utils::get_file_size(file); + length -= file.tellg(); + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(std::streamoff i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(file.get())]; + + file.seekg(pos); + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for data block +double entropy_calculator::calculate_entropy(const char* data, size_t length) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + if(!length) //Don't calculate entropy for empty buffers + throw pe_exception("Data length is zero", pe_exception::data_is_empty); + + //Count bytes + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + + return calculate_entropy(byte_count, length); +} + +//Calculates entropy for this PE file (only section data) +double entropy_calculator::calculate_entropy(const pe_base& pe) +{ + uint32_t byte_count[256] = {0}; //Byte count for each of 255 bytes + + size_t total_data_length = 0; + + //Count bytes for each section + for(section_list::const_iterator it = pe.get_image_sections().begin(); it != pe.get_image_sections().end(); ++it) + { + const std::string& data = (*it).get_raw_data(); + size_t length = data.length(); + total_data_length += length; + for(size_t i = 0; i != length; ++i) + ++byte_count[static_cast<unsigned char>(data[i])]; + } + + return calculate_entropy(byte_count, total_data_length); +} + +//Calculates entropy from bytes count +double entropy_calculator::calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length) +{ + double entropy = 0.; //Entropy result value + //Calculate entropy + for(uint32_t i = 0; i < 256; ++i) + { + double temp = static_cast<double>(byte_count[i]) / total_length; + if(temp > 0.) + entropy += std::abs(temp * (std::log(temp) * pe_utils::log_2)); + } + + return entropy; +} +} diff --git a/tools/pe_bliss/entropy.h b/tools/pe_bliss/entropy.h new file mode 100644 index 0000000000..7d225a3e32 --- /dev/null +++ b/tools/pe_bliss/entropy.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* 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 <istream> +#include "pe_base.h" + +namespace pe_bliss +{ +class entropy_calculator +{ +public: + //Calculates entropy for PE image section + static double calculate_entropy(const section& s); + + //Calculates entropy for istream (from current position of stream) + static double calculate_entropy(std::istream& file); + + //Calculates entropy for data block + static double calculate_entropy(const char* data, size_t length); + + //Calculates entropy for this PE file (only section data) + static double calculate_entropy(const pe_base& pe); + +private: + entropy_calculator(); + entropy_calculator(const entropy_calculator&); + entropy_calculator& operator=(const entropy_calculator&); + + //Calculates entropy from bytes count + static double calculate_entropy(const uint32_t byte_count[256], std::streamoff total_length); +}; +} diff --git a/tools/pe_bliss/file_version_info.cpp b/tools/pe_bliss/file_version_info.cpp new file mode 100644 index 0000000000..3f2ba454b4 --- /dev/null +++ b/tools/pe_bliss/file_version_info.cpp @@ -0,0 +1,440 @@ +/*************************************************************************/ +/* 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 "file_version_info.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Default constructor +file_version_info::file_version_info() + :file_version_ms_(0), file_version_ls_(0), + product_version_ms_(0), product_version_ls_(0), + file_flags_(0), + file_os_(0), + file_type_(0), file_subtype_(0), + file_date_ms_(0), file_date_ls_(0) +{} + +//Constructor from Windows fixed version info structure +file_version_info::file_version_info(const vs_fixedfileinfo& info) + :file_version_ms_(info.dwFileVersionMS), file_version_ls_(info.dwFileVersionLS), + product_version_ms_(info.dwProductVersionMS), product_version_ls_(info.dwProductVersionLS), + file_flags_(info.dwFileFlags), + file_os_(info.dwFileOS), + file_type_(info.dwFileType), file_subtype_(info.dwFileSubtype), + file_date_ms_(info.dwFileDateMS), file_date_ls_(info.dwFileDateLS) +{} + +//Returns true if file is debug-built +bool file_version_info::is_debug() const +{ + return file_flags_ & vs_ff_debug ? true : false; +} + +//Returns true if file is release-built +bool file_version_info::is_prerelease() const +{ + return file_flags_ & vs_ff_prerelease ? true : false; +} + +//Returns true if file is patched +bool file_version_info::is_patched() const +{ + return file_flags_ & vs_ff_patched ? true : false; +} + +//Returns true if private build +bool file_version_info::is_private_build() const +{ + return file_flags_ & vs_ff_privatebuild ? true : false; +} + +//Returns true if special build +bool file_version_info::is_special_build() const +{ + return file_flags_ & vs_ff_specialbuild ? true : false; +} + +//Returns true if info inferred +bool file_version_info::is_info_inferred() const +{ + return file_flags_ & vs_ff_infoinferred ? true : false; +} + +//Retuens file flags (raw DWORD) +uint32_t file_version_info::get_file_flags() const +{ + return file_flags_; +} + +//Returns file version most significant DWORD +uint32_t file_version_info::get_file_version_ms() const +{ + return file_version_ms_; +} + +//Returns file version least significant DWORD +uint32_t file_version_info::get_file_version_ls() const +{ + return file_version_ls_; +} + +//Returns product version most significant DWORD +uint32_t file_version_info::get_product_version_ms() const +{ + return product_version_ms_; +} + +//Returns product version least significant DWORD +uint32_t file_version_info::get_product_version_ls() const +{ + return product_version_ls_; +} + +//Returns file OS type (raw DWORD) +uint32_t file_version_info::get_file_os_raw() const +{ + return file_os_; +} + +//Returns file OS type +file_version_info::file_os_type file_version_info::get_file_os() const +{ + //Determine file operation system type + switch(file_os_) + { + case vos_dos: + return file_os_dos; + + case vos_os216: + return file_os_os216; + + case vos_os232: + return file_os_os232; + + case vos_nt: + return file_os_nt; + + case vos_wince: + return file_os_wince; + + case vos__windows16: + return file_os_win16; + + case vos__pm16: + return file_os_pm16; + + case vos__pm32: + return file_os_pm32; + + case vos__windows32: + return file_os_win32; + + case vos_dos_windows16: + return file_os_dos_win16; + + case vos_dos_windows32: + return file_os_dos_win32; + + case vos_os216_pm16: + return file_os_os216_pm16; + + case vos_os232_pm32: + return file_os_os232_pm32; + + case vos_nt_windows32: + return file_os_nt_win32; + } + + return file_os_unknown; +} + +//Returns file type (raw DWORD) +uint32_t file_version_info::get_file_type_raw() const +{ + return file_type_; +} + +//Returns file type +file_version_info::file_type file_version_info::get_file_type() const +{ + //Determine file type + switch(file_type_) + { + case vft_app: + return file_type_application; + + case vft_dll: + return file_type_dll; + + case vft_drv: + return file_type_driver; + + case vft_font: + return file_type_font; + + case vft_vxd: + return file_type_vxd; + + case vft_static_lib: + return file_type_static_lib; + } + + return file_type_unknown; +} + +//Returns file subtype (usually non-zero for drivers and fonts) +uint32_t file_version_info::get_file_subtype() const +{ + return file_subtype_; +} + +//Returns file date most significant DWORD +uint32_t file_version_info::get_file_date_ms() const +{ + return file_date_ms_; +} + +//Returns file date least significant DWORD +uint32_t file_version_info::get_file_date_ls() const +{ + return file_date_ls_; +} + +//Helper to set file flag +void file_version_info::set_file_flag(uint32_t flag) +{ + file_flags_ |= flag; +} + +//Helper to clear file flag +void file_version_info::clear_file_flag(uint32_t flag) +{ + file_flags_ &= ~flag; +} + +//Helper to set or clear file flag +void file_version_info::set_file_flag(uint32_t flag, bool set_flag) +{ + set_flag ? set_file_flag(flag) : clear_file_flag(flag); +} + +//Sets if file is debug-built +void file_version_info::set_debug(bool debug) +{ + set_file_flag(vs_ff_debug, debug); +} + +//Sets if file is prerelease +void file_version_info::set_prerelease(bool prerelease) +{ + set_file_flag(vs_ff_prerelease, prerelease); +} + +//Sets if file is patched +void file_version_info::set_patched(bool patched) +{ + set_file_flag(vs_ff_patched, patched); +} + +//Sets if private build +void file_version_info::set_private_build(bool private_build) +{ + set_file_flag(vs_ff_privatebuild, private_build); +} + +//Sets if special build +void file_version_info::set_special_build(bool special_build) +{ + set_file_flag(vs_ff_specialbuild, special_build); +} + +//Sets if info inferred +void file_version_info::set_info_inferred(bool info_inferred) +{ + set_file_flag(vs_ff_infoinferred, info_inferred); +} + +//Sets flags (raw DWORD) +void file_version_info::set_file_flags(uint32_t file_flags) +{ + file_flags_ = file_flags; +} + +//Sets file version most significant DWORD +void file_version_info::set_file_version_ms(uint32_t file_version_ms) +{ + file_version_ms_ = file_version_ms; +} + +//Sets file version least significant DWORD +void file_version_info::set_file_version_ls(uint32_t file_version_ls) +{ + file_version_ls_ = file_version_ls; +} + +//Sets product version most significant DWORD +void file_version_info::set_product_version_ms(uint32_t product_version_ms) +{ + product_version_ms_ = product_version_ms; +} + +//Sets product version least significant DWORD +void file_version_info::set_product_version_ls(uint32_t product_version_ls) +{ + product_version_ls_ = product_version_ls; +} + +//Sets file OS type (raw DWORD) +void file_version_info::set_file_os_raw(uint32_t file_os) +{ + file_os_ = file_os; +} + +//Sets file OS type +void file_version_info::set_file_os(file_os_type file_os) +{ + //Determine file operation system type + switch(file_os) + { + case file_os_dos: + file_os_ = vos_dos; + return; + + case file_os_os216: + file_os_ = vos_os216; + return; + + case file_os_os232: + file_os_ = vos_os232; + return; + + case file_os_nt: + file_os_ = vos_nt; + return; + + case file_os_wince: + file_os_ = vos_wince; + return; + + case file_os_win16: + file_os_ = vos__windows16; + return; + + case file_os_pm16: + file_os_ = vos__pm16; + return; + + case file_os_pm32: + file_os_ = vos__pm32; + return; + + case file_os_win32: + file_os_ = vos__windows32; + return; + + case file_os_dos_win16: + file_os_ = vos_dos_windows16; + return; + + case file_os_dos_win32: + file_os_ = vos_dos_windows32; + return; + + case file_os_os216_pm16: + file_os_ = vos_os216_pm16; + return; + + case file_os_os232_pm32: + file_os_ = vos_os232_pm32; + return; + + case file_os_nt_win32: + file_os_ = vos_nt_windows32; + return; + + default: + return; + } +} + +//Sets file type (raw DWORD) +void file_version_info::set_file_type_raw(uint32_t file_type) +{ + file_type_ = file_type; +} + +//Sets file type +void file_version_info::set_file_type(file_type file_type) +{ + //Determine file type + switch(file_type) + { + case file_type_application: + file_type_ = vft_app; + return; + + case file_type_dll: + file_type_ = vft_dll; + return; + + case file_type_driver: + file_type_ = vft_drv; + return; + + case file_type_font: + file_type_ = vft_font; + return; + + case file_type_vxd: + file_type_ = vft_vxd; + return; + + case file_type_static_lib: + file_type_ = vft_static_lib; + return; + + default: + return; + } +} + +//Sets file subtype (usually non-zero for drivers and fonts) +void file_version_info::set_file_subtype(uint32_t file_subtype) +{ + file_subtype_ = file_subtype; +} + +//Sets file date most significant DWORD +void file_version_info::set_file_date_ms(uint32_t file_date_ms) +{ + file_date_ms_ = file_date_ms; +} + +//Sets file date least significant DWORD +void file_version_info::set_file_date_ls(uint32_t file_date_ls) +{ + file_date_ls_ = file_date_ls; +} +} diff --git a/tools/pe_bliss/file_version_info.h b/tools/pe_bliss/file_version_info.h new file mode 100644 index 0000000000..d898351ba1 --- /dev/null +++ b/tools/pe_bliss/file_version_info.h @@ -0,0 +1,199 @@ +/*************************************************************************/ +/* 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 <map> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +//Structure representing fixed file version info +class file_version_info +{ +public: + //Enumeration of file operating system types + enum file_os_type + { + file_os_unknown, + file_os_dos, + file_os_os216, + file_os_os232, + file_os_nt, + file_os_wince, + file_os_win16, + file_os_pm16, + file_os_pm32, + file_os_win32, + file_os_dos_win16, + file_os_dos_win32, + file_os_os216_pm16, + file_os_os232_pm32, + file_os_nt_win32 + }; + + //Enumeration of file types + enum file_type + { + file_type_unknown, + file_type_application, + file_type_dll, + file_type_driver, + file_type_font, + file_type_vxd, + file_type_static_lib + }; + +public: + //Default constructor + file_version_info(); + //Constructor from Windows fixed version info structure + explicit file_version_info(const pe_win::vs_fixedfileinfo& info); + +public: //Getters + //Returns true if file is debug-built + bool is_debug() const; + //Returns true if file is prerelease + bool is_prerelease() const; + //Returns true if file is patched + bool is_patched() const; + //Returns true if private build + bool is_private_build() const; + //Returns true if special build + bool is_special_build() const; + //Returns true if info inferred + bool is_info_inferred() const; + //Retuens file flags (raw DWORD) + uint32_t get_file_flags() const; + + //Returns file version most significant DWORD + uint32_t get_file_version_ms() const; + //Returns file version least significant DWORD + uint32_t get_file_version_ls() const; + //Returns product version most significant DWORD + uint32_t get_product_version_ms() const; + //Returns product version least significant DWORD + uint32_t get_product_version_ls() const; + + //Returns file OS type (raw DWORD) + uint32_t get_file_os_raw() const; + //Returns file OS type + file_os_type get_file_os() const; + + //Returns file type (raw DWORD) + uint32_t get_file_type_raw() const; + //Returns file type + file_type get_file_type() const; + + //Returns file subtype (usually non-zero for drivers and fonts) + uint32_t get_file_subtype() const; + + //Returns file date most significant DWORD + uint32_t get_file_date_ms() const; + //Returns file date least significant DWORD + uint32_t get_file_date_ls() const; + + //Returns file version string + template<typename T> + const std::basic_string<T> get_file_version_string() const + { + return get_version_string<T>(file_version_ms_, file_version_ls_); + } + + //Returns product version string + template<typename T> + const std::basic_string<T> get_product_version_string() const + { + return get_version_string<T>(product_version_ms_, product_version_ls_); + } + +public: //Setters + //Sets if file is debug-built + void set_debug(bool debug); + //Sets if file is prerelease + void set_prerelease(bool prerelease); + //Sets if file is patched + void set_patched(bool patched); + //Sets if private build + void set_private_build(bool private_build); + //Sets if special build + void set_special_build(bool special_build); + //Sets if info inferred + void set_info_inferred(bool info_inferred); + //Sets flags (raw DWORD) + void set_file_flags(uint32_t file_flags); + + //Sets file version most significant DWORD + void set_file_version_ms(uint32_t file_version_ms); + //Sets file version least significant DWORD + void set_file_version_ls(uint32_t file_version_ls); + //Sets product version most significant DWORD + void set_product_version_ms(uint32_t product_version_ms); + //Sets product version least significant DWORD + void set_product_version_ls(uint32_t product_version_ls); + + //Sets file OS type (raw DWORD) + void set_file_os_raw(uint32_t file_os); + //Sets file OS type + void set_file_os(file_os_type file_os); + + //Sets file type (raw DWORD) + void set_file_type_raw(uint32_t file_type); + //Sets file type + void set_file_type(file_type file_type); + + //Sets file subtype (usually non-zero for drivers and fonts) + void set_file_subtype(uint32_t file_subtype); + + //Sets file date most significant DWORD + void set_file_date_ms(uint32_t file_date_ms); + //Sets file date least significant DWORD + void set_file_date_ls(uint32_t file_date_ls); + +private: + //Helper to convert version DWORDs to string + template<typename T> + static const std::basic_string<T> get_version_string(uint32_t ms, uint32_t ls) + { + std::basic_stringstream<T> ss; + ss << (ms >> 16) << static_cast<T>(L'.') + << (ms & 0xFFFF) << static_cast<T>(L'.') + << (ls >> 16) << static_cast<T>(L'.') + << (ls & 0xFFFF); + return ss.str(); + } + + //Helper to set file flag + void set_file_flag(uint32_t flag); + //Helper to clear file flag + void clear_file_flag(uint32_t flag); + //Helper to set or clear file flag + void set_file_flag(uint32_t flag, bool set_flag); + + uint32_t file_version_ms_, file_version_ls_, + product_version_ms_, product_version_ls_; + uint32_t file_flags_; + uint32_t file_os_; + uint32_t file_type_, file_subtype_; + uint32_t file_date_ms_, file_date_ls_; +}; +} diff --git a/tools/pe_bliss/message_table.cpp b/tools/pe_bliss/message_table.cpp new file mode 100644 index 0000000000..909be5d494 --- /dev/null +++ b/tools/pe_bliss/message_table.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* 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 "message_table.h" +#include "utils.h" + +namespace pe_bliss +{ +//Default constructor +message_table_item::message_table_item() + :unicode_(false) +{} + +//Constructor from ANSI string +message_table_item::message_table_item(const std::string& str) + :unicode_(false), ansi_str_(str) +{ + pe_utils::strip_nullbytes(ansi_str_); +} + +//Constructor from UNICODE string +message_table_item::message_table_item(const std::wstring& str) + :unicode_(true), unicode_str_(str) +{ + pe_utils::strip_nullbytes(unicode_str_); +} + +//Returns true if contained string is unicode +bool message_table_item::is_unicode() const +{ + return unicode_; +} + +//Returns ANSI string +const std::string& message_table_item::get_ansi_string() const +{ + return ansi_str_; +} + +//Returns UNICODE string +const std::wstring& message_table_item::get_unicode_string() const +{ + return unicode_str_; +} + +//Sets ANSI string (clears UNICODE one) +void message_table_item::set_string(const std::string& str) +{ + ansi_str_ = str; + pe_utils::strip_nullbytes(ansi_str_); + unicode_str_.clear(); + unicode_ = false; +} + +//Sets UNICODE string (clears ANSI one) +void message_table_item::set_string(const std::wstring& str) +{ + unicode_str_ = str; + pe_utils::strip_nullbytes(unicode_str_); + ansi_str_.clear(); + unicode_ = true; +} +} diff --git a/tools/pe_bliss/message_table.h b/tools/pe_bliss/message_table.h new file mode 100644 index 0000000000..5a3feb32c1 --- /dev/null +++ b/tools/pe_bliss/message_table.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* 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 <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Structure representing message table string +class message_table_item +{ +public: + //Default constructor + message_table_item(); + //Constructors from ANSI and UNICODE strings + explicit message_table_item(const std::string& str); + explicit message_table_item(const std::wstring& str); + + //Returns true if string is UNICODE + bool is_unicode() const; + //Returns ANSI string + const std::string& get_ansi_string() const; + //Returns UNICODE string + const std::wstring& get_unicode_string() const; + +public: + //Sets ANSI or UNICODE string + void set_string(const std::string& str); + void set_string(const std::wstring& str); + +private: + bool unicode_; + std::string ansi_str_; + std::wstring unicode_str_; +}; +} diff --git a/tools/pe_bliss/pe_base.cpp b/tools/pe_bliss/pe_base.cpp new file mode 100644 index 0000000000..97baa17cb3 --- /dev/null +++ b/tools/pe_bliss/pe_base.cpp @@ -0,0 +1,1680 @@ +/*************************************************************************/ +/* 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 <string> +#include <vector> +#include <istream> +#include <ostream> +#include <algorithm> +#include <cmath> +#include <set> +#include <string.h> +#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<uint16_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint16_t>(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<unsigned long>(full_headers_data_.length()); + + const section& s = section_from_rva(rva); + return static_cast<unsigned long>(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<unsigned long>(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<long>(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<unsigned long>(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<int32_t>(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<uint32_t>(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<char*>(&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<uint32_t>(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<char*>(&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<uint32_t>(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<std::streamoff>(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<uint32_t>(get_size_of_headers(), static_cast<uint32_t>(filesize)); + + if(!sections_.empty()) + { + for(section_list::const_iterator i = sections_.begin(); i != sections_.end(); ++i) + { + if(!(*i).empty()) + { + size_of_headers = std::min<uint32_t>(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<image_debug_directory>(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<image_debug_directory>(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<char*>(&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<uint32_t, const section*> 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<uint32_t>(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(); +} +} 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); +}; +} diff --git a/tools/pe_bliss/pe_bliss.h b/tools/pe_bliss/pe_bliss.h new file mode 100644 index 0000000000..1a8b430284 --- /dev/null +++ b/tools/pe_bliss/pe_bliss.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* 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 "pe_base.h" +#include "pe_rebuilder.h" +#include "pe_factory.h" +#include "pe_bound_import.h" +#include "pe_debug.h" +#include "pe_dotnet.h" +#include "pe_exception_directory.h" +#include "pe_exports.h" +#include "pe_imports.h" +#include "pe_load_config.h" +#include "pe_relocations.h" +#include "pe_resources.h" +#include "pe_rich_data.h" +#include "pe_tls.h" +#include "pe_properties_generic.h" +#include "pe_checksum.h" +#include "entropy.h" diff --git a/tools/pe_bliss/pe_bliss_godot.cpp b/tools/pe_bliss/pe_bliss_godot.cpp new file mode 100644 index 0000000000..8297aa1045 --- /dev/null +++ b/tools/pe_bliss/pe_bliss_godot.cpp @@ -0,0 +1,118 @@ +#include "pe_bliss/pe_bliss.h" +#include "pe_bliss/pe_bliss_resources.h" +#include "core/ustring.h" +#include "core/dvector.h" +#include "os/file_access.h" + +using namespace pe_bliss; + +String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor, + String& company_name, String& file_description, + String& legal_copyright, String& version_text, + String& product_name, String& godot_version, + DVector<uint8_t>& icon_content) { + try + { + pe_base image(pe_factory::create_pe(p_path)); + + const section_list& pe_sections = image.get_image_sections(); + uint32_t end_of_pe = 0; + FileAccess *dst; + DVector<uint8_t> overlay_data; + if(image.has_overlay()) + { + end_of_pe = pe_sections.back().get_pointer_to_raw_data() + pe_sections.back().get_size_of_raw_data(); + dst=FileAccess::open(p_path,FileAccess::READ); + if (dst) { + overlay_data.resize(dst->get_len()-end_of_pe); + dst->seek(end_of_pe); + DVector<uint8_t>::Write overlay_data_write = overlay_data.write(); + dst->get_buffer(overlay_data_write.ptr(),overlay_data.size()); + dst->close(); + memdelete(dst); + } + } + resource_directory root; + if(image.has_resources()) + { + root = resource_directory(get_resources(image)); + } + pe_resource_manager res(root); + if(image.has_resources()) + { + if(icon_content.size()) { + if(res.resource_exists(pe_resource_viewer::resource_icon)) + { + res.remove_resource_type(pe_resource_viewer::resource_icon); + } + if(res.resource_exists(pe_resource_viewer::resource_icon_group)) + { + res.remove_resource_type(pe_resource_viewer::resource_icon_group); + } + } + if(res.resource_exists(pe_resource_viewer::resource_version)) + { + res.remove_resource_type(pe_resource_viewer::resource_version); + } + } + file_version_info file_info; + file_info.set_file_os(file_version_info::file_os_nt_win32); + file_info.set_file_type(file_version_info::file_type_application); + unsigned int ver = version_major << 16; + ver = ver + version_minor; + file_info.set_file_version_ms(ver); + file_info.set_file_version_ls(0x00000000); + file_info.set_product_version_ms(ver); + file_info.set_product_version_ls(0x00000000); + lang_string_values_map strings; + translation_values_map translations; + version_info_editor version(strings, translations); + version.add_translation(version_info_editor::default_language_translation); + version.set_company_name(company_name.c_str()); + version.set_file_description(file_description.c_str()); + if (!product_name.empty()) { + version.set_internal_name((product_name+String(".exe")).c_str()); + version.set_original_filename((product_name+String(".exe")).c_str()); + version.set_product_name(product_name.c_str()); + } + version.set_legal_copyright(legal_copyright.c_str()); + version.set_product_version(version_text.c_str()); + if(!godot_version.empty()) version.set_property(L"Godot Engine Version", godot_version.c_str() ); + resource_version_info_writer(res).set_version_info(file_info, strings, translations, 1033, 1200); + if(icon_content.size()) { + std::string icon; + icon.resize(icon_content.size()); + for(int i=0; i<icon_content.size(); i++) + { + icon[i] = icon_content[i]; + } + resource_cursor_icon_writer(res).add_icon(icon, L"MAIN_ICON", 1033); + } + if(image.has_resources()) + { + rebuild_resources(image, root, image.section_from_directory(pe_win::image_directory_entry_resource)); + } else { + section new_resources; + new_resources.get_raw_data().resize(1); + new_resources.set_name(".rsrc"); + new_resources.readable(true); + section& attached_section = image.add_section(new_resources); + rebuild_resources(image, root, attached_section); + } + rebuild_pe(image, p_path); + if(image.has_overlay() && end_of_pe) { + dst=FileAccess::open(p_path,FileAccess::READ_WRITE); + if (dst) { + dst->seek_end(); + DVector<uint8_t>::Read overlay_data_read = overlay_data.read(); + dst->store_buffer(overlay_data_read.ptr(),overlay_data.size()); + dst->close(); + memdelete(dst); + } + } + return String(); + } catch(const pe_exception& e) { + String ret("Error In Add rsrc Section : "); + return ret + String(e.what()); + } +} diff --git a/tools/pe_bliss/pe_bliss_godot.h b/tools/pe_bliss/pe_bliss_godot.h new file mode 100644 index 0000000000..0365ca9eaf --- /dev/null +++ b/tools/pe_bliss/pe_bliss_godot.h @@ -0,0 +1,7 @@ + + +String pe_bliss_add_resrc(const char* p_path, int version_major, int version_minor, + String& company_name, String& file_description, + String& legal_copyright, String& version_text, + String& product_name, String& godot_version, + DVector<uint8_t>& icon_content); diff --git a/tools/pe_bliss/pe_bliss_resources.h b/tools/pe_bliss/pe_bliss_resources.h new file mode 100644 index 0000000000..60369f8011 --- /dev/null +++ b/tools/pe_bliss/pe_bliss_resources.h @@ -0,0 +1,36 @@ +/*************************************************************************/ +/* 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 "file_version_info.h" +#include "message_table.h" +#include "pe_resource_manager.h" +#include "pe_resource_viewer.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" +#include "resource_bitmap_reader.h" +#include "resource_bitmap_writer.h" +#include "resource_cursor_icon_reader.h" +#include "resource_cursor_icon_writer.h" +#include "resource_version_info_reader.h" +#include "resource_version_info_writer.h" +#include "resource_string_table_reader.h" +#include "resource_message_list_reader.h" diff --git a/tools/pe_bliss/pe_bound_import.cpp b/tools/pe_bliss/pe_bound_import.cpp new file mode 100644 index 0000000000..4b54b36105 --- /dev/null +++ b/tools/pe_bliss/pe_bound_import.cpp @@ -0,0 +1,311 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "pe_bound_import.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//BOUND IMPORT +//Default constructor +bound_import_ref::bound_import_ref() + :timestamp_(0) +{} + +//Constructor from data +bound_import_ref::bound_import_ref(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import_ref::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import_ref::get_timestamp() const +{ + return timestamp_; +} + +//Sets module name +void bound_import_ref::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import_ref::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Default constructor +bound_import::bound_import() + :timestamp_(0) +{} + +//Constructor from data +bound_import::bound_import(const std::string& module_name, uint32_t timestamp) + :module_name_(module_name), timestamp_(timestamp) +{} + +//Returns imported module name +const std::string& bound_import::get_module_name() const +{ + return module_name_; +} + +//Returns bound import date and time stamp +uint32_t bound_import::get_timestamp() const +{ + return timestamp_; +} + +//Returns bound references cound +size_t bound_import::get_module_ref_count() const +{ + return refs_.size(); +} + +//Returns module references +const bound_import::ref_list& bound_import::get_module_ref_list() const +{ + return refs_; +} + +//Adds module reference +void bound_import::add_module_ref(const bound_import_ref& ref) +{ + refs_.push_back(ref); +} + +//Clears module references list +void bound_import::clear_module_refs() +{ + refs_.clear(); +} + +//Returns module references +bound_import::ref_list& bound_import::get_module_ref_list() +{ + return refs_; +} + +//Sets module name +void bound_import::set_module_name(const std::string& module_name) +{ + module_name_ = module_name; +} + +//Sets timestamp +void bound_import::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +const bound_import_module_list get_bound_import_module_list(const pe_base& pe) +{ + //Returned bound import modules list + bound_import_module_list ret; + + //If image has no bound imports + if(!pe.has_bound_import()) + return ret; + + uint32_t bound_import_data_len = + pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + if(bound_import_data_len < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + const char* bound_import_data = pe.section_data_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true); + + //Check read in "read_pe" function raw bound import data size + if(bound_import_data_len < sizeof(image_bound_import_descriptor)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //current bound_import_data_ in-string position + unsigned long current_pos = 0; + //first bound import descriptor + //so, we're working with raw data here, no section helpers available + const image_bound_import_descriptor* descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Enumerate until zero + while(descriptor->OffsetModuleName) + { + //Check module name offset + if(descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Check module name for null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[descriptor->OffsetModuleName], bound_import_data_len - descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Create bound import descriptor structure + bound_import elem(&bound_import_data[descriptor->OffsetModuleName], descriptor->TimeDateStamp); + + //Check DWORDs + if(descriptor->NumberOfModuleForwarderRefs >= pe_utils::max_dword / sizeof(image_bound_forwarder_ref) + || !pe_utils::is_sum_safe(current_pos, 2 /* this descriptor and the next one */ * sizeof(image_bound_import_descriptor) + descriptor->NumberOfModuleForwarderRefs * sizeof(image_bound_forwarder_ref))) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move after current descriptor + current_pos += sizeof(image_bound_import_descriptor); + + //Enumerate referenced bound import descriptors + for(unsigned long i = 0; i != descriptor->NumberOfModuleForwarderRefs; ++i) + { + //They're just after parent descriptor + //Check size of structure + if(current_pos + sizeof(image_bound_forwarder_ref) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Get IMAGE_BOUND_FORWARDER_REF pointer + const image_bound_forwarder_ref* ref_descriptor = reinterpret_cast<const image_bound_forwarder_ref*>(&bound_import_data[current_pos]); + + //Check referenced module name + if(ref_descriptor->OffsetModuleName >= bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //And its null-termination + if(!pe_utils::is_null_terminated(&bound_import_data[ref_descriptor->OffsetModuleName], bound_import_data_len - ref_descriptor->OffsetModuleName)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Add referenced module to current bound import structure + elem.add_module_ref(bound_import_ref(&bound_import_data[ref_descriptor->OffsetModuleName], ref_descriptor->TimeDateStamp)); + + //Move after referenced bound import descriptor + current_pos += sizeof(image_bound_forwarder_ref); + } + + //Check structure size + if(current_pos + sizeof(image_bound_import_descriptor) > bound_import_data_len) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + + //Move to next bound import descriptor + descriptor = reinterpret_cast<const image_bound_import_descriptor*>(&bound_import_data[current_pos]); + + //Save created descriptor structure and references + ret.push_back(elem); + } + + //Return result + return ret; +} + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(imports_section)) + throw pe_exception("Bound import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size = sizeof(image_bound_import_descriptor) /* Ending null descriptor */; + uint32_t needed_size_for_strings = 0; + + //Calculate needed size for bound import data + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + needed_size += sizeof(image_bound_import_descriptor); + needed_size_for_strings += static_cast<uint32_t>((*it).get_module_name().length()) + 1 /* nullbyte */; + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + needed_size_for_strings += static_cast<uint32_t>((*ref_it).get_module_name().length()) + 1 /* nullbyte */; + needed_size += sizeof(image_bound_forwarder_ref); + } + } + + needed_size += needed_size_for_strings; + + //Check if imports_section is last one. If it's not, check if there's enough place for bound import data + if(&imports_section != &*(pe.get_image_sections().end() - 1) && + (imports_section.empty() || pe_utils::align_up(imports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for bound import directory", pe_exception::insufficient_space); + + std::string& raw_data = imports_section.get_raw_data(); + + //This will be done only if imports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + uint32_t current_pos_for_structures = directory_pos; + uint32_t current_pos_for_strings = current_pos_for_structures + needed_size - needed_size_for_strings; + + for(bound_import_module_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + const bound_import& import = *it; + image_bound_import_descriptor descriptor; + descriptor.NumberOfModuleForwarderRefs = static_cast<uint16_t>(import.get_module_ref_list().size()); + descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + descriptor.TimeDateStamp = import.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &descriptor, sizeof(descriptor)); + current_pos_for_structures += sizeof(descriptor); + + size_t length = import.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], import.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + + const bound_import::ref_list& refs = import.get_module_ref_list(); + for(bound_import::ref_list::const_iterator ref_it = refs.begin(); ref_it != refs.end(); ++ref_it) + { + const bound_import_ref& ref = *ref_it; + image_bound_forwarder_ref ref_descriptor = {0}; + ref_descriptor.OffsetModuleName = static_cast<uint16_t>(current_pos_for_strings - directory_pos); + ref_descriptor.TimeDateStamp = ref.get_timestamp(); + + memcpy(&raw_data[current_pos_for_structures], &ref_descriptor, sizeof(ref_descriptor)); + current_pos_for_structures += sizeof(ref_descriptor); + + length = ref.get_module_name().length() + 1 /* nullbyte */; + memcpy(&raw_data[current_pos_for_strings], ref.get_module_name().c_str(), length); + current_pos_for_strings += static_cast<uint32_t>(length); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(imports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(imports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_bound_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_bound_import, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_bound_import.h b/tools/pe_bliss/pe_bound_import.h new file mode 100644 index 0000000000..667e28792e --- /dev/null +++ b/tools/pe_bliss/pe_bound_import.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* 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 <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing bound import reference +class bound_import_ref +{ +public: + //Default constructor + bound_import_ref(); + //Constructor from data + bound_import_ref(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp +}; + +//Class representing image bound import information +class bound_import +{ +public: + typedef std::vector<bound_import_ref> ref_list; + +public: + //Default constructor + bound_import(); + //Constructor from data + bound_import(const std::string& module_name, uint32_t timestamp); + + //Returns imported module name + const std::string& get_module_name() const; + //Returns bound import date and time stamp + uint32_t get_timestamp() const; + + //Returns bound references cound + size_t get_module_ref_count() const; + //Returns module references + const ref_list& get_module_ref_list() const; + +public: //Setters + //Sets module name + void set_module_name(const std::string& module_name); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds module reference + void add_module_ref(const bound_import_ref& ref); + //Clears module references list + void clear_module_refs(); + //Returns module references + ref_list& get_module_ref_list(); + +private: + std::string module_name_; //Imported module name + uint32_t timestamp_; //Bound import timestamp + ref_list refs_; //Module references list +}; + +typedef std::vector<bound_import> bound_import_module_list; + +//Returns bound import information +const bound_import_module_list get_bound_import_module_list(const pe_base& pe);//Export directory rebuilder + +//imports - bound imported modules list +//imports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from imports_section raw data start +//save_to_pe_headers - if true, new bound import directory information will be saved to PE image headers +//auto_strip_last_section - if true and bound imports are placed in the last section, it will be automatically stripped +const image_directory rebuild_bound_imports(pe_base& pe, const bound_import_module_list& imports, section& imports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_checksum.cpp b/tools/pe_bliss/pe_checksum.cpp new file mode 100644 index 0000000000..5971a33c90 --- /dev/null +++ b/tools/pe_bliss/pe_checksum.cpp @@ -0,0 +1,103 @@ +/*************************************************************************/ +/* 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 "pe_checksum.h" +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Calculate checksum of image +uint32_t calculate_checksum(std::istream& file) +{ + //Save istream state + std::ios_base::iostate state = file.exceptions(); + std::streamoff old_offset = file.tellg(); + + //Checksum value + unsigned long long checksum = 0; + + try + { + image_dos_header header; + + file.exceptions(std::ios::goodbit); + + //Read DOS header + pe_base::read_dos_header(file, header); + + //Calculate PE checksum + file.seekg(0); + unsigned long long top = 0xFFFFFFFF; + top++; + + //"CheckSum" field position in optional PE headers - it's always 64 for PE and PE+ + static const unsigned long checksum_pos_in_optional_headers = 64; + //Calculate real PE headers "CheckSum" field position + //Sum is safe here + unsigned long pe_checksum_pos = header.e_lfanew + sizeof(image_file_header) + sizeof(uint32_t) + checksum_pos_in_optional_headers; + + //Calculate checksum for each byte of file + std::streamoff filesize = pe_utils::get_file_size(file); + for(long long i = 0; i < filesize; i += 4) + { + unsigned long dw = 0; + + //Read DWORD from file + file.read(reinterpret_cast<char*>(&dw), sizeof(unsigned long)); + //Skip "CheckSum" DWORD + if(i == pe_checksum_pos) + continue; + + //Calculate checksum + checksum = (checksum & 0xffffffff) + dw + (checksum >> 32); + if(checksum > top) + checksum = (checksum & 0xffffffff) + (checksum >> 32); + } + + //Finish checksum + checksum = (checksum & 0xffff) + (checksum >> 16); + checksum = (checksum) + (checksum >> 16); + checksum = checksum & 0xffff; + + checksum += static_cast<unsigned long>(filesize); + } + catch(const std::exception&) + { + //If something went wrong, restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + //Rethrow + throw; + } + + //Restore istream state + file.exceptions(state); + file.seekg(old_offset); + file.clear(); + + //Return checksum + return static_cast<uint32_t>(checksum); +} +} diff --git a/tools/pe_bliss/pe_checksum.h b/tools/pe_bliss/pe_checksum.h new file mode 100644 index 0000000000..a568d5d369 --- /dev/null +++ b/tools/pe_bliss/pe_checksum.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* 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 <istream> +#include "stdint_defs.h" + +namespace pe_bliss +{ +//Calculate checksum of image (performs no checks on PE structures) +uint32_t calculate_checksum(std::istream& file); +} diff --git a/tools/pe_bliss/pe_debug.cpp b/tools/pe_bliss/pe_debug.cpp new file mode 100644 index 0000000000..a0ed3f5af1 --- /dev/null +++ b/tools/pe_bliss/pe_debug.cpp @@ -0,0 +1,865 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "pe_debug.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; +//DEBUG +//Default constructor +debug_info::debug_info() + :characteristics_(0), + time_stamp_(0), + major_version_(0), minor_version_(0), + type_(0), + size_of_data_(0), + address_of_raw_data_(0), + pointer_to_raw_data_(0), + advanced_info_type_(advanced_info_none) +{} + +//Constructor from data +debug_info::debug_info(const image_debug_directory& debug) + :characteristics_(debug.Characteristics), + time_stamp_(debug.TimeDateStamp), + major_version_(debug.MajorVersion), minor_version_(debug.MinorVersion), + type_(debug.Type), + size_of_data_(debug.SizeOfData), + address_of_raw_data_(debug.AddressOfRawData), + pointer_to_raw_data_(debug.PointerToRawData), + advanced_info_type_(advanced_info_none) +{} + +//Returns debug characteristics +uint32_t debug_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns debug datetimestamp +uint32_t debug_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version +uint32_t debug_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint32_t debug_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns type of debug info (unchecked) +uint32_t debug_info::get_type_raw() const +{ + return type_; +} + +//Returns type of debug info from debug_info_type enumeration +debug_info::debug_info_type debug_info::get_type() const +{ + //Determine debug type + switch(type_) + { + case image_debug_type_coff: + return debug_type_coff; + + case image_debug_type_codeview: + return debug_type_codeview; + + case image_debug_type_fpo: + return debug_type_fpo; + + case image_debug_type_misc: + return debug_type_misc; + + case image_debug_type_exception: + return debug_type_exception; + + case image_debug_type_fixup: + return debug_type_fixup; + + case image_debug_type_omap_to_src: + return debug_type_omap_to_src; + + case image_debug_type_omap_from_src: + return debug_type_omap_from_src; + + case image_debug_type_borland: + return debug_type_borland; + + case image_debug_type_clsid: + return debug_type_clsid; + + case image_debug_type_reserved10: + return debug_type_reserved10; + } + + return debug_type_unknown; +} + +//Returns size of debug data (internal, .pdb or other file doesn't count) +uint32_t debug_info::get_size_of_data() const +{ + return size_of_data_; +} + +//Returns RVA of debug info when mapped to memory or zero, if info is not mapped +uint32_t debug_info::get_rva_of_raw_data() const +{ + return address_of_raw_data_; +} + +//Returns raw file pointer to raw data +uint32_t debug_info::get_pointer_to_raw_data() const +{ + return pointer_to_raw_data_; +} + +//Copy constructor +debug_info::debug_info(const debug_info& info) + :characteristics_(info.characteristics_), + time_stamp_(info.time_stamp_), + major_version_(info.major_version_), minor_version_(info.minor_version_), + type_(info.type_), + size_of_data_(info.size_of_data_), + address_of_raw_data_(info.address_of_raw_data_), + pointer_to_raw_data_(info.pointer_to_raw_data_), + advanced_info_type_(info.advanced_info_type_) +{ + copy_advanced_info(info); +} + +//Copy assignment operator +debug_info& debug_info::operator=(const debug_info& info) +{ + copy_advanced_info(info); + + characteristics_ = info.characteristics_; + time_stamp_ = info.time_stamp_; + major_version_ = info.major_version_; + minor_version_ = info.minor_version_; + type_ = info.type_; + size_of_data_ = info.size_of_data_; + address_of_raw_data_ = info.address_of_raw_data_; + pointer_to_raw_data_ = info.pointer_to_raw_data_; + advanced_info_type_ = info.advanced_info_type_; + + return *this; +} + +//Default constructor +debug_info::advanced_info::advanced_info() + :adv_pdb_7_0_info(0) //Zero pointer to advanced data +{} + +//Returns true if advanced debug info is present +bool debug_info::advanced_info::is_present() const +{ + return adv_pdb_7_0_info != 0; +} + +//Helper for advanced debug information copying +void debug_info::copy_advanced_info(const debug_info& info) +{ + free_present_advanced_info(); + + switch(info.advanced_info_type_) + { + case advanced_info_pdb_7_0: + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(*info.advanced_debug_info_.adv_pdb_7_0_info); + break; + case advanced_info_pdb_2_0: + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(*info.advanced_debug_info_.adv_pdb_2_0_info); + break; + case advanced_info_misc: + advanced_debug_info_.adv_misc_info = new misc_debug_info(*info.advanced_debug_info_.adv_misc_info); + break; + case advanced_info_coff: + advanced_debug_info_.adv_coff_info = new coff_debug_info(*info.advanced_debug_info_.adv_coff_info); + break; + default: + break; + } + + advanced_info_type_ = info.advanced_info_type_; +} + +//Helper for clearing any present advanced debug information +void debug_info::free_present_advanced_info() +{ + switch(advanced_info_type_) + { + case advanced_info_pdb_7_0: + delete advanced_debug_info_.adv_pdb_7_0_info; + break; + case advanced_info_pdb_2_0: + delete advanced_debug_info_.adv_pdb_2_0_info; + break; + case advanced_info_misc: + delete advanced_debug_info_.adv_misc_info; + break; + case advanced_info_coff: + delete advanced_debug_info_.adv_coff_info; + break; + default: + break; + } + + advanced_debug_info_.adv_pdb_7_0_info = 0; + advanced_info_type_ = advanced_info_none; +} + +//Destructor +debug_info::~debug_info() +{ + free_present_advanced_info(); +} + +//Sets advanced debug information +void debug_info::set_advanced_debug_info(const pdb_7_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_7_0_info = new pdb_7_0_info(info); + advanced_info_type_ = advanced_info_pdb_7_0; +} + +void debug_info::set_advanced_debug_info(const pdb_2_0_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_pdb_2_0_info = new pdb_2_0_info(info); + advanced_info_type_ = advanced_info_pdb_2_0; +} + +void debug_info::set_advanced_debug_info(const misc_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_misc_info = new misc_debug_info(info); + advanced_info_type_ = advanced_info_misc; +} + +void debug_info::set_advanced_debug_info(const coff_debug_info& info) +{ + free_present_advanced_info(); + advanced_debug_info_.adv_coff_info = new coff_debug_info(info); + advanced_info_type_ = advanced_info_coff; +} + +//Returns advanced debug information type +debug_info::advanced_info_type debug_info::get_advanced_info_type() const +{ + return advanced_info_type_; +} + +//Returns advanced debug information or throws an exception, +//if requested information type is not contained by structure +template<> +const pdb_7_0_info debug_info::get_advanced_debug_info<pdb_7_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_7_0) + throw pe_exception("Debug info structure does not contain PDB 7.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_7_0_info; +} + +template<> +const pdb_2_0_info debug_info::get_advanced_debug_info<pdb_2_0_info>() const +{ + if(advanced_info_type_ != advanced_info_pdb_2_0) + throw pe_exception("Debug info structure does not contain PDB 2.0 data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_pdb_2_0_info; +} + +template<> +const misc_debug_info debug_info::get_advanced_debug_info<misc_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_misc) + throw pe_exception("Debug info structure does not contain MISC data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_misc_info; +} + +template<> +const coff_debug_info debug_info::get_advanced_debug_info<coff_debug_info>() const +{ + if(advanced_info_type_ != advanced_info_coff) + throw pe_exception("Debug info structure does not contain COFF data", pe_exception::advanced_debug_information_request_error); + + return *advanced_debug_info_.adv_coff_info; +} + +//Sets advanced debug information type, if no advanced info structure available +void debug_info::set_advanced_info_type(advanced_info_type type) +{ + free_present_advanced_info(); + if(advanced_info_type_ >= advanced_info_codeview_4_0) //Don't set info type for those types, which have advanced info structures + advanced_info_type_ = type; +} + +//Default constructor +pdb_7_0_info::pdb_7_0_info() + :age_(0) +{ + memset(&guid_, 0, sizeof(guid_)); +} + +//Constructor from data +pdb_7_0_info::pdb_7_0_info(const CV_INFO_PDB70* info) + :age_(info->Age), guid_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 7.0 structure GUID +const guid pdb_7_0_info::get_guid() const +{ + return guid_; +} + +//Returns age of build +uint32_t pdb_7_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_7_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +pdb_2_0_info::pdb_2_0_info() + :age_(0), signature_(0) +{} + +//Constructor from data +pdb_2_0_info::pdb_2_0_info(const CV_INFO_PDB20* info) + :age_(info->Age), signature_(info->Signature), + pdb_file_name_(reinterpret_cast<const char*>(info->PdbFileName)) //Must be checked before for null-termination +{} + +//Returns debug PDB 2.0 structure signature +uint32_t pdb_2_0_info::get_signature() const +{ + return signature_; +} + +//Returns age of build +uint32_t pdb_2_0_info::get_age() const +{ + return age_; +} + +//Returns PDB file name / path +const std::string& pdb_2_0_info::get_pdb_file_name() const +{ + return pdb_file_name_; +} + +//Default constructor +misc_debug_info::misc_debug_info() + :data_type_(0), unicode_(false) +{} + +//Constructor from data +misc_debug_info::misc_debug_info(const image_debug_misc* info) + :data_type_(info->DataType), unicode_(info->Unicode ? true : false) +{ + //IMAGE_DEBUG_MISC::Data must be checked before! + if(info->Unicode) + { +#ifdef PE_BLISS_WINDOWS + debug_data_unicode_ = std::wstring(reinterpret_cast<const wchar_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2); +#else + debug_data_unicode_ = pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(info->Data), (info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */) / 2)); +#endif + + pe_utils::strip_nullbytes(debug_data_unicode_); //Strip nullbytes in the end of string + } + else + { + debug_data_ansi_ = std::string(reinterpret_cast<const char*>(info->Data), info->Length - sizeof(image_debug_misc) + 1 /* BYTE[1] in the end of structure */); + pe_utils::strip_nullbytes(debug_data_ansi_); //Strip nullbytes in the end of string + } +} + +//Returns debug data type +uint32_t misc_debug_info::get_data_type() const +{ + return data_type_; +} + +//Returns true if data type is exe name +bool misc_debug_info::is_exe_name() const +{ + return data_type_ == image_debug_misc_exename; +} + +//Returns true if debug data is UNICODE +bool misc_debug_info::is_unicode() const +{ + return unicode_; +} + +//Returns debug data (ANSI) +const std::string& misc_debug_info::get_data_ansi() const +{ + return debug_data_ansi_; +} + +//Returns debug data (UNICODE) +const std::wstring& misc_debug_info::get_data_unicode() const +{ + return debug_data_unicode_; +} + +//Default constructor +coff_debug_info::coff_debug_info() + :number_of_symbols_(0), + lva_to_first_symbol_(0), + number_of_line_numbers_(0), + lva_to_first_line_number_(0), + rva_to_first_byte_of_code_(0), + rva_to_last_byte_of_code_(0), + rva_to_first_byte_of_data_(0), + rva_to_last_byte_of_data_(0) +{} + +//Constructor from data +coff_debug_info::coff_debug_info(const image_coff_symbols_header* info) + :number_of_symbols_(info->NumberOfSymbols), + lva_to_first_symbol_(info->LvaToFirstSymbol), + number_of_line_numbers_(info->NumberOfLinenumbers), + lva_to_first_line_number_(info->LvaToFirstLinenumber), + rva_to_first_byte_of_code_(info->RvaToFirstByteOfCode), + rva_to_last_byte_of_code_(info->RvaToLastByteOfCode), + rva_to_first_byte_of_data_(info->RvaToFirstByteOfData), + rva_to_last_byte_of_data_(info->RvaToLastByteOfData) +{} + +//Returns number of symbols +uint32_t coff_debug_info::get_number_of_symbols() const +{ + return number_of_symbols_; +} + +//Returns virtual address of the first symbol +uint32_t coff_debug_info::get_lva_to_first_symbol() const +{ + return lva_to_first_symbol_; +} + +//Returns number of line-number entries +uint32_t coff_debug_info::get_number_of_line_numbers() const +{ + return number_of_line_numbers_; +} + +//Returns virtual address of the first line-number entry +uint32_t coff_debug_info::get_lva_to_first_line_number() const +{ + return lva_to_first_line_number_; +} + +//Returns relative virtual address of the first byte of code +uint32_t coff_debug_info::get_rva_to_first_byte_of_code() const +{ + return rva_to_first_byte_of_code_; +} + +//Returns relative virtual address of the last byte of code +uint32_t coff_debug_info::get_rva_to_last_byte_of_code() const +{ + return rva_to_last_byte_of_code_; +} + +//Returns relative virtual address of the first byte of data +uint32_t coff_debug_info::get_rva_to_first_byte_of_data() const +{ + return rva_to_first_byte_of_data_; +} + +//Returns relative virtual address of the last byte of data +uint32_t coff_debug_info::get_rva_to_last_byte_of_data() const +{ + return rva_to_last_byte_of_data_; +} + +//Returns COFF symbols list +const coff_debug_info::coff_symbols_list& coff_debug_info::get_symbols() const +{ + return symbols_; +} + +//Adds COFF symbol +void coff_debug_info::add_symbol(const coff_symbol& sym) +{ + symbols_.push_back(sym); +} + +//Default constructor +coff_debug_info::coff_symbol::coff_symbol() + :storage_class_(0), + index_(0), + section_number_(0), rva_(0), + type_(0), + is_filename_(false) +{} + +//Returns storage class +uint32_t coff_debug_info::coff_symbol::get_storage_class() const +{ + return storage_class_; +} + +//Returns symbol index +uint32_t coff_debug_info::coff_symbol::get_index() const +{ + return index_; +} + +//Returns section number +uint32_t coff_debug_info::coff_symbol::get_section_number() const +{ + return section_number_; +} + +//Returns RVA +uint32_t coff_debug_info::coff_symbol::get_rva() const +{ + return rva_; +} + +//Returns true if structure contains file name +bool coff_debug_info::coff_symbol::is_file() const +{ + return is_filename_; +} + +//Returns text data (symbol or file name) +const std::string& coff_debug_info::coff_symbol::get_symbol() const +{ + return name_; +} + +//Sets storage class +void coff_debug_info::coff_symbol::set_storage_class(uint32_t storage_class) +{ + storage_class_ = storage_class; +} + +//Sets symbol index +void coff_debug_info::coff_symbol::set_index(uint32_t index) +{ + index_ = index; +} + +//Sets section number +void coff_debug_info::coff_symbol::set_section_number(uint32_t section_number) +{ + section_number_ = section_number; +} + +//Sets RVA +void coff_debug_info::coff_symbol::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets file name +void coff_debug_info::coff_symbol::set_file_name(const std::string& file_name) +{ + name_ = file_name; + is_filename_ = true; +} + +//Sets symbol name +void coff_debug_info::coff_symbol::set_symbol_name(const std::string& symbol_name) +{ + name_ = symbol_name; + is_filename_ = false; +} + +//Returns type +uint16_t coff_debug_info::coff_symbol::get_type() const +{ + return type_; +} + +//Sets type +void coff_debug_info::coff_symbol::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe) +{ + debug_info_list ret; + + //If there's no debug directory, return empty list + if(!pe.has_debug()) + return ret; + + //Check the length in bytes of the section containing debug directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_rva(image_directory_entry_debug), section_data_virtual, true) + < sizeof(image_debug_directory)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_debug); + + //First IMAGE_DEBUG_DIRECTORY table + image_debug_directory directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + + if(!pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_debug), pe.get_directory_size(image_directory_entry_debug))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Iterate over all IMAGE_DEBUG_DIRECTORY directories + while(directory.PointerToRawData + && current_pos < pe.get_directory_rva(image_directory_entry_debug) + pe.get_directory_size(image_directory_entry_debug)) + { + //Create debug information structure + debug_info info(directory); + + //Find raw debug data + const pe_base::debug_data_list& debug_datas = pe.get_raw_debug_data_list(); + pe_base::debug_data_list::const_iterator it = debug_datas.find(directory.PointerToRawData); + if(it != debug_datas.end()) //If it exists, we'll do some detailed debug info research + { + const std::string& debug_data = (*it).second; + switch(directory.Type) + { + case image_debug_type_coff: + { + //Check data length + if(debug_data.length() < sizeof(image_coff_symbols_header)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get coff header structure pointer + const image_coff_symbols_header* coff = reinterpret_cast<const image_coff_symbols_header*>(debug_data.data()); + + //Check possible overflows + if(coff->NumberOfSymbols >= pe_utils::max_dword / sizeof(image_symbol) + || !pe_utils::is_sum_safe(coff->NumberOfSymbols * sizeof(image_symbol), coff->LvaToFirstSymbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check data length again + if(debug_data.length() < coff->NumberOfSymbols * sizeof(image_symbol) + coff->LvaToFirstSymbol) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Create COFF debug info structure + coff_debug_info coff_info(coff); + + //Enumerate debug symbols data + for(uint32_t i = 0; i < coff->NumberOfSymbols; ++i) + { + //Safe sum (checked above) + const image_symbol* sym = reinterpret_cast<const image_symbol*>(debug_data.data() + i * sizeof(image_symbol) + coff->LvaToFirstSymbol); + + coff_debug_info::coff_symbol symbol; + symbol.set_index(i); //Save symbol index + symbol.set_storage_class(sym->StorageClass); //Save storage class + symbol.set_type(sym->Type); //Save storage class + + //Check data length again + if(!pe_utils::is_sum_safe(i, sym->NumberOfAuxSymbols) + || (i + sym->NumberOfAuxSymbols) > coff->NumberOfSymbols + || debug_data.length() < (i + 1) * sizeof(image_symbol) + coff->LvaToFirstSymbol + sym->NumberOfAuxSymbols * sizeof(image_symbol)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //If symbol is filename + if(sym->StorageClass == image_sym_class_file) + { + //Save file name, it is situated just after this IMAGE_SYMBOL structure + std::string file_name(reinterpret_cast<const char*>(debug_data.data() + (i + 1) * sizeof(image_symbol)), sym->NumberOfAuxSymbols * sizeof(image_symbol)); + pe_utils::strip_nullbytes(file_name); + symbol.set_file_name(file_name); + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + + //Dump some other symbols + if(((sym->StorageClass == image_sym_class_static) + && (sym->NumberOfAuxSymbols == 0) + && (sym->SectionNumber == 1)) + || + ((sym->StorageClass == image_sym_class_external) + && ISFCN(sym->Type) + && (sym->SectionNumber > 0)) + ) + { + //Save RVA and section number + symbol.set_section_number(sym->SectionNumber); + symbol.set_rva(sym->Value); + + //If symbol has short name + if(sym->N.Name.Short) + { + //Copy and save symbol name + char name_buff[9]; + memcpy(name_buff, sym->N.ShortName, 8); + name_buff[8] = '\0'; + symbol.set_symbol_name(name_buff); + } + else + { + //Symbol has long name + + //Check possible overflows + if(!pe_utils::is_sum_safe(coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol), sym->N.Name.Long)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Here we have an offset to the string table + uint32_t symbol_offset = coff->LvaToFirstSymbol + coff->NumberOfSymbols * sizeof(image_symbol) + sym->N.Name.Long; + + //Check data length + if(debug_data.length() < symbol_offset) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Check symbol name for null-termination + if(!pe_utils::is_null_terminated(debug_data.data() + symbol_offset, debug_data.length() - symbol_offset)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save symbol name + symbol.set_symbol_name(debug_data.data() + symbol_offset); + } + + //Save symbol info + coff_info.add_symbol(symbol); + + //Move to next symbol + i += sym->NumberOfAuxSymbols; + continue; + } + } + + info.set_advanced_debug_info(coff_info); + } + break; + + case image_debug_type_codeview: + { + //Check data length + if(debug_data.length() < sizeof(OMFSignature*)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get POMFSignature structure pointer from the very beginning of debug data + const OMFSignature* sig = reinterpret_cast<const OMFSignature*>(debug_data.data()); + if(!memcmp(sig->Signature, "RSDS", 4)) + { + //Signature is "RSDS" - PDB 7.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB70)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB70* pdb_data = reinterpret_cast<const CV_INFO_PDB70*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB70) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_7_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB10", 4)) + { + //Signature is "NB10" - PDB 2.0 + + //Check data length + if(debug_data.length() < sizeof(CV_INFO_PDB20)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + const CV_INFO_PDB20* pdb_data = reinterpret_cast<const CV_INFO_PDB20*>(debug_data.data()); + + //Check PDB file name null-termination + if(!pe_utils::is_null_terminated(pdb_data->PdbFileName, debug_data.length() - (sizeof(CV_INFO_PDB20) - 1 /* BYTE of filename in structure */))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + info.set_advanced_debug_info(pdb_2_0_info(pdb_data)); + } + else if(!memcmp(sig->Signature, "NB09", 4)) + { + //CodeView 4.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_4_0); + } + else if(!memcmp(sig->Signature, "NB11", 4)) + { + //CodeView 5.0, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview_5_0); + } + else if(!memcmp(sig->Signature, "NB05", 4)) + { + //Other CodeView, no structures available + info.set_advanced_info_type(debug_info::advanced_info_codeview); + } + } + + break; + + case image_debug_type_misc: + { + //Check data length + if(debug_data.length() < sizeof(image_debug_misc)) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Get misc structure pointer + const image_debug_misc* misc_data = reinterpret_cast<const image_debug_misc*>(debug_data.data()); + + //Check misc data length + if(debug_data.length() < misc_data->Length /* Total length of record */) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Save advanced information + info.set_advanced_debug_info(misc_debug_info(misc_data)); + } + break; + } + } + + //Save debug information structure + ret.push_back(info); + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_pos, sizeof(image_debug_directory))) + throw pe_exception("Incorrect debug directory", pe_exception::incorrect_debug_directory); + + //Go to next debug entry + current_pos += sizeof(image_debug_directory); + directory = pe.section_data_from_rva<image_debug_directory>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_debug.h b/tools/pe_bliss/pe_debug.h new file mode 100644 index 0000000000..73a7e6860d --- /dev/null +++ b/tools/pe_bliss/pe_debug.h @@ -0,0 +1,324 @@ +/*************************************************************************/ +/* 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 <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing advanced RSDS (PDB 7.0) information +class pdb_7_0_info +{ +public: + //Default constructor + pdb_7_0_info(); + //Constructor from data + explicit pdb_7_0_info(const pe_win::CV_INFO_PDB70* info); + + //Returns debug PDB 7.0 structure GUID + const pe_win::guid get_guid() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + pe_win::guid guid_; + std::string pdb_file_name_; +}; + +//Class representing advanced NB10 (PDB 2.0) information +class pdb_2_0_info +{ +public: + //Default constructor + pdb_2_0_info(); + //Constructor from data + explicit pdb_2_0_info(const pe_win::CV_INFO_PDB20* info); + + //Returns debug PDB 2.0 structure signature + uint32_t get_signature() const; + //Returns age of build + uint32_t get_age() const; + //Returns PDB file name / path + const std::string& get_pdb_file_name() const; + +private: + uint32_t age_; + uint32_t signature_; + std::string pdb_file_name_; +}; + +//Class representing advanced misc (IMAGE_DEBUG_TYPE_MISC) info +class misc_debug_info +{ +public: + //Default constructor + misc_debug_info(); + //Constructor from data + explicit misc_debug_info(const pe_win::image_debug_misc* info); + + //Returns debug data type + uint32_t get_data_type() const; + //Returns true if data type is exe name + bool is_exe_name() const; + + //Returns true if debug data is UNICODE + bool is_unicode() const; + //Returns debug data (ANSI or UNICODE) + const std::string& get_data_ansi() const; + const std::wstring& get_data_unicode() const; + +private: + uint32_t data_type_; + bool unicode_; + std::string debug_data_ansi_; + std::wstring debug_data_unicode_; +}; + +//Class representing COFF (IMAGE_DEBUG_TYPE_COFF) debug info +class coff_debug_info +{ +public: + //Structure representing COFF symbol + struct coff_symbol + { + public: + //Default constructor + coff_symbol(); + + //Returns storage class + uint32_t get_storage_class() const; + //Returns symbol index + uint32_t get_index() const; + //Returns section number + uint32_t get_section_number() const; + //Returns RVA + uint32_t get_rva() const; + //Returns type + uint16_t get_type() const; + + //Returns true if structure contains file name + bool is_file() const; + //Returns text data (symbol or file name) + const std::string& get_symbol() const; + + public: //These functions do not change everything inside image, they are used by PE class + //Sets storage class + void set_storage_class(uint32_t storage_class); + //Sets symbol index + void set_index(uint32_t index); + //Sets section number + void set_section_number(uint32_t section_number); + //Sets RVA + void set_rva(uint32_t rva); + //Sets type + void set_type(uint16_t type); + + //Sets file name + void set_file_name(const std::string& file_name); + //Sets symbol name + void set_symbol_name(const std::string& symbol_name); + + private: + uint32_t storage_class_; + uint32_t index_; + uint32_t section_number_, rva_; + uint16_t type_; + bool is_filename_; + std::string name_; + }; + +public: + typedef std::vector<coff_symbol> coff_symbols_list; + +public: + //Default constructor + coff_debug_info(); + //Constructor from data + explicit coff_debug_info(const pe_win::image_coff_symbols_header* info); + + //Returns number of symbols + uint32_t get_number_of_symbols() const; + //Returns virtual address of the first symbol + uint32_t get_lva_to_first_symbol() const; + //Returns number of line-number entries + uint32_t get_number_of_line_numbers() const; + //Returns virtual address of the first line-number entry + uint32_t get_lva_to_first_line_number() const; + //Returns relative virtual address of the first byte of code + uint32_t get_rva_to_first_byte_of_code() const; + //Returns relative virtual address of the last byte of code + uint32_t get_rva_to_last_byte_of_code() const; + //Returns relative virtual address of the first byte of data + uint32_t get_rva_to_first_byte_of_data() const; + //Returns relative virtual address of the last byte of data + uint32_t get_rva_to_last_byte_of_data() const; + + //Returns COFF symbols list + const coff_symbols_list& get_symbols() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Adds COFF symbol + void add_symbol(const coff_symbol& sym); + +private: + uint32_t number_of_symbols_; + uint32_t lva_to_first_symbol_; + uint32_t number_of_line_numbers_; + uint32_t lva_to_first_line_number_; + uint32_t rva_to_first_byte_of_code_; + uint32_t rva_to_last_byte_of_code_; + uint32_t rva_to_first_byte_of_data_; + uint32_t rva_to_last_byte_of_data_; + +private: + coff_symbols_list symbols_; +}; + +//Class representing debug information +class debug_info +{ +public: + //Enumeration of debug information types + enum debug_info_type + { + debug_type_unknown, + debug_type_coff, + debug_type_codeview, + debug_type_fpo, + debug_type_misc, + debug_type_exception, + debug_type_fixup, + debug_type_omap_to_src, + debug_type_omap_from_src, + debug_type_borland, + debug_type_reserved10, + debug_type_clsid + }; + +public: + //Enumeration of advanced debug information types + enum advanced_info_type + { + advanced_info_none, //No advanced info + advanced_info_pdb_7_0, //PDB 7.0 + advanced_info_pdb_2_0, //PDB 2.0 + advanced_info_misc, //MISC debug info + advanced_info_coff, //COFF debug info + //No advanced info structures available for types below + advanced_info_codeview_4_0, //CodeView 4.0 + advanced_info_codeview_5_0, //CodeView 5.0 + advanced_info_codeview //CodeView + }; + +public: + //Default constructor + debug_info(); + //Constructor from data + explicit debug_info(const pe_win::image_debug_directory& debug); + //Copy constructor + debug_info(const debug_info& info); + //Copy assignment operator + debug_info& operator=(const debug_info& info); + //Destructor + ~debug_info(); + + //Returns debug characteristics + uint32_t get_characteristics() const; + //Returns debug datetimestamp + uint32_t get_time_stamp() const; + //Returns major version + uint32_t get_major_version() const; + //Returns minor version + uint32_t get_minor_version() const; + //Returns type of debug info (unchecked) + uint32_t get_type_raw() const; + //Returns type of debug info from debug_info_type enumeration + debug_info_type get_type() const; + //Returns size of debug data (internal, .pdb or other file doesn't count) + uint32_t get_size_of_data() const; + //Returns RVA of debug info when mapped to memory or zero, if info is not mapped + uint32_t get_rva_of_raw_data() const; + //Returns raw file pointer to raw data + uint32_t get_pointer_to_raw_data() const; + + //Returns advanced debug information type + advanced_info_type get_advanced_info_type() const; + //Returns advanced debug information or throws an exception, + //if requested information type is not contained by structure + template<typename AdvancedInfo> + const AdvancedInfo get_advanced_debug_info() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Sets advanced debug information + void set_advanced_debug_info(const pdb_7_0_info& info); + void set_advanced_debug_info(const pdb_2_0_info& info); + void set_advanced_debug_info(const misc_debug_info& info); + void set_advanced_debug_info(const coff_debug_info& info); + + //Sets advanced debug information type, if no advanced info structure available + void set_advanced_info_type(advanced_info_type type); + +private: + uint32_t characteristics_; + uint32_t time_stamp_; + uint32_t major_version_, minor_version_; + uint32_t type_; + uint32_t size_of_data_; + uint32_t address_of_raw_data_; //RVA when mapped or 0 + uint32_t pointer_to_raw_data_; //RAW file offset + + //Union containing advanced debug information pointer + union advanced_info + { + public: + //Default constructor + advanced_info(); + + //Returns true if advanced debug info is present + bool is_present() const; + + public: + pdb_7_0_info* adv_pdb_7_0_info; + pdb_2_0_info* adv_pdb_2_0_info; + misc_debug_info* adv_misc_info; + coff_debug_info* adv_coff_info; + }; + + //Helper for advanced debug information copying + void copy_advanced_info(const debug_info& info); + //Helper for clearing any present advanced debug information + void free_present_advanced_info(); + + advanced_info advanced_debug_info_; + //Advanced information type + advanced_info_type advanced_info_type_; +}; + +typedef std::vector<debug_info> debug_info_list; + +//Returns debug information list +const debug_info_list get_debug_information(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_directory.cpp b/tools/pe_bliss/pe_directory.cpp new file mode 100644 index 0000000000..13ad2afc5d --- /dev/null +++ b/tools/pe_bliss/pe_directory.cpp @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* 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 "pe_directory.h" + +namespace pe_bliss +{ +//Default constructor +image_directory::image_directory() + :rva_(0), size_(0) +{} + +//Constructor from data +image_directory::image_directory(uint32_t rva, uint32_t size) + :rva_(rva), size_(size) +{} + +//Returns RVA +uint32_t image_directory::get_rva() const +{ + return rva_; +} + +//Returns size +uint32_t image_directory::get_size() const +{ + return size_; +} + +//Sets RVA +void image_directory::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets size +void image_directory::set_size(uint32_t size) +{ + size_ = size; +} +} diff --git a/tools/pe_bliss/pe_directory.h b/tools/pe_bliss/pe_directory.h new file mode 100644 index 0000000000..a7b1ea7a5f --- /dev/null +++ b/tools/pe_bliss/pe_directory.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* 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 "stdint_defs.h" + +namespace pe_bliss +{ +//Class representing image directory data +class image_directory +{ +public: + //Default constructor + image_directory(); + //Constructor from data + image_directory(uint32_t rva, uint32_t size); + + //Returns RVA + uint32_t get_rva() const; + //Returns size + uint32_t get_size() const; + + //Sets RVA + void set_rva(uint32_t rva); + //Sets size + void set_size(uint32_t size); + +private: + uint32_t rva_; + uint32_t size_; +}; +} diff --git a/tools/pe_bliss/pe_dotnet.cpp b/tools/pe_bliss/pe_dotnet.cpp new file mode 100644 index 0000000000..f34a76eae8 --- /dev/null +++ b/tools/pe_bliss/pe_dotnet.cpp @@ -0,0 +1,186 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "pe_dotnet.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//.NET +basic_dotnet_info::basic_dotnet_info() +{ + memset(&header_, 0, sizeof(header_)); +} + +//Constructor from data +basic_dotnet_info::basic_dotnet_info(const image_cor20_header& header) + :header_(header) +{} + +//Returns major runtime version +uint16_t basic_dotnet_info::get_major_runtime_version() const +{ + return header_.MajorRuntimeVersion; +} + +//Returns minor runtime version +uint16_t basic_dotnet_info::get_minor_runtime_version() const +{ + return header_.MinorRuntimeVersion; +} + +//Returns RVA of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_rva_of_metadata() const +{ + return header_.MetaData.VirtualAddress; +} + +//Returns size of metadata (symbol table and startup information) +uint32_t basic_dotnet_info::get_size_of_metadata() const +{ + return header_.MetaData.Size; +} + +//Returns flags +uint32_t basic_dotnet_info::get_flags() const +{ + return header_.Flags; +} + +//Returns true if entry point is native +bool basic_dotnet_info::is_native_entry_point() const +{ + return (header_.Flags & comimage_flags_native_entrypoint) ? true : false; +} + +//Returns true if 32 bit required +bool basic_dotnet_info::is_32bit_required() const +{ + return (header_.Flags & comimage_flags_32bitrequired) ? true : false; +} + +//Returns true if image is IL library +bool basic_dotnet_info::is_il_library() const +{ + return (header_.Flags & comimage_flags_il_library) ? true : false; +} + +//Returns true if image uses IL only +bool basic_dotnet_info::is_il_only() const +{ + return (header_.Flags & comimage_flags_ilonly) ? true : false; +} + +//Returns entry point RVA (if entry point is native) +//Returns entry point managed token (if entry point is managed) +uint32_t basic_dotnet_info::get_entry_point_rva_or_token() const +{ + return header_.EntryPointToken; +} + +//Returns RVA of managed resources +uint32_t basic_dotnet_info::get_rva_of_resources() const +{ + return header_.Resources.VirtualAddress; +} + +//Returns size of managed resources +uint32_t basic_dotnet_info::get_size_of_resources() const +{ + return header_.Resources.Size; +} + +//Returns RVA of strong name signature +uint32_t basic_dotnet_info::get_rva_of_strong_name_signature() const +{ + return header_.StrongNameSignature.VirtualAddress; +} + +//Returns size of strong name signature +uint32_t basic_dotnet_info::get_size_of_strong_name_signature() const +{ + return header_.StrongNameSignature.Size; +} + +//Returns RVA of code manager table +uint32_t basic_dotnet_info::get_rva_of_code_manager_table() const +{ + return header_.CodeManagerTable.VirtualAddress; +} + +//Returns size of code manager table +uint32_t basic_dotnet_info::get_size_of_code_manager_table() const +{ + return header_.CodeManagerTable.Size; +} + +//Returns RVA of VTable fixups +uint32_t basic_dotnet_info::get_rva_of_vtable_fixups() const +{ + return header_.VTableFixups.VirtualAddress; +} + +//Returns size of VTable fixups +uint32_t basic_dotnet_info::get_size_of_vtable_fixups() const +{ + return header_.VTableFixups.Size; +} + +//Returns RVA of export address table jumps +uint32_t basic_dotnet_info::get_rva_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.VirtualAddress; +} + +//Returns size of export address table jumps +uint32_t basic_dotnet_info::get_size_of_export_address_table_jumps() const +{ + return header_.ExportAddressTableJumps.Size; +} + +//Returns RVA of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_rva_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.VirtualAddress; +} + +//Returns size of managed native header +//(precompiled header info, usually set to zero, for internal use) +uint32_t basic_dotnet_info::get_size_of_managed_native_header() const +{ + return header_.ManagedNativeHeader.Size; +} + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe) +{ + //If there's no debug directory, return empty list + if(!pe.is_dotnet()) + throw pe_exception("Image does not have managed code", pe_exception::image_does_not_have_managed_code); + + //Return basic .NET information + return basic_dotnet_info(pe.section_data_from_rva<image_cor20_header>(pe.get_directory_rva(image_directory_entry_com_descriptor), section_data_virtual, true)); +} +} diff --git a/tools/pe_bliss/pe_dotnet.h b/tools/pe_bliss/pe_dotnet.h new file mode 100644 index 0000000000..96b0ac7d0a --- /dev/null +++ b/tools/pe_bliss/pe_dotnet.h @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* 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 "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing basic .NET header information +class basic_dotnet_info +{ +public: + //Default constructor + basic_dotnet_info(); + //Constructor from data + explicit basic_dotnet_info(const pe_win::image_cor20_header& header); + + //Returns major runtime version + uint16_t get_major_runtime_version() const; + //Returns minor runtime version + uint16_t get_minor_runtime_version() const; + + //Returns RVA of metadata (symbol table and startup information) + uint32_t get_rva_of_metadata() const; + //Returns size of metadata (symbol table and startup information) + uint32_t get_size_of_metadata() const; + + //Returns flags + uint32_t get_flags() const; + + //Returns true if entry point is native + bool is_native_entry_point() const; + //Returns true if 32 bit required + bool is_32bit_required() const; + //Returns true if image is IL library + bool is_il_library() const; + //Returns true if image uses IL only + bool is_il_only() const; + + //Returns entry point RVA (if entry point is native) + //Returns entry point managed token (if entry point is managed) + uint32_t get_entry_point_rva_or_token() const; + + //Returns RVA of managed resources + uint32_t get_rva_of_resources() const; + //Returns size of managed resources + uint32_t get_size_of_resources() const; + //Returns RVA of strong name signature + uint32_t get_rva_of_strong_name_signature() const; + //Returns size of strong name signature + uint32_t get_size_of_strong_name_signature() const; + //Returns RVA of code manager table + uint32_t get_rva_of_code_manager_table() const; + //Returns size of code manager table + uint32_t get_size_of_code_manager_table() const; + //Returns RVA of VTable fixups + uint32_t get_rva_of_vtable_fixups() const; + //Returns size of VTable fixups + uint32_t get_size_of_vtable_fixups() const; + //Returns RVA of export address table jumps + uint32_t get_rva_of_export_address_table_jumps() const; + //Returns size of export address table jumps + uint32_t get_size_of_export_address_table_jumps() const; + //Returns RVA of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_rva_of_managed_native_header() const; + //Returns size of managed native header + //(precompiled header info, usually set to zero, for internal use) + uint32_t get_size_of_managed_native_header() const; + +private: + pe_win::image_cor20_header header_; +}; + +//Returns basic .NET information +//If image is not native, throws an exception +const basic_dotnet_info get_basic_dotnet_info(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_exception.cpp b/tools/pe_bliss/pe_exception.cpp new file mode 100644 index 0000000000..3161f93599 --- /dev/null +++ b/tools/pe_bliss/pe_exception.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* 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 "pe_exception.h" + +namespace pe_bliss +{ +//PE exception class constructors +pe_exception::pe_exception(const char* text, exception_id id) + :std::runtime_error(text), id_(id) +{} + +pe_exception::pe_exception(const std::string& text, exception_id id) + :std::runtime_error(text), id_(id) +{} + +//Returns exception ID +pe_exception::exception_id pe_exception::get_id() const +{ + return id_; +} +} diff --git a/tools/pe_bliss/pe_exception.h b/tools/pe_bliss/pe_exception.h new file mode 100644 index 0000000000..2b58a95772 --- /dev/null +++ b/tools/pe_bliss/pe_exception.h @@ -0,0 +1,130 @@ +/*************************************************************************/ +/* 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 <exception> +#include <stdexcept> + +namespace pe_bliss +{ +//PE exception class +class pe_exception : public std::runtime_error +{ +public: + //Exception IDs + enum exception_id + { + unknown_error, + bad_pe_file, + bad_dos_header, + image_nt_headers_not_found, + error_reading_image_nt_headers, + error_reading_data_directories, + error_reading_file, + pe_signature_incorrect, + incorrect_number_of_rva_and_sizes, + error_changing_section_virtual_size, + section_number_incorrect, + section_table_incorrect, + incorrect_section_alignment, + incorrect_file_alignment, + incorrect_size_of_image, + incorrect_size_of_headers, + image_section_headers_not_found, + zero_section_sizes, + section_incorrect_addr_or_size, + section_not_found, + image_section_data_not_found, + no_section_found, + image_section_table_incorrect, + directory_does_not_exist, + rva_not_exists, + error_reading_section_header, + error_reading_overlay, + incorrect_address_conversion, + + incorrect_export_directory, + incorrect_import_directory, + incorrect_relocation_directory, + incorrect_tls_directory, + incorrect_config_directory, + incorrect_bound_import_directory, + incorrect_resource_directory, + incorrect_exception_directory, + incorrect_debug_directory, + + resource_directory_entry_error, + resource_directory_entry_not_found, + resource_data_entry_not_found, + resource_incorrect_bitmap, + resource_incorrect_icon, + resource_incorrect_cursor, + resource_incorrect_string_table, + resource_string_not_found, + resource_incorrect_message_table, + resource_incorrect_version_info, + + advanced_debug_information_request_error, + image_does_not_have_managed_code, + + section_is_empty, + data_is_empty, + stream_is_bad, + + section_is_not_attached, + insufficient_space, + + cannot_rebase_relocations, + + exports_list_is_empty, + duplicate_exported_function_ordinal, + duplicate_exported_function_name, + + version_info_string_does_not_exist, + + no_more_sections_can_be_added, + + no_icon_group_found, + no_cursor_group_found, + + encoding_convertion_error, + + error_expanding_section, + + cannot_rebuild_image + }; + +public: + //Class constructors + explicit pe_exception(const char* text, exception_id id = unknown_error); + explicit pe_exception(const std::string& text, exception_id id = unknown_error); + + //Returns exception ID from exception_id enumeration + exception_id get_id() const; + + //Destructor + virtual ~pe_exception() throw() + {} + +private: + exception_id id_; +}; +} diff --git a/tools/pe_bliss/pe_exception_directory.cpp b/tools/pe_bliss/pe_exception_directory.cpp new file mode 100644 index 0000000000..1813f02021 --- /dev/null +++ b/tools/pe_bliss/pe_exception_directory.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* 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 "pe_exception_directory.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXCEPTION DIRECTORY (exists on PE+ only) +//Default constructor +exception_entry::exception_entry() + :begin_address_(0), end_address_(0), unwind_info_address_(0), + unwind_info_version_(0), + flags_(0), + size_of_prolog_(0), + count_of_codes_(0), + frame_register_(0), + frame_offset_(0) +{} + +//Constructor from data +exception_entry::exception_entry(const image_runtime_function_entry& entry, const unwind_info& unwind_info) + :begin_address_(entry.BeginAddress), end_address_(entry.EndAddress), unwind_info_address_(entry.UnwindInfoAddress), + unwind_info_version_(unwind_info.Version), + flags_(unwind_info.Flags), + size_of_prolog_(unwind_info.SizeOfProlog), + count_of_codes_(unwind_info.CountOfCodes), + frame_register_(unwind_info.FrameRegister), + frame_offset_(unwind_info.FrameOffset) +{} + +//Returns starting address of function, affected by exception unwinding +uint32_t exception_entry::get_begin_address() const +{ + return begin_address_; +} + +//Returns ending address of function, affected by exception unwinding +uint32_t exception_entry::get_end_address() const +{ + return end_address_; +} + +//Returns unwind info address +uint32_t exception_entry::get_unwind_info_address() const +{ + return unwind_info_address_; +} + +//Returns UNWIND_INFO structure version +uint8_t exception_entry::get_unwind_info_version() const +{ + return unwind_info_version_; +} + +//Returns unwind info flags +uint8_t exception_entry::get_flags() const +{ + return flags_; +} + +//The function has an exception handler that should be called +//when looking for functions that need to examine exceptions +bool exception_entry::has_exception_handler() const +{ + return (flags_ & unw_flag_ehandler) ? true : false; +} + +//The function has a termination handler that should be called +//when unwinding an exception +bool exception_entry::has_termination_handler() const +{ + return (flags_ & unw_flag_uhandler) ? true : false; +} + +//The unwind info structure is not the primary one for the procedure +bool exception_entry::is_chaininfo() const +{ + return (flags_ & unw_flag_chaininfo) ? true : false; +} + +//Returns size of function prolog +uint8_t exception_entry::get_size_of_prolog() const +{ + return size_of_prolog_; +} + +//Returns number of unwind slots +uint8_t exception_entry::get_number_of_unwind_slots() const +{ + return count_of_codes_; +} + +//If the function uses frame pointer +bool exception_entry::uses_frame_pointer() const +{ + return frame_register_ != 0; +} + +//Number of the nonvolatile register used as the frame pointer, +//using the same encoding for the operation info field of UNWIND_CODE nodes +uint8_t exception_entry::get_frame_pointer_register_number() const +{ + return frame_register_; +} + +//The scaled offset from RSP that is applied to the FP reg when it is established. +//The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 +uint8_t exception_entry::get_scaled_rsp_offset() const +{ + return frame_offset_; +} + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe) +{ + exception_entry_list ret; + + //If image doesn't have exception directory, return empty list + if(!pe.has_exception_directory()) + return ret; + + //Check the length in bytes of the section containing exception directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_exception), pe.get_directory_rva(image_directory_entry_exception), section_data_virtual, true) + < sizeof(image_runtime_function_entry)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_exception); + + //Check if structures are DWORD-aligned + if(current_pos % sizeof(uint32_t)) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //First IMAGE_RUNTIME_FUNCTION_ENTRY table + image_runtime_function_entry exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + + //todo: virtual addresses BeginAddress and EndAddress are not checked to be inside image + while(exception_table.BeginAddress) + { + //Check addresses + if(exception_table.BeginAddress > exception_table.EndAddress) + throw pe_exception("Incorrect exception directory", pe_exception::incorrect_exception_directory); + + //Get unwind information + unwind_info info = pe.section_data_from_rva<unwind_info>(exception_table.UnwindInfoAddress, section_data_virtual, true); + + //Create exception entry and save it + ret.push_back(exception_entry(exception_table, info)); + + //Go to next exception entry + current_pos += sizeof(image_runtime_function_entry); + exception_table = pe.section_data_from_rva<image_runtime_function_entry>(current_pos, section_data_virtual, true); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_exception_directory.h b/tools/pe_bliss/pe_exception_directory.h new file mode 100644 index 0000000000..6f4fc2298b --- /dev/null +++ b/tools/pe_bliss/pe_exception_directory.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* 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 <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing exception directory entry +class exception_entry +{ +public: + //Default constructor + exception_entry(); + //Constructor from data + exception_entry(const pe_win::image_runtime_function_entry& entry, const pe_win::unwind_info& unwind_info); + + //Returns starting address of function, affected by exception unwinding + uint32_t get_begin_address() const; + //Returns ending address of function, affected by exception unwinding + uint32_t get_end_address() const; + //Returns unwind info address + uint32_t get_unwind_info_address() const; + + //Returns UNWIND_INFO structure version + uint8_t get_unwind_info_version() const; + + //Returns unwind info flags + uint8_t get_flags() const; + //The function has an exception handler that should be called + //when looking for functions that need to examine exceptions + bool has_exception_handler() const; + //The function has a termination handler that should be called + //when unwinding an exception + bool has_termination_handler() const; + //The unwind info structure is not the primary one for the procedure + bool is_chaininfo() const; + + //Returns size of function prolog + uint8_t get_size_of_prolog() const; + + //Returns number of unwind slots + uint8_t get_number_of_unwind_slots() const; + + //If the function uses frame pointer + bool uses_frame_pointer() const; + //Number of the nonvolatile register used as the frame pointer, + //using the same encoding for the operation info field of UNWIND_CODE nodes + uint8_t get_frame_pointer_register_number() const; + //The scaled offset from RSP that is applied to the FP reg when it is established. + //The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240 + uint8_t get_scaled_rsp_offset() const; + +private: + uint32_t begin_address_, end_address_, unwind_info_address_; + uint8_t unwind_info_version_; + uint8_t flags_; + uint8_t size_of_prolog_; + uint8_t count_of_codes_; + uint8_t frame_register_, frame_offset_; +}; + +typedef std::vector<exception_entry> exception_entry_list; + +//Returns exception directory data (exists on PE+ only) +//Unwind opcodes are not listed, because their format and list are subject to change +const exception_entry_list get_exception_directory_data(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_exports.cpp b/tools/pe_bliss/pe_exports.cpp new file mode 100644 index 0000000000..c2ad895554 --- /dev/null +++ b/tools/pe_bliss/pe_exports.cpp @@ -0,0 +1,700 @@ +/*************************************************************************/ +/* 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 <set> +#include <algorithm> +#include <string.h> +#include "pe_exports.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//EXPORTS +//Default constructor +exported_function::exported_function() + :ordinal_(0), rva_(0), has_name_(false), name_ordinal_(0), forward_(false) +{} + +//Returns ordinal of function (actually, ordinal = hint + ordinal base) +uint16_t exported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns RVA of function +uint32_t exported_function::get_rva() const +{ + return rva_; +} + +//Returns name of function +const std::string& exported_function::get_name() const +{ + return name_; +} + +//Returns true if function has name and name ordinal +bool exported_function::has_name() const +{ + return has_name_; +} + +//Returns name ordinal of function +uint16_t exported_function::get_name_ordinal() const +{ + return name_ordinal_; +} + +//Returns true if function is forwarded to other library +bool exported_function::is_forwarded() const +{ + return forward_; +} + +//Returns the name of forwarded function +const std::string& exported_function::get_forwarded_name() const +{ + return forward_name_; +} + +//Sets ordinal of function +void exported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets RVA of function +void exported_function::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Sets name of function (or clears it, if empty name is passed) +void exported_function::set_name(const std::string& name) +{ + name_ = name; + has_name_ = !name.empty(); +} + +//Sets name ordinal +void exported_function::set_name_ordinal(uint16_t name_ordinal) +{ + name_ordinal_ = name_ordinal; +} + +//Sets forwarded function name (or clears it, if empty name is passed) +void exported_function::set_forwarded_name(const std::string& name) +{ + forward_name_ = name; + forward_ = !name.empty(); +} + +//Default constructor +export_info::export_info() + :characteristics_(0), + timestamp_(0), + major_version_(0), + minor_version_(0), + ordinal_base_(0), + number_of_functions_(0), + number_of_names_(0), + address_of_functions_(0), + address_of_names_(0), + address_of_name_ordinals_(0) +{} + +//Returns characteristics +uint32_t export_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns timestamp +uint32_t export_info::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version +uint16_t export_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version +uint16_t export_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns DLL name +const std::string& export_info::get_name() const +{ + return name_; +} + +//Returns ordinal base +uint32_t export_info::get_ordinal_base() const +{ + return ordinal_base_; +} + +//Returns number of functions +uint32_t export_info::get_number_of_functions() const +{ + return number_of_functions_; +} + +//Returns number of function names +uint32_t export_info::get_number_of_names() const +{ + return number_of_names_; +} + +//Returns RVA of function address table +uint32_t export_info::get_rva_of_functions() const +{ + return address_of_functions_; +} + +//Returns RVA of function name address table +uint32_t export_info::get_rva_of_names() const +{ + return address_of_names_; +} + +//Returns RVA of name ordinals table +uint32_t export_info::get_rva_of_name_ordinals() const +{ + return address_of_name_ordinals_; +} + +//Sets characteristics +void export_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets timestamp +void export_info::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets major version +void export_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version +void export_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets DLL name +void export_info::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets ordinal base +void export_info::set_ordinal_base(uint32_t ordinal_base) +{ + ordinal_base_ = ordinal_base; +} + +//Sets number of functions +void export_info::set_number_of_functions(uint32_t number_of_functions) +{ + number_of_functions_ = number_of_functions; +} + +//Sets number of function names +void export_info::set_number_of_names(uint32_t number_of_names) +{ + number_of_names_ = number_of_names; +} + +//Sets RVA of function address table +void export_info::set_rva_of_functions(uint32_t rva_of_functions) +{ + address_of_functions_ = rva_of_functions; +} + +//Sets RVA of function name address table +void export_info::set_rva_of_names(uint32_t rva_of_names) +{ + address_of_names_ = rva_of_names; +} + +//Sets RVA of name ordinals table +void export_info::set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals) +{ + address_of_name_ordinals_ = rva_of_name_ordinals; +} + +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info); + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe) +{ + return get_exported_functions(pe, 0); +} + +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info) +{ + return get_exported_functions(pe, &info); +} + +//Helper: sorts exported function list by ordinals +struct ordinal_sorter +{ +public: + bool operator()(const exported_function& func1, const exported_function& func2) const; +}; + +//Returns array of exported functions and information about export (if info != 0) +const exported_functions_list get_exported_functions(const pe_base& pe, export_info* info) +{ + //Returned exported functions info array + std::vector<exported_function> ret; + + if(pe.has_exports()) + { + //Check the length in bytes of the section containing export directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_export), + pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true) + < sizeof(image_export_directory)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + image_export_directory exports = pe.section_data_from_rva<image_export_directory>(pe.get_directory_rva(image_directory_entry_export), section_data_virtual, true); + + unsigned long max_name_length; + + if(info) + { + //Save some export info data + info->set_characteristics(exports.Characteristics); + info->set_major_version(exports.MajorVersion); + info->set_minor_version(exports.MinorVersion); + + //Get byte count that we have for dll name + if((max_name_length = pe.section_data_length_from_rva(exports.Name, exports.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get dll name pointer + const char* dll_name = pe.section_data_from_rva(exports.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save the rest of export information data + info->set_name(dll_name); + info->set_number_of_functions(exports.NumberOfFunctions); + info->set_number_of_names(exports.NumberOfNames); + info->set_ordinal_base(exports.Base); + info->set_rva_of_functions(exports.AddressOfFunctions); + info->set_rva_of_names(exports.AddressOfNames); + info->set_rva_of_name_ordinals(exports.AddressOfNameOrdinals); + info->set_timestamp(exports.TimeDateStamp); + } + + if(!exports.NumberOfFunctions) + return ret; + + //Check IMAGE_EXPORT_DIRECTORY fields + if(exports.NumberOfNames > exports.NumberOfFunctions) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check some export directory fields + if((!exports.AddressOfNameOrdinals && exports.AddressOfNames) || + (exports.AddressOfNameOrdinals && !exports.AddressOfNames) || + !exports.AddressOfFunctions + || exports.NumberOfFunctions >= pe_utils::max_dword / sizeof(uint32_t) + || exports.NumberOfNames > pe_utils::max_dword / sizeof(uint32_t) + || !pe_utils::is_sum_safe(exports.AddressOfFunctions, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNames, exports.NumberOfNames * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(exports.AddressOfNameOrdinals, exports.NumberOfFunctions * sizeof(uint32_t)) + || !pe_utils::is_sum_safe(pe.get_directory_rva(image_directory_entry_export), pe.get_directory_size(image_directory_entry_export))) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Check if it is enough bytes to hold AddressOfFunctions table + if(pe.section_data_length_from_rva(exports.AddressOfFunctions, exports.AddressOfFunctions, section_data_virtual, true) + < exports.NumberOfFunctions * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(exports.AddressOfNames) + { + //Check if it is enough bytes to hold name and ordinal tables + if(pe.section_data_length_from_rva(exports.AddressOfNameOrdinals, exports.AddressOfNameOrdinals, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint16_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + if(pe.section_data_length_from_rva(exports.AddressOfNames, exports.AddressOfNames, section_data_virtual, true) + < exports.NumberOfNames * sizeof(uint32_t)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + } + + for(uint32_t ordinal = 0; ordinal < exports.NumberOfFunctions; ordinal++) + { + //Get function address + //Sum and multiplication are safe (checked above) + uint32_t rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfFunctions + ordinal * sizeof(uint32_t), section_data_virtual, true); + + //If we have a skip + if(!rva) + continue; + + exported_function func; + func.set_rva(rva); + + if(!pe_utils::is_sum_safe(exports.Base, ordinal) || exports.Base + ordinal > pe_utils::max_word) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + func.set_ordinal(static_cast<uint16_t>(ordinal + exports.Base)); + + //Scan for function name ordinal + for(uint32_t i = 0; i < exports.NumberOfNames; i++) + { + uint16_t ordinal2 = pe.section_data_from_rva<uint16_t>(exports.AddressOfNameOrdinals + i * sizeof(uint16_t), section_data_virtual, true); + + //If function has name (and name ordinal) + if(ordinal == ordinal2) + { + //Get function name + //Sum and multiplication are safe (checked above) + uint32_t function_name_rva = pe.section_data_from_rva<uint32_t>(exports.AddressOfNames + i * sizeof(uint32_t), section_data_virtual, true); + + //Get byte count that we have for function name + if((max_name_length = pe.section_data_length_from_rva(function_name_rva, function_name_rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get function name pointer + const char* func_name = pe.section_data_from_rva(function_name_rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Save function info + func.set_name(func_name); + func.set_name_ordinal(ordinal2); + + //If the function is just a redirect, save its name + if(rva >= pe.get_directory_rva(image_directory_entry_export) + sizeof(image_directory_entry_export) && + rva < pe.get_directory_rva(image_directory_entry_export) + pe.get_directory_size(image_directory_entry_export)) + { + if((max_name_length = pe.section_data_length_from_rva(rva, rva, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Get forwarded function name pointer + const char* forwarded_func_name = pe.section_data_from_rva(rva, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(forwarded_func_name, max_name_length)) + throw pe_exception("Incorrect export directory", pe_exception::incorrect_export_directory); + + //Set the name of forwarded function + func.set_forwarded_name(forwarded_func_name); + } + + break; + } + } + + //Add function info to output array + ret.push_back(func); + } + } + + return ret; +} + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports) +{ + if(exports.empty()) + return std::make_pair(0, 0); + + uint16_t max_ordinal = 0; //Maximum ordinal number + uint16_t ordinal_base = pe_utils::max_word; //Minimum ordinal value + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint16_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint16_t>(ordinal_base, func.get_ordinal()); + } + + return std::make_pair(ordinal_base, max_ordinal); +} + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).has_name() && (*it).get_name() == function_name) + return true; + } + + return false; +} + +//Checks if exported function name already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports) +{ + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + if((*it).get_ordinal() == ordinal) + return true; + } + + return false; +} + +//Helper: sorts exported function list by ordinals +bool ordinal_sorter::operator()(const exported_function& func1, const exported_function& func2) const +{ + return func1.get_ordinal() < func2.get_ordinal(); +} + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that exports_section is attached to this PE image + if(!pe.section_attached(exports_section)) + throw pe_exception("Exports section must be attached to PE file", pe_exception::section_is_not_attached); + + //Needed space for strings + uint32_t needed_size_for_strings = static_cast<uint32_t>(info.get_name().length() + 1); + uint32_t number_of_names = 0; //Number of named functions + uint32_t max_ordinal = 0; //Maximum ordinal number + uint32_t ordinal_base = static_cast<uint32_t>(-1); //Minimum ordinal value + + if(exports.empty()) + ordinal_base = info.get_ordinal_base(); + + uint32_t needed_size_for_function_names = 0; //Needed space for function name strings + uint32_t needed_size_for_function_forwards = 0; //Needed space for function forwards names + + //List all exported functions + //Calculate needed size for function list + { + //Also check that there're no duplicate names and ordinals + std::set<std::string> used_function_names; + std::set<uint16_t> used_function_ordinals; + + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + //Calculate maximum and minimum ordinal numbers + max_ordinal = std::max<uint32_t>(max_ordinal, func.get_ordinal()); + ordinal_base = std::min<uint32_t>(ordinal_base, func.get_ordinal()); + + //Check if ordinal is unique + if(!used_function_ordinals.insert(func.get_ordinal()).second) + throw pe_exception("Duplicate exported function ordinal", pe_exception::duplicate_exported_function_ordinal); + + if(func.has_name()) + { + //If function is named + ++number_of_names; + needed_size_for_function_names += static_cast<uint32_t>(func.get_name().length() + 1); + + //Check if it's name and name ordinal are unique + if(!used_function_names.insert(func.get_name()).second) + throw pe_exception("Duplicate exported function name", pe_exception::duplicate_exported_function_name); + } + + //If function is forwarded to another DLL + if(func.is_forwarded()) + needed_size_for_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + } + + //Sort functions by ordinal value + std::sort(exports.begin(), exports.end(), ordinal_sorter()); + + //Calculate needed space for different things... + needed_size_for_strings += needed_size_for_function_names; + needed_size_for_strings += needed_size_for_function_forwards; + uint32_t needed_size_for_function_name_ordinals = number_of_names * sizeof(uint16_t); + uint32_t needed_size_for_function_name_rvas = number_of_names * sizeof(uint32_t); + uint32_t needed_size_for_function_addresses = (max_ordinal - ordinal_base + 1) * sizeof(uint32_t); + + //Export directory header will be placed first + uint32_t directory_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = sizeof(image_export_directory); //Calculate needed size for export tables and strings + //sizeof(IMAGE_EXPORT_DIRECTORY) = export directory header + + //Total needed space... + needed_size += needed_size_for_function_name_ordinals; //For list of names ordinals + needed_size += needed_size_for_function_addresses; //For function RVAs + needed_size += needed_size_for_strings; //For all strings + needed_size += needed_size_for_function_name_rvas; //For function name strings RVAs + + //Check if exports_section is last one. If it's not, check if there's enough place for exports data + if(&exports_section != &*(pe.get_image_sections().end() - 1) && + (exports_section.empty() || pe_utils::align_up(exports_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + directory_pos)) + throw pe_exception("Insufficient space for export directory", pe_exception::insufficient_space); + + std::string& raw_data = exports_section.get_raw_data(); + + //This will be done only if exports_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + directory_pos) + raw_data.resize(needed_size + directory_pos); //Expand section raw data + + //Library name will be placed after it + uint32_t current_pos_of_function_names = static_cast<uint32_t>(info.get_name().length() + 1 + directory_pos + sizeof(image_export_directory)); + //Next - function names + uint32_t current_pos_of_function_name_ordinals = current_pos_of_function_names + needed_size_for_function_names; + //Next - function name ordinals + uint32_t current_pos_of_function_forwards = current_pos_of_function_name_ordinals + needed_size_for_function_name_ordinals; + //Finally - function addresses + uint32_t current_pos_of_function_addresses = current_pos_of_function_forwards + needed_size_for_function_forwards; + //Next - function names RVAs + uint32_t current_pos_of_function_names_rvas = current_pos_of_function_addresses + needed_size_for_function_addresses; + + { + //Create export directory and fill it + image_export_directory dir = {0}; + dir.Characteristics = info.get_characteristics(); + dir.MajorVersion = info.get_major_version(); + dir.MinorVersion = info.get_minor_version(); + dir.TimeDateStamp = info.get_timestamp(); + dir.NumberOfFunctions = max_ordinal - ordinal_base + 1; + dir.NumberOfNames = number_of_names; + dir.Base = ordinal_base; + dir.AddressOfFunctions = pe.rva_from_section_offset(exports_section, current_pos_of_function_addresses); + dir.AddressOfNameOrdinals = pe.rva_from_section_offset(exports_section, current_pos_of_function_name_ordinals); + dir.AddressOfNames = pe.rva_from_section_offset(exports_section, current_pos_of_function_names_rvas); + dir.Name = pe.rva_from_section_offset(exports_section, directory_pos + sizeof(image_export_directory)); + + //Save it + memcpy(&raw_data[directory_pos], &dir, sizeof(dir)); + } + + //Sve library name + memcpy(&raw_data[directory_pos + sizeof(image_export_directory)], info.get_name().c_str(), info.get_name().length() + 1); + + //A map to sort function names alphabetically + typedef std::map<std::string, uint16_t> funclist; //function name; function name ordinal + funclist funcs; + + uint32_t last_ordinal = ordinal_base; + //Enumerate all exported functions + for(exported_functions_list::const_iterator it = exports.begin(); it != exports.end(); ++it) + { + const exported_function& func = (*it); + + //If we're skipping some ordinals... + if(func.get_ordinal() > last_ordinal) + { + //Fill this function RVAs data with zeros + uint32_t len = sizeof(uint32_t) * (func.get_ordinal() - last_ordinal - 1); + if(len) + { + memset(&raw_data[current_pos_of_function_addresses], 0, len); + current_pos_of_function_addresses += len; + } + + //Save last encountered ordinal + last_ordinal = func.get_ordinal(); + } + + //If function is named, save its name ordinal and name in sorted alphabetically order + if(func.has_name()) + funcs.insert(std::make_pair(func.get_name(), static_cast<uint16_t>(func.get_ordinal() - ordinal_base))); //Calculate name ordinal + + //If function is forwarded to another DLL + if(func.is_forwarded()) + { + //Write its forwarded name and its RVA + uint32_t function_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_forwards); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + + memcpy(&raw_data[current_pos_of_function_forwards], func.get_forwarded_name().c_str(), func.get_forwarded_name().length() + 1); + current_pos_of_function_forwards += static_cast<uint32_t>(func.get_forwarded_name().length() + 1); + } + else + { + //Write actual function RVA + uint32_t function_rva = func.get_rva(); + memcpy(&raw_data[current_pos_of_function_addresses], &function_rva, sizeof(function_rva)); + current_pos_of_function_addresses += sizeof(function_rva); + } + } + + //Enumerate sorted function names + for(funclist::const_iterator it = funcs.begin(); it != funcs.end(); ++it) + { + //Save function name RVA + uint32_t function_name_rva = pe.rva_from_section_offset(exports_section, current_pos_of_function_names); + memcpy(&raw_data[current_pos_of_function_names_rvas], &function_name_rva, sizeof(function_name_rva)); + current_pos_of_function_names_rvas += sizeof(function_name_rva); + + //Save function name + memcpy(&raw_data[current_pos_of_function_names], (*it).first.c_str(), (*it).first.length() + 1); + current_pos_of_function_names += static_cast<uint32_t>((*it).first.length() + 1); + + //Save function name ordinal + uint16_t name_ordinal = (*it).second; + memcpy(&raw_data[current_pos_of_function_name_ordinals], &name_ordinal, sizeof(name_ordinal)); + current_pos_of_function_name_ordinals += sizeof(name_ordinal); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(exports_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(exports_section, directory_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_export, ret.get_rva()); + pe.set_directory_size(image_directory_entry_export, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_exports.h b/tools/pe_bliss/pe_exports.h new file mode 100644 index 0000000000..127cf86ed6 --- /dev/null +++ b/tools/pe_bliss/pe_exports.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* 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 <vector> +#include <string> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing exported function +class exported_function +{ +public: + //Default constructor + exported_function(); + + //Returns ordinal of function (actually, ordinal = hint + ordinal base) + uint16_t get_ordinal() const; + + //Returns RVA of function + uint32_t get_rva() const; + + //Returns true if function has name and name ordinal + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns name ordinal of function + uint16_t get_name_ordinal() const; + + //Returns true if function is forwarded to other library + bool is_forwarded() const; + //Returns the name of forwarded function + const std::string& get_forwarded_name() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory + + //Sets ordinal of function + void set_ordinal(uint16_t ordinal); + + //Sets RVA of function + void set_rva(uint32_t rva); + + //Sets name of function (or clears it, if empty name is passed) + void set_name(const std::string& name); + //Sets name ordinal + void set_name_ordinal(uint16_t name_ordinal); + + //Sets forwarded function name (or clears it, if empty name is passed) + void set_forwarded_name(const std::string& name); + +private: + uint16_t ordinal_; //Function ordinal + uint32_t rva_; //Function RVA + std::string name_; //Function name + bool has_name_; //true == function has name + uint16_t name_ordinal_; //Function name ordinal + bool forward_; //true == function is forwarded + std::string forward_name_; //Name of forwarded function +}; + +//Class representing export information +class export_info +{ +public: + //Default constructor + export_info(); + + //Returns characteristics + uint32_t get_characteristics() const; + //Returns timestamp + uint32_t get_timestamp() const; + //Returns major version + uint16_t get_major_version() const; + //Returns minor version + uint16_t get_minor_version() const; + //Returns DLL name + const std::string& get_name() const; + //Returns ordinal base + uint32_t get_ordinal_base() const; + //Returns number of functions + uint32_t get_number_of_functions() const; + //Returns number of function names + uint32_t get_number_of_names() const; + //Returns RVA of function address table + uint32_t get_rva_of_functions() const; + //Returns RVA of function name address table + uint32_t get_rva_of_names() const; + //Returns RVA of name ordinals table + uint32_t get_rva_of_name_ordinals() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild export directory using rebuild_exports + + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + //Sets major version + void set_major_version(uint16_t major_version); + //Sets minor version + void set_minor_version(uint16_t minor_version); + //Sets DLL name + void set_name(const std::string& name); + //Sets ordinal base + void set_ordinal_base(uint32_t ordinal_base); + //Sets number of functions + void set_number_of_functions(uint32_t number_of_functions); + //Sets number of function names + void set_number_of_names(uint32_t number_of_names); + //Sets RVA of function address table + void set_rva_of_functions(uint32_t rva_of_functions); + //Sets RVA of function name address table + void set_rva_of_names(uint32_t rva_of_names); + //Sets RVA of name ordinals table + void set_rva_of_name_ordinals(uint32_t rva_of_name_ordinals); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_; + uint16_t minor_version_; + std::string name_; + uint32_t ordinal_base_; + uint32_t number_of_functions_; + uint32_t number_of_names_; + uint32_t address_of_functions_; + uint32_t address_of_names_; + uint32_t address_of_name_ordinals_; +}; + +//Exported functions list typedef +typedef std::vector<exported_function> exported_functions_list; + +//Returns array of exported functions +const exported_functions_list get_exported_functions(const pe_base& pe); +//Returns array of exported functions and information about export +const exported_functions_list get_exported_functions(const pe_base& pe, export_info& info); + +//Helper export functions +//Returns pair: <ordinal base for supplied functions; maximum ordinal value for supplied functions> +const std::pair<uint16_t, uint16_t> get_export_ordinal_limits(const exported_functions_list& exports); + +//Checks if exported function name already exists +bool exported_name_exists(const std::string& function_name, const exported_functions_list& exports); + +//Checks if exported function ordinal already exists +bool exported_ordinal_exists(uint16_t ordinal, const exported_functions_list& exports); + +//Export directory rebuilder +//info - export information +//exported_functions_list - list of exported functions +//exports_section - section where export directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from exports_section raw data start +//save_to_pe_headers - if true, new export directory information will be saved to PE image headers +//auto_strip_last_section - if true and exports are placed in the last section, it will be automatically stripped +//number_of_functions and number_of_names parameters don't matter in "info" when rebuilding, they're calculated independently +//characteristics, major_version, minor_version, timestamp and name are the only used members of "info" structure +//Returns new export directory information +//exported_functions_list is copied intentionally to be sorted by ordinal values later +//Name ordinals in exported function don't matter, they will be recalculated +const image_directory rebuild_exports(pe_base& pe, const export_info& info, exported_functions_list exports, section& exports_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_factory.cpp b/tools/pe_bliss/pe_factory.cpp new file mode 100644 index 0000000000..f6d8a3e1ed --- /dev/null +++ b/tools/pe_bliss/pe_factory.cpp @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* 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 "pe_factory.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +pe_base pe_factory::create_pe(std::istream& file, bool read_debug_raw_data) +{ + return pe_base::get_pe_type(file) == pe_type_32 + ? pe_base(file, pe_properties_32(), read_debug_raw_data) + : pe_base(file, pe_properties_64(), read_debug_raw_data); +} + +pe_base pe_factory::create_pe(const char* file_path, bool read_debug_raw_data) +{ + std::ifstream pe_file(file_path, std::ios::in | std::ios::binary); + if(!pe_file) + { + throw pe_exception("Error in open file.", pe_exception::stream_is_bad); + } + return pe_factory::create_pe(pe_file,read_debug_raw_data); +} +} diff --git a/tools/pe_bliss/pe_factory.h b/tools/pe_bliss/pe_factory.h new file mode 100644 index 0000000000..60b42d9b71 --- /dev/null +++ b/tools/pe_bliss/pe_factory.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* 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 <memory> +#include <istream> +#include <fstream> +#include "pe_base.h" + +namespace pe_bliss +{ +class pe_factory +{ +public: + //Creates pe_base class instance from PE or PE+ istream + //If read_bound_import_raw_data, raw bound import data will be read (used to get bound import info) + //If read_debug_raw_data, raw debug data will be read (used to get image debug info) + static pe_base create_pe(std::istream& file, bool read_debug_raw_data = true); + static pe_base create_pe(const char* file_path, bool read_debug_raw_data = true); +}; +} diff --git a/tools/pe_bliss/pe_imports.cpp b/tools/pe_bliss/pe_imports.cpp new file mode 100644 index 0000000000..0a6c01d6c0 --- /dev/null +++ b/tools/pe_bliss/pe_imports.cpp @@ -0,0 +1,777 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "pe_imports.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMPORTS +//Default constructor +//If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset +//to new value after import rebuilding +//If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero +//IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable +//to be able to modify IAT thunks +import_rebuilder_settings::import_rebuilder_settings(bool set_to_pe_headers, bool auto_zero_directory_entry_iat) + :offset_from_section_start_(0), + build_original_iat_(true), + save_iat_and_original_iat_rvas_(true), + fill_missing_original_iats_(false), + set_to_pe_headers_(set_to_pe_headers), + zero_directory_entry_iat_(auto_zero_directory_entry_iat), + rewrite_iat_and_original_iat_contents_(false), + auto_strip_last_section_(true) +{} + +//Returns offset from section start where import directory data will be placed +uint32_t import_rebuilder_settings::get_offset_from_section_start() const +{ + return offset_from_section_start_; +} + +//Returns true if Original import address table (IAT) will be rebuilt +bool import_rebuilder_settings::build_original_iat() const +{ + return build_original_iat_; +} + +//Returns true if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +bool import_rebuilder_settings::save_iat_and_original_iat_rvas() const +{ + return save_iat_and_original_iat_rvas_; +} + +//Returns true if Original import address and import address tables contents will be rewritten +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +//and save_iat_and_original_iat_rvas is true +bool import_rebuilder_settings::rewrite_iat_and_original_iat_contents() const +{ + return rewrite_iat_and_original_iat_contents_; +} + +//Returns true if original missing IATs will be rebuilt +//(only if IATs are saved) +bool import_rebuilder_settings::fill_missing_original_iats() const +{ + return fill_missing_original_iats_; +} + +//Returns true if PE headers should be updated automatically after rebuilding of imports +bool import_rebuilder_settings::auto_set_to_pe_headers() const +{ + return set_to_pe_headers_; +} + +//Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +bool import_rebuilder_settings::zero_directory_entry_iat() const +{ + return zero_directory_entry_iat_; +} + +//Returns true if the last section should be stripped automatically, if imports are inside it +bool import_rebuilder_settings::auto_strip_last_section_enabled() const +{ + return auto_strip_last_section_; +} + +//Sets offset from section start where import directory data will be placed +void import_rebuilder_settings::set_offset_from_section_start(uint32_t offset) +{ + offset_from_section_start_ = offset; +} + +//Sets if Original import address table (IAT) will be rebuilt +void import_rebuilder_settings::build_original_iat(bool enable) +{ + build_original_iat_ = enable; +} + +//Sets if Original import address and import address tables will not be rebuilt, +//works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero +void import_rebuilder_settings::save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents) +{ + save_iat_and_original_iat_rvas_ = enable; + if(save_iat_and_original_iat_rvas_) + rewrite_iat_and_original_iat_contents_ = enable_rewrite_iat_and_original_iat_contents; + else + rewrite_iat_and_original_iat_contents_ = false; +} + +//Sets if original missing IATs will be rebuilt +//(only if IATs are saved) +void import_rebuilder_settings::fill_missing_original_iats(bool enable) +{ + fill_missing_original_iats_ = enable; +} + +//Sets if PE headers should be updated automatically after rebuilding of imports +void import_rebuilder_settings::auto_set_to_pe_headers(bool enable) +{ + set_to_pe_headers_ = enable; +} + +//Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true +void import_rebuilder_settings::zero_directory_entry_iat(bool enable) +{ + zero_directory_entry_iat_ = enable; +} + +//Sets if the last section should be stripped automatically, if imports are inside it, default true +void import_rebuilder_settings::enable_auto_strip_last_section(bool enable) +{ + auto_strip_last_section_ = enable; +} + +//Default constructor +imported_function::imported_function() + :hint_(0), ordinal_(0), iat_va_(0) +{} + +//Returns name of function +const std::string& imported_function::get_name() const +{ + return name_; +} + +//Returns true if imported function has name (and hint) +bool imported_function::has_name() const +{ + return !name_.empty(); +} + +//Returns hint +uint16_t imported_function::get_hint() const +{ + return hint_; +} + +//Returns ordinal of function +uint16_t imported_function::get_ordinal() const +{ + return ordinal_; +} + +//Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) +uint64_t imported_function::get_iat_va() const +{ + return iat_va_; +} + +//Sets name of function +void imported_function::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets hint +void imported_function::set_hint(uint16_t hint) +{ + hint_ = hint; +} + +//Sets ordinal +void imported_function::set_ordinal(uint16_t ordinal) +{ + ordinal_ = ordinal; +} + +//Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) +void imported_function::set_iat_va(uint64_t va) +{ + iat_va_ = va; +} + +//Default constructor +import_library::import_library() + :rva_to_iat_(0), rva_to_original_iat_(0), timestamp_(0) +{} + +//Returns name of library +const std::string& import_library::get_name() const +{ + return name_; +} + +//Returns RVA to Import Address Table (IAT) +uint32_t import_library::get_rva_to_iat() const +{ + return rva_to_iat_; +} + +//Returns RVA to Original Import Address Table (Original IAT) +uint32_t import_library::get_rva_to_original_iat() const +{ + return rva_to_original_iat_; +} + +//Returns timestamp +uint32_t import_library::get_timestamp() const +{ + return timestamp_; +} + +//Sets name of library +void import_library::set_name(const std::string& name) +{ + name_ = name; +} + +//Sets RVA to Import Address Table (IAT) +void import_library::set_rva_to_iat(uint32_t rva_to_iat) +{ + rva_to_iat_ = rva_to_iat; +} + +//Sets RVA to Original Import Address Table (Original IAT) +void import_library::set_rva_to_original_iat(uint32_t rva_to_original_iat) +{ + rva_to_original_iat_ = rva_to_original_iat; +} + +//Sets timestamp +void import_library::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Returns imported functions list +const import_library::imported_list& import_library::get_imported_functions() const +{ + return imports_; +} + +//Adds imported function +void import_library::add_import(const imported_function& func) +{ + imports_.push_back(func); +} + +//Clears imported functions list +void import_library::clear_imports() +{ + imports_.clear(); +} + +const imported_functions_list get_imported_functions(const pe_base& pe) +{ + return (pe.get_pe_type() == pe_type_32 ? + get_imported_functions_base<pe_types_class_32>(pe) + : get_imported_functions_base<pe_types_class_64>(pe)); +} + +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + return (pe.get_pe_type() == pe_type_32 ? + rebuild_imports_base<pe_types_class_32>(pe, imports, import_section, import_settings) + : rebuild_imports_base<pe_types_class_64>(pe, imports, import_section, import_settings)); +} + +//Returns imported functions list with related libraries info +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe) +{ + imported_functions_list ret; + + //If image has no imports, return empty array + if(!pe.has_imports()) + return ret; + + unsigned long current_descriptor_pos = pe.get_directory_rva(image_directory_entry_import); + //Get first IMAGE_IMPORT_DESCRIPTOR + image_import_descriptor import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Iterate them until we reach zero-element + //We don't need to check correctness of this, because exception will be thrown + //inside of loop if we go outsize of section + while(import_descriptor.Name) + { + //Get imported library information + import_library lib; + + unsigned long max_name_length; + //Get byte count that we have for library name + if((max_name_length = pe.section_data_length_from_rva(import_descriptor.Name, import_descriptor.Name, section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get DLL name pointer + const char* dll_name = pe.section_data_from_rva(import_descriptor.Name, section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(dll_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Set library name + lib.set_name(dll_name); + //Set library timestamp + lib.set_timestamp(import_descriptor.TimeDateStamp); + //Set library RVA to IAT and original IAT + lib.set_rva_to_iat(import_descriptor.FirstThunk); + lib.set_rva_to_original_iat(import_descriptor.OriginalFirstThunk); + + //Get RVA to IAT (it must be filled by loader when loading PE) + uint32_t current_thunk_rva = import_descriptor.FirstThunk; + typename PEClassType::BaseSize import_address_table = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + + //Get RVA to original IAT (lookup table), which must handle imported functions names + //Some linkers leave this pointer zero-filled + //Such image is valid, but it is not possible to restore imported functions names + //afted image was loaded, because IAT becomes the only one table + //containing both function names and function RVAs after loading + uint32_t current_original_thunk_rva = import_descriptor.OriginalFirstThunk; + typename PEClassType::BaseSize import_lookup_table = current_original_thunk_rva == 0 ? import_address_table : pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + if(current_original_thunk_rva == 0) + current_original_thunk_rva = current_thunk_rva; + + //List all imported functions for current DLL + if(import_lookup_table != 0 && import_address_table != 0) + { + while(true) + { + //Imported function description + imported_function func; + + //Get VA from IAT + typename PEClassType::BaseSize address = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_thunk_rva, section_data_virtual, true); + //Move pointer + current_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Jump to next DLL if we finished with this one + if(!address) + break; + + func.set_iat_va(address); + + //Get VA from original IAT + typename PEClassType::BaseSize lookup = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_original_thunk_rva, section_data_virtual, true); + //Move pointer + current_original_thunk_rva += sizeof(typename PEClassType::BaseSize); + + //Check if function is imported by ordinal + if((lookup & PEClassType::ImportSnapFlag) != 0) + { + //Set function ordinal + func.set_ordinal(static_cast<uint16_t>(lookup & 0xffff)); + } + else + { + //Get byte count that we have for function name + if(lookup > static_cast<uint32_t>(-1) - sizeof(uint16_t)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get maximum available length of function name + if((max_name_length = pe.section_data_length_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true)) < 2) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Get imported function name + const char* func_name = pe.section_data_from_rva(static_cast<uint32_t>(lookup + sizeof(uint16_t)), section_data_virtual, true); + + //Check for null-termination + if(!pe_utils::is_null_terminated(func_name, max_name_length)) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //HINT in import table is ORDINAL in export table + uint16_t hint = pe.section_data_from_rva<uint16_t>(static_cast<uint32_t>(lookup), section_data_virtual, true); + + //Save hint and name + func.set_name(func_name); + func.set_hint(hint); + } + + //Add function to list + lib.add_import(func); + } + } + + //Check possible overflow + if(!pe_utils::is_sum_safe(current_descriptor_pos, sizeof(image_import_descriptor))) + throw pe_exception("Incorrect import directory", pe_exception::incorrect_import_directory); + + //Go to next library + current_descriptor_pos += sizeof(image_import_descriptor); + import_descriptor = pe.section_data_from_rva<image_import_descriptor>(current_descriptor_pos, section_data_virtual, true); + + //Save import information + ret.push_back(lib); + } + + //Return resulting list + return ret; +} + + +//Simple import directory rebuilder +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings) +{ + //Check that import_section is attached to this PE image + if(!pe.section_attached(import_section)) + throw pe_exception("Import section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t needed_size = 0; //Calculate needed size for import structures and strings + uint32_t needed_size_for_strings = 0; //Calculate needed size for import strings (library and function names and hints) + uint32_t size_of_iat = 0; //Size of IAT structures + + needed_size += static_cast<uint32_t>((1 /* ending null descriptor */ + imports.size()) * sizeof(image_import_descriptor)); + + //Enumerate imported functions + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + needed_size_for_strings += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + const import_library::imported_list& funcs = (*it).get_imported_functions(); + + //IMAGE_THUNK_DATA + size_of_iat += static_cast<uint32_t>(sizeof(typename PEClassType::BaseSize) * (1 /*ending null */ + funcs.size())); + + //Enumerate all imported functions in library + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) + needed_size_for_strings += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + } + + if(import_settings.build_original_iat() || import_settings.fill_missing_original_iats()) + needed_size += size_of_iat * 2; //We'll have two similar-sized IATs if we're building original IAT + else + needed_size += size_of_iat; + + needed_size += sizeof(typename PEClassType::BaseSize); //Maximum align for IAT and original IAT + + //Total needed size for import structures and strings + needed_size += needed_size_for_strings; + + //Check if import_section is last one. If it's not, check if there's enough place for import data + if(&import_section != &*(pe.get_image_sections().end() - 1) && + (import_section.empty() || pe_utils::align_up(import_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + import_settings.get_offset_from_section_start())) + throw pe_exception("Insufficient space for import directory", pe_exception::insufficient_space); + + std::string& raw_data = import_section.get_raw_data(); + + //This will be done only if image_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + import_settings.get_offset_from_section_start()) + raw_data.resize(needed_size + import_settings.get_offset_from_section_start()); //Expand section raw data + + uint32_t current_string_pointer = import_settings.get_offset_from_section_start();/* we will paste structures after strings */ + + //Position for IAT + uint32_t current_pos_for_iat = pe_utils::align_up(static_cast<uint32_t>(needed_size_for_strings + import_settings.get_offset_from_section_start() + (1 + imports.size()) * sizeof(image_import_descriptor)), sizeof(typename PEClassType::BaseSize)); + //Position for original IAT + uint32_t current_pos_for_original_iat = current_pos_for_iat + size_of_iat; + //Position for import descriptors + uint32_t current_pos_for_descriptors = needed_size_for_strings + import_settings.get_offset_from_section_start(); + + //Build imports + for(imported_functions_list::const_iterator it = imports.begin(); it != imports.end(); ++it) + { + //Create import descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + descr.TimeDateStamp = (*it).get_timestamp(); //Restore timestamp + descr.Name = pe.rva_from_section_offset(import_section, current_string_pointer); //Library name RVA + + //If we should save IAT for current import descriptor + bool save_iats_for_this_descriptor = import_settings.save_iat_and_original_iat_rvas() && (*it).get_rva_to_iat() != 0; + //If we should write original IAT + bool write_original_iat = (!save_iats_for_this_descriptor && import_settings.build_original_iat()) || import_settings.fill_missing_original_iats(); + + //If we should rewrite saved original IAT for current import descriptor (without changing its position) + bool rewrite_saved_original_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && import_settings.build_original_iat(); + //If we should rewrite saved IAT for current import descriptor (without changing its position) + bool rewrite_saved_iat = save_iats_for_this_descriptor && import_settings.rewrite_iat_and_original_iat_contents() && (*it).get_rva_to_iat() != 0; + + //Helper values if we're rewriting existing IAT or orig.IAT + uint32_t original_first_thunk = 0; + uint32_t first_thunk = 0; + + if(save_iats_for_this_descriptor) + { + //If there's no original IAT and we're asked to rebuild missing original IATs + if(!(*it).get_rva_to_original_iat() && import_settings.fill_missing_original_iats()) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + else + descr.OriginalFirstThunk = import_settings.build_original_iat() ? (*it).get_rva_to_original_iat() : 0; + + descr.FirstThunk = (*it).get_rva_to_iat(); + + original_first_thunk = descr.OriginalFirstThunk; + first_thunk = descr.FirstThunk; + + if(rewrite_saved_original_iat) + { + if((*it).get_rva_to_original_iat()) + write_original_iat = true; + else + rewrite_saved_original_iat = false; + } + + if(rewrite_saved_iat) + save_iats_for_this_descriptor = false; + } + else + { + //We are creating new IAT and original IAT (if needed) + descr.OriginalFirstThunk = import_settings.build_original_iat() ? pe.rva_from_section_offset(import_section, current_pos_for_original_iat) : 0; + descr.FirstThunk = pe.rva_from_section_offset(import_section, current_pos_for_iat); + } + + //Save import descriptor + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + current_pos_for_descriptors += sizeof(descr); + + //Save library name + memcpy(&raw_data[current_string_pointer], (*it).get_name().c_str(), (*it).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*it).get_name().length() + 1 /* nullbyte */); + + //List all imported functions + const import_library::imported_list& funcs = (*it).get_imported_functions(); + for(import_library::imported_list::const_iterator f = funcs.begin(); f != funcs.end(); ++f) + { + if((*f).has_name()) //If function is imported by name + { + //Get RVA of IMAGE_IMPORT_BY_NAME + typename PEClassType::BaseSize rva_of_named_import = pe.rva_from_section_offset(import_section, current_string_pointer); + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can write to IAT saved VA (because IMAGE_IMPORT_BY_NAME will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + else + { + //Else - write to IAT RVA of IMAGE_IMPORT_BY_NAME + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + first_thunk += sizeof(rva_of_named_import); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_iat += sizeof(rva_of_named_import); + } + } + } + + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(rva_of_named_import)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &rva_of_named_import, sizeof(rva_of_named_import)); + + original_first_thunk += sizeof(rva_of_named_import); + } + else + { + //We're creating original IATs + memcpy(&raw_data[current_pos_for_original_iat], &rva_of_named_import, sizeof(rva_of_named_import)); + current_pos_for_original_iat += sizeof(rva_of_named_import); + } + } + + //Write IMAGE_IMPORT_BY_NAME (WORD hint + string function name) + uint16_t hint = (*f).get_hint(); + memcpy(&raw_data[current_string_pointer], &hint, sizeof(hint)); + memcpy(&raw_data[current_string_pointer + sizeof(uint16_t)], (*f).get_name().c_str(), (*f).get_name().length() + 1 /* nullbyte */); + current_string_pointer += static_cast<uint32_t>((*f).get_name().length() + 1 /* nullbyte */ + sizeof(uint16_t) /* hint */); + } + else //Function is imported by ordinal + { + uint16_t ordinal = (*f).get_ordinal(); + typename PEClassType::BaseSize thunk_value = ordinal; + thunk_value |= PEClassType::ImportSnapFlag; //Imported by ordinal + + if(!save_iats_for_this_descriptor) + { + if(write_original_iat) + { + //We're creating original IATs - so we can wtire to IAT saved VA (because ordinal will be read + //by PE loader from original IAT) + typename PEClassType::BaseSize iat_value = static_cast<typename PEClassType::BaseSize>((*f).get_iat_va()); + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(iat_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &iat_value, sizeof(iat_value)); + + first_thunk += sizeof(iat_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &iat_value, sizeof(iat_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + else + { + //Else - write ordinal to IAT + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + } + } + } + + //We're writing ordinal to original IAT slot + if(write_original_iat) + { + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + } + + if(!save_iats_for_this_descriptor) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_iat) + { + if(pe.section_data_length_from_rva(first_thunk, first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(first_thunk, true), &thunk_value, sizeof(thunk_value)); + + first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_iat += sizeof(thunk_value); + } + } + + if(write_original_iat) + { + //Ending null thunks + typename PEClassType::BaseSize thunk_value = 0; + + if(rewrite_saved_original_iat) + { + if(pe.section_data_length_from_rva(original_first_thunk, original_first_thunk, section_data_raw, true) <= sizeof(thunk_value)) + throw pe_exception("Insufficient space inside initial original IAT", pe_exception::insufficient_space); + + memcpy(pe.section_data_from_rva(original_first_thunk, true), &thunk_value, sizeof(thunk_value)); + + original_first_thunk += sizeof(thunk_value); + } + else + { + memcpy(&raw_data[current_pos_for_original_iat], &thunk_value, sizeof(thunk_value)); + current_pos_for_original_iat += sizeof(thunk_value); + } + } + } + + { + //Null ending descriptor + image_import_descriptor descr; + memset(&descr, 0, sizeof(descr)); + memcpy(&raw_data[current_pos_for_descriptors], &descr, sizeof(descr)); + } + + //Strip data a little, if we saved some place + //We're allocating more space than needed, if present original IAT and IAT are saved + raw_data.resize(current_pos_for_original_iat); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(import_section, import_settings.auto_strip_last_section_enabled()); + + //Return information about rebuilt import directory + image_directory ret(pe.rva_from_section_offset(import_section, import_settings.get_offset_from_section_start() + needed_size_for_strings), needed_size - needed_size_for_strings); + + //If auto-rewrite of PE headers is required + if(import_settings.auto_set_to_pe_headers()) + { + pe.set_directory_rva(image_directory_entry_import, ret.get_rva()); + pe.set_directory_size(image_directory_entry_import, ret.get_size()); + + //If we are requested to zero IMAGE_DIRECTORY_ENTRY_IAT also + if(import_settings.zero_directory_entry_iat()) + { + pe.set_directory_rva(image_directory_entry_iat, 0); + pe.set_directory_size(image_directory_entry_iat, 0); + } + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_imports.h b/tools/pe_bliss/pe_imports.h new file mode 100644 index 0000000000..681b5b59bd --- /dev/null +++ b/tools/pe_bliss/pe_imports.h @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* 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 <vector> +#include <string> +#include "pe_structures.h" +#include "pe_directory.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Class representing imported function +class imported_function +{ +public: + //Default constructor + imported_function(); + + //Returns true if imported function has name (and hint) + bool has_name() const; + //Returns name of function + const std::string& get_name() const; + //Returns hint + uint16_t get_hint() const; + //Returns ordinal of function + uint16_t get_ordinal() const; + + //Returns IAT entry VA (usable if image has both IAT and original IAT and is bound) + uint64_t get_iat_va() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of function + void set_name(const std::string& name); + //Sets hint + void set_hint(uint16_t hint); + //Sets ordinal + void set_ordinal(uint16_t ordinal); + + //Sets IAT entry VA (usable if image has both IAT and original IAT and is bound) + void set_iat_va(uint64_t rva); + +private: + std::string name_; //Function name + uint16_t hint_; //Hint + uint16_t ordinal_; //Ordinal + uint64_t iat_va_; +}; + +//Class representing imported library information +class import_library +{ +public: + typedef std::vector<imported_function> imported_list; + +public: + //Default constructor + import_library(); + + //Returns name of library + const std::string& get_name() const; + //Returns RVA to Import Address Table (IAT) + uint32_t get_rva_to_iat() const; + //Returns RVA to Original Import Address Table (Original IAT) + uint32_t get_rva_to_original_iat() const; + //Returns timestamp + uint32_t get_timestamp() const; + + //Returns imported functions list + const imported_list& get_imported_functions() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You also can use them to rebuild image imports + //Sets name of library + void set_name(const std::string& name); + //Sets RVA to Import Address Table (IAT) + void set_rva_to_iat(uint32_t rva_to_iat); + //Sets RVA to Original Import Address Table (Original IAT) + void set_rva_to_original_iat(uint32_t rva_to_original_iat); + //Sets timestamp + void set_timestamp(uint32_t timestamp); + + //Adds imported function + void add_import(const imported_function& func); + //Clears imported functions list + void clear_imports(); + +private: + std::string name_; //Library name + uint32_t rva_to_iat_; //RVA to IAT + uint32_t rva_to_original_iat_; //RVA to original IAT + uint32_t timestamp_; //DLL TimeStamp + + imported_list imports_; +}; + +//Simple import directory rebuilder +//Class representing import rebuilder advanced settings +class import_rebuilder_settings +{ +public: + //Default constructor + //Default constructor + //If set_to_pe_headers = true, IMAGE_DIRECTORY_ENTRY_IMPORT entry will be reset + //to new value after import rebuilding + //If auto_zero_directory_entry_iat = true, IMAGE_DIRECTORY_ENTRY_IAT will be set to zero + //IMAGE_DIRECTORY_ENTRY_IAT is used by loader to temporarily make section, where IMAGE_DIRECTORY_ENTRY_IAT RVA points, writeable + //to be able to modify IAT thunks + explicit import_rebuilder_settings(bool set_to_pe_headers = true, bool auto_zero_directory_entry_iat = false); + + //Returns offset from section start where import directory data will be placed + uint32_t get_offset_from_section_start() const; + //Returns true if Original import address table (IAT) will be rebuilt + bool build_original_iat() const; + + //Returns true if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + bool save_iat_and_original_iat_rvas() const; + //Returns true if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + bool rewrite_iat_and_original_iat_contents() const; + + //Returns true if original missing IATs will be rebuilt + //(only if IATs are saved) + bool fill_missing_original_iats() const; + //Returns true if PE headers should be updated automatically after rebuilding of imports + bool auto_set_to_pe_headers() const; + //Returns true if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + bool zero_directory_entry_iat() const; + + //Returns true if the last section should be stripped automatically, if imports are inside it + bool auto_strip_last_section_enabled() const; + +public: //Setters + //Sets offset from section start where import directory data will be placed + void set_offset_from_section_start(uint32_t offset); + //Sets if Original import address table (IAT) will be rebuilt + void build_original_iat(bool enable); + //Sets if Original import address and import address tables will not be rebuilt, + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //enable_rewrite_iat_and_original_iat_contents sets if Original import address and import address tables contents will be rewritten + //works only if import descriptor IAT (and orig.IAT, if present) RVAs are not zero + //and save_iat_and_original_iat_rvas is true + void save_iat_and_original_iat_rvas(bool enable, bool enable_rewrite_iat_and_original_iat_contents = false); + //Sets if original missing IATs will be rebuilt + //(only if IATs are saved) + void fill_missing_original_iats(bool enable); + //Sets if PE headers should be updated automatically after rebuilding of imports + void auto_set_to_pe_headers(bool enable); + //Sets if IMAGE_DIRECTORY_ENTRY_IAT must be zeroed, works only if auto_set_to_pe_headers = true + void zero_directory_entry_iat(bool enable); + + //Sets if the last section should be stripped automatically, if imports are inside it, default true + void enable_auto_strip_last_section(bool enable); + +private: + uint32_t offset_from_section_start_; + bool build_original_iat_; + bool save_iat_and_original_iat_rvas_; + bool fill_missing_original_iats_; + bool set_to_pe_headers_; + bool zero_directory_entry_iat_; + bool rewrite_iat_and_original_iat_contents_; + bool auto_strip_last_section_; +}; + +typedef std::vector<import_library> imported_functions_list; + + +//Returns imported functions list with related libraries info +const imported_functions_list get_imported_functions(const pe_base& pe); + +template<typename PEClassType> +const imported_functions_list get_imported_functions_base(const pe_base& pe); + + +//You can get all image imports with get_imported_functions() function +//You can use returned value to, for example, add new imported library with some functions +//to the end of list of imported libraries +//To keep PE file working, rebuild its imports with save_iat_and_original_iat_rvas = true (default) +//Don't add new imported functions to existing imported library entries, because this can cause +//rewriting of some used memory (or other IAT/orig.IAT fields) by system loader +//The safest way is just adding import libraries with functions to the end of imported_functions_list array +const image_directory rebuild_imports(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); + +template<typename PEClassType> +const image_directory rebuild_imports_base(pe_base& pe, const imported_functions_list& imports, section& import_section, const import_rebuilder_settings& import_settings = import_rebuilder_settings()); +} diff --git a/tools/pe_bliss/pe_load_config.cpp b/tools/pe_bliss/pe_load_config.cpp new file mode 100644 index 0000000000..c05895fecd --- /dev/null +++ b/tools/pe_bliss/pe_load_config.cpp @@ -0,0 +1,557 @@ +/*************************************************************************/ +/* 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 <algorithm> +#include <string.h> +#include "pe_load_config.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//IMAGE CONFIG +//Default constructor +image_config_info::image_config_info() + :time_stamp_(0), + major_version_(0), minor_version_(0), + global_flags_clear_(0), global_flags_set_(0), + critical_section_default_timeout_(0), + decommit_free_block_threshold_(0), decommit_total_free_threshold_(0), + lock_prefix_table_va_(0), + max_allocation_size_(0), + virtual_memory_threshold_(0), + process_affinity_mask_(0), + process_heap_flags_(0), + service_pack_version_(0), + edit_list_va_(0), + security_cookie_va_(0), + se_handler_table_va_(0), + se_handler_count_(0) +{} + +//Constructors from PE structures +template<typename ConfigStructure> +image_config_info::image_config_info(const ConfigStructure& info) + :time_stamp_(info.TimeDateStamp), + major_version_(info.MajorVersion), minor_version_(info.MinorVersion), + global_flags_clear_(info.GlobalFlagsClear), global_flags_set_(info.GlobalFlagsSet), + critical_section_default_timeout_(info.CriticalSectionDefaultTimeout), + decommit_free_block_threshold_(info.DeCommitFreeBlockThreshold), decommit_total_free_threshold_(info.DeCommitTotalFreeThreshold), + lock_prefix_table_va_(info.LockPrefixTable), + max_allocation_size_(info.MaximumAllocationSize), + virtual_memory_threshold_(info.VirtualMemoryThreshold), + process_affinity_mask_(info.ProcessAffinityMask), + process_heap_flags_(info.ProcessHeapFlags), + service_pack_version_(info.CSDVersion), + edit_list_va_(info.EditList), + security_cookie_va_(info.SecurityCookie), + se_handler_table_va_(info.SEHandlerTable), + se_handler_count_(info.SEHandlerCount) +{} + +//Instantiate template constructor with needed structures +template image_config_info::image_config_info(const image_load_config_directory32& info); +template image_config_info::image_config_info(const image_load_config_directory64& info); + +//Returns the date and time stamp value +uint32_t image_config_info::get_time_stamp() const +{ + return time_stamp_; +} + +//Returns major version number +uint16_t image_config_info::get_major_version() const +{ + return major_version_; +} + +//Returns minor version number +uint16_t image_config_info::get_minor_version() const +{ + return minor_version_; +} + +//Returns clear global flags +uint32_t image_config_info::get_global_flags_clear() const +{ + return global_flags_clear_; +} + +//Returns set global flags +uint32_t image_config_info::get_global_flags_set() const +{ + return global_flags_set_; +} + +//Returns critical section default timeout +uint32_t image_config_info::get_critical_section_default_timeout() const +{ + return critical_section_default_timeout_; +} + +//Get the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_free_block_threshold() const +{ + return decommit_free_block_threshold_; +} + +//Returns the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +uint64_t image_config_info::get_decommit_total_free_threshold() const +{ + return decommit_total_free_threshold_; +} + +//Returns VA of a list of addresses where the LOCK prefix is used +uint64_t image_config_info::get_lock_prefix_table_va() const +{ + return lock_prefix_table_va_; +} + +//Returns the maximum allocation size, in bytes +uint64_t image_config_info::get_max_allocation_size() const +{ + return max_allocation_size_; +} + +//Returns the maximum block size that can be allocated from heap segments, in bytes +uint64_t image_config_info::get_virtual_memory_threshold() const +{ + return virtual_memory_threshold_; +} + +//Returns process affinity mask +uint64_t image_config_info::get_process_affinity_mask() const +{ + return process_affinity_mask_; +} + +//Returns process heap flags +uint32_t image_config_info::get_process_heap_flags() const +{ + return process_heap_flags_; +} + +//Returns service pack version (CSDVersion) +uint16_t image_config_info::get_service_pack_version() const +{ + return service_pack_version_; +} + +//Returns VA of edit list (reserved by system) +uint64_t image_config_info::get_edit_list_va() const +{ + return edit_list_va_; +} + +//Returns a pointer to a cookie that is used by Visual C++ or GS implementation +uint64_t image_config_info::get_security_cookie_va() const +{ + return security_cookie_va_; +} + +//Returns VA of the sorted table of RVAs of each valid, unique handler in the image +uint64_t image_config_info::get_se_handler_table_va() const +{ + return se_handler_table_va_; +} + +//Returns the count of unique handlers in the table +uint64_t image_config_info::get_se_handler_count() const +{ + return se_handler_count_; +} + +//Returns SE Handler RVA list +const image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() const +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +const image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() const +{ + return lock_prefixes_; +} + +//Adds SE Handler RVA to list +void image_config_info::add_se_handler_rva(uint32_t rva) +{ + se_handlers_.push_back(rva); +} + +//Clears SE Handler list +void image_config_info::clear_se_handler_list() +{ + se_handlers_.clear(); +} + +//Adds Lock Prefix RVA to list +void image_config_info::add_lock_prefix_rva(uint32_t rva) +{ + lock_prefixes_.push_back(rva); +} + +//Clears Lock Prefix list +void image_config_info::clear_lock_prefix_list() +{ + lock_prefixes_.clear(); +} + +//Sets the date and time stamp value +void image_config_info::set_time_stamp(uint32_t time_stamp) +{ + time_stamp_ = time_stamp; +} + +//Sets major version number +void image_config_info::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version number +void image_config_info::set_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Sets clear global flags +void image_config_info::set_global_flags_clear(uint32_t global_flags_clear) +{ + global_flags_clear_ = global_flags_clear; +} + +//Sets set global flags +void image_config_info::set_global_flags_set(uint32_t global_flags_set) +{ + global_flags_set_ = global_flags_set; +} + +//Sets critical section default timeout +void image_config_info::set_critical_section_default_timeout(uint32_t critical_section_default_timeout) +{ + critical_section_default_timeout_ = critical_section_default_timeout; +} + +//Sets the size of the minimum block that +//must be freed before it is freed (de-committed), in bytes +void image_config_info::set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold) +{ + decommit_free_block_threshold_ = decommit_free_block_threshold; +} + +//Sets the size of the minimum total memory +//that must be freed in the process heap before it is freed (de-committed), in bytes +void image_config_info::set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold) +{ + decommit_total_free_threshold_ = decommit_total_free_threshold; +} + +//Sets VA of a list of addresses where the LOCK prefix is used +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_lock_prefix_table_va(uint64_t lock_prefix_table_va) +{ + lock_prefix_table_va_ = lock_prefix_table_va; +} + +//Sets the maximum allocation size, in bytes +void image_config_info::set_max_allocation_size(uint64_t max_allocation_size) +{ + max_allocation_size_ = max_allocation_size; +} + +//Sets the maximum block size that can be allocated from heap segments, in bytes +void image_config_info::set_virtual_memory_threshold(uint64_t virtual_memory_threshold) +{ + virtual_memory_threshold_ = virtual_memory_threshold; +} + +//Sets process affinity mask +void image_config_info::set_process_affinity_mask(uint64_t process_affinity_mask) +{ + process_affinity_mask_ = process_affinity_mask; +} + +//Sets process heap flags +void image_config_info::set_process_heap_flags(uint32_t process_heap_flags) +{ + process_heap_flags_ = process_heap_flags; +} + +//Sets service pack version (CSDVersion) +void image_config_info::set_service_pack_version(uint16_t service_pack_version) +{ + service_pack_version_ = service_pack_version; +} + +//Sets VA of edit list (reserved by system) +void image_config_info::set_edit_list_va(uint64_t edit_list_va) +{ + edit_list_va_ = edit_list_va; +} + +//Sets a pointer to a cookie that is used by Visual C++ or GS implementation +void image_config_info::set_security_cookie_va(uint64_t security_cookie_va) +{ + security_cookie_va_ = security_cookie_va; +} + +//Sets VA of the sorted table of RVAs of each valid, unique handler in the image +//If you rebuild this list, VA will be re-assigned automatically +void image_config_info::set_se_handler_table_va(uint64_t se_handler_table_va) +{ + se_handler_table_va_ = se_handler_table_va; +} + +//Returns SE Handler RVA list +image_config_info::se_handler_list& image_config_info::get_se_handler_rvas() +{ + return se_handlers_; +} + +//Returns Lock Prefix RVA list +image_config_info::lock_prefix_rva_list& image_config_info::get_lock_prefix_rvas() +{ + return lock_prefixes_; +} + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_image_config_base<pe_types_class_32>(pe) + : get_image_config_base<pe_types_class_64>(pe); +} + +//Image config rebuilder +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_image_config_base<pe_types_class_32>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section) + : rebuild_image_config_base<pe_types_class_64>(pe, info, image_config_section, offset_from_section_start, write_se_handlers, write_lock_prefixes, save_to_pe_header, auto_strip_last_section); +} + + +//Returns image config info +//If image does not have config info, throws an exception +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe) +{ + //Check if image has config directory + if(!pe.has_config()) + throw pe_exception("Image does not have load config directory", pe_exception::directory_does_not_exist); + + //Get load config structure + typename PEClassType::ConfigStruct config_info = pe.section_data_from_rva<typename PEClassType::ConfigStruct>(pe.get_directory_rva(image_directory_entry_load_config), section_data_virtual); + + //Check size of config directory + if(config_info.Size != sizeof(config_info)) + throw pe_exception("Incorrect (or old) load config directory", pe_exception::incorrect_config_directory); + + //Fill return structure + image_config_info ret(config_info); + + //Check possible overflow + if(config_info.SEHandlerCount >= pe_utils::max_dword / sizeof(uint32_t) + || config_info.SEHandlerTable >= static_cast<typename PEClassType::BaseSize>(-1) - config_info.SEHandlerCount * sizeof(uint32_t)) + throw pe_exception("Incorrect load config directory", pe_exception::incorrect_config_directory); + + //Read sorted SE handler RVA list (if any) + for(typename PEClassType::BaseSize i = 0; i != config_info.SEHandlerCount; ++i) + ret.add_se_handler_rva(pe.section_data_from_va<uint32_t>(static_cast<typename PEClassType::BaseSize>(config_info.SEHandlerTable + i * sizeof(uint32_t)))); + + if(config_info.LockPrefixTable) + { + //Read Lock Prefix VA list (if any) + unsigned long current = 0; + while(true) + { + typename PEClassType::BaseSize lock_prefix_va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(config_info.LockPrefixTable + current * sizeof(typename PEClassType::BaseSize))); + if(!lock_prefix_va) + break; + + ret.add_lock_prefix_rva(pe.va_to_rva(lock_prefix_va)); + + ++current; + } + } + + return ret; +} + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start, bool write_se_handlers, bool write_lock_prefixes, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that image_config_section is attached to this PE image + if(!pe.section_attached(image_config_section)) + throw pe_exception("Image Config section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t alignment = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)) - offset_from_section_start; + + uint32_t needed_size = sizeof(typename PEClassType::ConfigStruct); //Calculate needed size for Image Config table + + uint32_t image_config_data_pos = offset_from_section_start + alignment; + + uint32_t current_pos_of_se_handlers = 0; + uint32_t current_pos_of_lock_prefixes = 0; + + if(write_se_handlers) + { + current_pos_of_se_handlers = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>(info.get_se_handler_rvas().size()) * sizeof(uint32_t); //RVAs of SE Handlers + } + + if(write_lock_prefixes) + { + current_pos_of_lock_prefixes = needed_size + image_config_data_pos; + needed_size += static_cast<uint32_t>((info.get_lock_prefix_rvas().size() + 1) * sizeof(typename PEClassType::BaseSize)); //VAs of Lock Prefixes (and ending null element) + } + + //Check if image_config_section is last one. If it's not, check if there's enough place for Image Config data + if(&image_config_section != &*(pe.get_image_sections().end() - 1) && + (image_config_section.empty() || pe_utils::align_up(image_config_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + image_config_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + std::string& raw_data = image_config_section.get_raw_data(); + + //This will be done only if image_config_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + image_config_data_pos) + raw_data.resize(needed_size + image_config_data_pos); //Expand section raw data + + //Create and fill Image Config structure + typename PEClassType::ConfigStruct image_config_section_struct = {0}; + image_config_section_struct.Size = sizeof(image_config_section_struct); + image_config_section_struct.TimeDateStamp = info.get_time_stamp(); + image_config_section_struct.MajorVersion = info.get_major_version(); + image_config_section_struct.MinorVersion = info.get_minor_version(); + image_config_section_struct.GlobalFlagsClear = info.get_global_flags_clear(); + image_config_section_struct.GlobalFlagsSet = info.get_global_flags_set(); + image_config_section_struct.CriticalSectionDefaultTimeout = info.get_critical_section_default_timeout(); + image_config_section_struct.DeCommitFreeBlockThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_free_block_threshold()); + image_config_section_struct.DeCommitTotalFreeThreshold = static_cast<typename PEClassType::BaseSize>(info.get_decommit_total_free_threshold()); + image_config_section_struct.MaximumAllocationSize = static_cast<typename PEClassType::BaseSize>(info.get_max_allocation_size()); + image_config_section_struct.VirtualMemoryThreshold = static_cast<typename PEClassType::BaseSize>(info.get_virtual_memory_threshold()); + image_config_section_struct.ProcessHeapFlags = info.get_process_heap_flags(); + image_config_section_struct.ProcessAffinityMask = static_cast<typename PEClassType::BaseSize>(info.get_process_affinity_mask()); + image_config_section_struct.CSDVersion = info.get_service_pack_version(); + image_config_section_struct.EditList = static_cast<typename PEClassType::BaseSize>(info.get_edit_list_va()); + image_config_section_struct.SecurityCookie = static_cast<typename PEClassType::BaseSize>(info.get_security_cookie_va()); + image_config_section_struct.SEHandlerCount = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_rvas().size()); + + + if(write_se_handlers) + { + if(info.get_se_handler_rvas().empty()) + { + write_se_handlers = false; + image_config_section_struct.SEHandlerTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_se_handlers), va); + image_config_section_struct.SEHandlerTable = va; + } + } + else + { + image_config_section_struct.SEHandlerTable = static_cast<typename PEClassType::BaseSize>(info.get_se_handler_table_va()); + } + + if(write_lock_prefixes) + { + if(info.get_lock_prefix_rvas().empty()) + { + write_lock_prefixes = false; + image_config_section_struct.LockPrefixTable = 0; + } + else + { + typename PEClassType::BaseSize va; + pe.rva_to_va(pe.rva_from_section_offset(image_config_section, current_pos_of_lock_prefixes), va); + image_config_section_struct.LockPrefixTable = va; + } + } + else + { + image_config_section_struct.LockPrefixTable = static_cast<typename PEClassType::BaseSize>(info.get_lock_prefix_table_va()); + } + + //Write image config section + memcpy(&raw_data[image_config_data_pos], &image_config_section_struct, sizeof(image_config_section_struct)); + + if(write_se_handlers) + { + //Sort SE Handlers list + image_config_info::se_handler_list sorted_list = info.get_se_handler_rvas(); + std::sort(sorted_list.begin(), sorted_list.end()); + + //Write SE Handlers table + for(image_config_info::se_handler_list::const_iterator it = sorted_list.begin(); it != sorted_list.end(); ++it) + { + uint32_t se_handler_rva = *it; + memcpy(&raw_data[current_pos_of_se_handlers], &se_handler_rva, sizeof(se_handler_rva)); + current_pos_of_se_handlers += sizeof(se_handler_rva); + } + } + + if(write_lock_prefixes) + { + //Write Lock Prefixes VA list + for(image_config_info::lock_prefix_rva_list::const_iterator it = info.get_lock_prefix_rvas().begin(); it != info.get_lock_prefix_rvas().end(); ++it) + { + typename PEClassType::BaseSize lock_prefix_va; + pe.rva_to_va(*it, lock_prefix_va); + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + current_pos_of_lock_prefixes += sizeof(lock_prefix_va); + } + + { + //Ending null VA + typename PEClassType::BaseSize lock_prefix_va = 0; + memcpy(&raw_data[current_pos_of_lock_prefixes], &lock_prefix_va, sizeof(lock_prefix_va)); + } + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(image_config_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(image_config_section, image_config_data_pos), sizeof(typename PEClassType::ConfigStruct)); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_load_config, ret.get_rva()); + pe.set_directory_size(image_directory_entry_load_config, ret.get_size()); + } + + return ret; +} + +} diff --git a/tools/pe_bliss/pe_load_config.h b/tools/pe_bliss/pe_load_config.h new file mode 100644 index 0000000000..cb24072de7 --- /dev/null +++ b/tools/pe_bliss/pe_load_config.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* 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 <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing image configuration information +class image_config_info +{ +public: + typedef std::vector<uint32_t> se_handler_list; + typedef std::vector<uint32_t> lock_prefix_rva_list; + +public: + //Default constructor + image_config_info(); + //Constructors from PE structures (no checks) + template<typename ConfigStructure> + explicit image_config_info(const ConfigStructure& info); + + //Returns the date and time stamp value + uint32_t get_time_stamp() const; + //Returns major version number + uint16_t get_major_version() const; + //Returns minor version number + uint16_t get_minor_version() const; + //Returns clear global flags + uint32_t get_global_flags_clear() const; + //Returns set global flags + uint32_t get_global_flags_set() const; + //Returns critical section default timeout + uint32_t get_critical_section_default_timeout() const; + //Get the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + uint64_t get_decommit_free_block_threshold() const; + //Returns the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + uint64_t get_decommit_total_free_threshold() const; + //Returns VA of a list of addresses where the LOCK prefix is used + uint64_t get_lock_prefix_table_va() const; + //Returns the maximum allocation size, in bytes + uint64_t get_max_allocation_size() const; + //Returns the maximum block size that can be allocated from heap segments, in bytes + uint64_t get_virtual_memory_threshold() const; + //Returns process affinity mask + uint64_t get_process_affinity_mask() const; + //Returns process heap flags + uint32_t get_process_heap_flags() const; + //Returns service pack version (CSDVersion) + uint16_t get_service_pack_version() const; + //Returns VA of edit list (reserved by system) + uint64_t get_edit_list_va() const; + //Returns a pointer to a cookie that is used by Visual C++ or GS implementation + uint64_t get_security_cookie_va() const; + //Returns VA of the sorted table of RVAs of each valid, unique handler in the image + uint64_t get_se_handler_table_va() const; + //Returns the count of unique handlers in the table + uint64_t get_se_handler_count() const; + + //Returns SE Handler RVA list + const se_handler_list& get_se_handler_rvas() const; + + //Returns Lock Prefix RVA list + const lock_prefix_rva_list& get_lock_prefix_rvas() const; + +public: //These functions do not change everything inside image, they are used by PE class + //Also you can use these functions to rebuild image config directory + + //Adds SE Handler RVA to list + void add_se_handler_rva(uint32_t rva); + //Clears SE Handler list + void clear_se_handler_list(); + + //Adds Lock Prefix RVA to list + void add_lock_prefix_rva(uint32_t rva); + //Clears Lock Prefix list + void clear_lock_prefix_list(); + + //Sets the date and time stamp value + void set_time_stamp(uint32_t time_stamp); + //Sets major version number + void set_major_version(uint16_t major_version); + //Sets minor version number + void set_minor_version(uint16_t minor_version); + //Sets clear global flags + void set_global_flags_clear(uint32_t global_flags_clear); + //Sets set global flags + void set_global_flags_set(uint32_t global_flags_set); + //Sets critical section default timeout + void set_critical_section_default_timeout(uint32_t critical_section_default_timeout); + //Sets the size of the minimum block that + //must be freed before it is freed (de-committed), in bytes + void set_decommit_free_block_threshold(uint64_t decommit_free_block_threshold); + //Sets the size of the minimum total memory + //that must be freed in the process heap before it is freed (de-committed), in bytes + void set_decommit_total_free_threshold(uint64_t decommit_total_free_threshold); + //Sets VA of a list of addresses where the LOCK prefix is used + //If you rebuild this list, VA will be re-assigned automatically + void set_lock_prefix_table_va(uint64_t lock_prefix_table_va); + //Sets the maximum allocation size, in bytes + void set_max_allocation_size(uint64_t max_allocation_size); + //Sets the maximum block size that can be allocated from heap segments, in bytes + void set_virtual_memory_threshold(uint64_t virtual_memory_threshold); + //Sets process affinity mask + void set_process_affinity_mask(uint64_t process_affinity_mask); + //Sets process heap flags + void set_process_heap_flags(uint32_t process_heap_flags); + //Sets service pack version (CSDVersion) + void set_service_pack_version(uint16_t service_pack_version); + //Sets VA of edit list (reserved by system) + void set_edit_list_va(uint64_t edit_list_va); + //Sets a pointer to a cookie that is used by Visual C++ or GS implementation + void set_security_cookie_va(uint64_t security_cookie_va); + //Sets VA of the sorted table of RVAs of each valid, unique handler in the image + //If you rebuild this list, VA will be re-assigned automatically + void set_se_handler_table_va(uint64_t se_handler_table_va); + + //Returns SE Handler RVA list + se_handler_list& get_se_handler_rvas(); + + //Returns Lock Prefix RVA list + lock_prefix_rva_list& get_lock_prefix_rvas(); + +private: + uint32_t time_stamp_; + uint16_t major_version_, minor_version_; + uint32_t global_flags_clear_, global_flags_set_; + uint32_t critical_section_default_timeout_; + uint64_t decommit_free_block_threshold_, decommit_total_free_threshold_; + uint64_t lock_prefix_table_va_; + uint64_t max_allocation_size_; + uint64_t virtual_memory_threshold_; + uint64_t process_affinity_mask_; + uint32_t process_heap_flags_; + uint16_t service_pack_version_; + uint64_t edit_list_va_; + uint64_t security_cookie_va_; + uint64_t se_handler_table_va_; + uint64_t se_handler_count_; + + se_handler_list se_handlers_; + lock_prefix_rva_list lock_prefixes_; +}; + +//Returns image config info +//If image does not have config info, throws an exception +const image_config_info get_image_config(const pe_base& pe); + +template<typename PEClassType> +const image_config_info get_image_config_base(const pe_base& pe); + + +//Image config directory rebuilder +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//If write_se_handlers = true, SE Handlers list will be written just after image config directory structure +//If write_lock_prefixes = true, Lock Prefixes address list will be written just after image config directory structure +const image_directory rebuild_image_config(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_image_config_base(pe_base& pe, const image_config_info& info, section& image_config_section, uint32_t offset_from_section_start = 0, bool write_se_handlers = true, bool write_lock_prefixes = true, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_properties.cpp b/tools/pe_bliss/pe_properties.cpp new file mode 100644 index 0000000000..8d1c2eac43 --- /dev/null +++ b/tools/pe_bliss/pe_properties.cpp @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* 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 "pe_properties.h" + +namespace pe_bliss +{ +//Destructor +pe_properties::~pe_properties() +{} + +//Clears PE characteristics flag +void pe_properties::clear_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() & ~flags); +} + +//Sets PE characteristics flag +void pe_properties::set_characteristics_flags(uint16_t flags) +{ + set_characteristics(get_characteristics() | flags); +} +} diff --git a/tools/pe_bliss/pe_properties.h b/tools/pe_bliss/pe_properties.h new file mode 100644 index 0000000000..1db163e8b1 --- /dev/null +++ b/tools/pe_bliss/pe_properties.h @@ -0,0 +1,236 @@ +/*************************************************************************/ +/* 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 <memory> +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_properties +{ +public: //Constructors + virtual std::auto_ptr<pe_properties> duplicate() const = 0; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem) = 0; + +public: + //Destructor + virtual ~pe_properties(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const = 0; + + //Removes directory + virtual void remove_directory(uint32_t id) = 0; + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const = 0; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const = 0; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva) = 0; + //Sets directory size (just a value of PE header, no moving occurs) + virtual void set_directory_size(uint32_t id, uint32_t size) = 0; + + //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 + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true) = 0; + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const = 0; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const = 0; + virtual uint64_t get_image_base_64() const = 0; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base) = 0; + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base) = 0; + + //Returns image entry point + virtual uint32_t get_ep() const = 0; + //Sets image entry point + virtual void set_ep(uint32_t new_ep) = 0; + + //Returns file alignment + virtual uint32_t get_file_alignment() const = 0; + //Returns section alignment + virtual uint32_t get_section_alignment() const = 0; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size) = 0; + virtual void set_heap_size_commit(uint64_t size) = 0; + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size) = 0; + virtual void set_heap_size_reserve(uint64_t size) = 0; + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size) = 0; + virtual void set_stack_size_commit(uint64_t size) = 0; + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size) = 0; + virtual void set_stack_size_reserve(uint64_t size) = 0; + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const = 0; + virtual uint64_t get_heap_size_commit_64() const = 0; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const = 0; + virtual uint64_t get_heap_size_reserve_64() const = 0; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const = 0; + virtual uint64_t get_stack_size_commit_64() const = 0; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const = 0; + virtual uint64_t get_stack_size_reserve_64() const = 0; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const = 0; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const = 0; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number) = 0; + + //Returns PE characteristics + virtual uint16_t get_characteristics() const = 0; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch) = 0; + + //Clears PE characteristics flag + void clear_characteristics_flags(uint16_t flags); + //Sets PE characteristics flag + void set_characteristics_flags(uint16_t flags); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const = 0; + + //Returns subsystem + virtual uint16_t get_subsystem() const = 0; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem) = 0; + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const = 0; + + //Returns PE signature + virtual uint32_t get_pe_signature() const = 0; + + //Returns PE magic value + virtual uint32_t get_magic() const = 0; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const = 0; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum) = 0; + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const = 0; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp) = 0; + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const = 0; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine) = 0; + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const = 0; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics) = 0; + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor) = 0; + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const = 0; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const = 0; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor) = 0; + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const = 0; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const = 0; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const = 0; + virtual uint32_t va_to_rva(uint64_t va, bool bound_check = true) const = 0; + + //Relative Virtual Address (RVA) to Virtual Address (VA) convertions + //for PE32 and PE64 respectively + virtual uint32_t rva_to_va_32(uint32_t rva) const = 0; + virtual uint64_t rva_to_va_64(uint32_t rva) const = 0; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const = 0; + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number) = 0; + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size) = 0; + //Sets size of headers + virtual void set_size_of_headers(uint32_t size) = 0; + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size) = 0; + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr() = 0; + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const = 0; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const = 0; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const = 0; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment) = 0; + //Sets base of code + virtual void set_base_of_code(uint32_t base) = 0; + //Returns base of code + virtual uint32_t get_base_of_code() const = 0; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const = 0; +}; +} diff --git a/tools/pe_bliss/pe_properties_generic.cpp b/tools/pe_bliss/pe_properties_generic.cpp new file mode 100644 index 0000000000..bcf6f2047d --- /dev/null +++ b/tools/pe_bliss/pe_properties_generic.cpp @@ -0,0 +1,645 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "pe_properties_generic.h" +#include "pe_exception.h" +#include "utils.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor +template<typename PEClassType> +std::auto_ptr<pe_properties> pe_properties_generic<PEClassType>::duplicate() const +{ + return std::auto_ptr<pe_properties>(new pe_properties_generic<PEClassType>(*this)); +} + +//Fills properly PE structures +template<typename PEClassType> +void pe_properties_generic<PEClassType>::create_pe(uint32_t section_alignment, uint16_t subsystem) +{ + memset(&nt_headers_, 0, sizeof(nt_headers_)); + nt_headers_.Signature = 0x4550; //"PE" + nt_headers_.FileHeader.Machine = 0x14C; //i386 + nt_headers_.FileHeader.SizeOfOptionalHeader = sizeof(nt_headers_.OptionalHeader); + nt_headers_.OptionalHeader.Magic = PEClassType::Id; + nt_headers_.OptionalHeader.ImageBase = 0x400000; + nt_headers_.OptionalHeader.SectionAlignment = section_alignment; + nt_headers_.OptionalHeader.FileAlignment = 0x200; + nt_headers_.OptionalHeader.SizeOfHeaders = 1024; + nt_headers_.OptionalHeader.Subsystem = subsystem; + nt_headers_.OptionalHeader.SizeOfHeapReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfHeapCommit = 0x1000; + nt_headers_.OptionalHeader.SizeOfStackReserve = 0x100000; + nt_headers_.OptionalHeader.SizeOfStackCommit = 0x1000; + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = 0x10; +} + +//Duplicate +template<typename PEClassType> +pe_properties_generic<PEClassType>::~pe_properties_generic() +{} + +//Returns true if directory exists +template<typename PEClassType> +bool pe_properties_generic<PEClassType>::directory_exists(uint32_t id) const +{ + return (nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1) >= id && + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Removes directory +template<typename PEClassType> +void pe_properties_generic<PEClassType>::remove_directory(uint32_t id) +{ + if(directory_exists(id)) + { + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = 0; + nt_headers_.OptionalHeader.DataDirectory[id].Size = 0; + + if(id == image_directory_entry_basereloc) + { + set_characteristics_flags(image_file_relocs_stripped); + set_dll_characteristics(get_dll_characteristics() & ~image_dllcharacteristics_dynamic_base); + } + else if(id == image_directory_entry_export) + { + clear_characteristics_flags(image_file_dll); + } + } +} + +//Returns directory RVA +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_rva(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress; +} + +//Returns directory size +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_rva(uint32_t id, uint32_t va) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].VirtualAddress = va; +} + +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_directory_size(uint32_t id, uint32_t size) +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + nt_headers_.OptionalHeader.DataDirectory[id].Size = size; +} + +//Returns directory size +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_directory_size(uint32_t id) const +{ + //Check if directory exists + if(nt_headers_.OptionalHeader.NumberOfRvaAndSizes <= id) + throw pe_exception("Specified directory does not exist", pe_exception::directory_does_not_exist); + + return nt_headers_.OptionalHeader.DataDirectory[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 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::strip_data_directories(uint32_t min_count, bool strip_iat_directory) +{ + int i = nt_headers_.OptionalHeader.NumberOfRvaAndSizes - 1; + + //Enumerate all data directories from the end + for(; i >= 0; i--) + { + //If directory exists, break + if(nt_headers_.OptionalHeader.DataDirectory[i].VirtualAddress && (static_cast<uint32_t>(i) != image_directory_entry_iat || !strip_iat_directory)) + break; + + if(i <= static_cast<int>(min_count) - 2) + break; + } + + if(i == image_numberof_directory_entries - 1) + return image_numberof_directory_entries; + + //Return new number of data directories + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes = i + 1; +} + +//Returns image base for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_image_base_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Returns image base for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_image_base_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.ImageBase); +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base(uint32_t base) +{ + nt_headers_.OptionalHeader.ImageBase = base; +} + +//Sets new image base +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_image_base_64(uint64_t base) +{ + nt_headers_.OptionalHeader.ImageBase = static_cast<typename PEClassType::BaseSize>(base); +} + +//Returns image entry point +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_ep() const +{ + return nt_headers_.OptionalHeader.AddressOfEntryPoint; +} + +//Sets image entry point +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_ep(uint32_t new_ep) +{ + nt_headers_.OptionalHeader.AddressOfEntryPoint = new_ep; +} + +//Returns file alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_file_alignment() const +{ + return nt_headers_.OptionalHeader.FileAlignment; +} + +//Returns section alignment +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_section_alignment() const +{ + return nt_headers_.OptionalHeader.SectionAlignment; +} + +//Sets heap size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets heap size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_heap_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeapReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size commit for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_commit(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackCommit = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Sets stack size reserve for PE32/PE64 +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_stack_size_reserve(uint64_t size) +{ + nt_headers_.OptionalHeader.SizeOfStackReserve = static_cast<typename PEClassType::BaseSize>(size); +} + +//Returns heap size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapCommit); +} + +//Returns heap size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_heap_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns heap size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_heap_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfHeapReserve); +} + +//Returns stack size commit for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_commit_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size commit for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_commit_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackCommit); +} + +//Returns stack size reserve for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_stack_size_reserve_32() const +{ + return static_cast<uint32_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns stack size reserve for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::get_stack_size_reserve_64() const +{ + return static_cast<uint64_t>(nt_headers_.OptionalHeader.SizeOfStackReserve); +} + +//Returns virtual size of image +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_image() const +{ + return nt_headers_.OptionalHeader.SizeOfImage; +} + +//Returns number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_number_of_rvas_and_sizes() const +{ + return nt_headers_.OptionalHeader.NumberOfRvaAndSizes; +} + +//Sets number of RVA and sizes (number of DATA_DIRECTORY entries) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_rvas_and_sizes(uint32_t number) +{ + nt_headers_.OptionalHeader.NumberOfRvaAndSizes = number; +} + +//Returns PE characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_characteristics() const +{ + return nt_headers_.FileHeader.Characteristics; +} + +//Returns checksum of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_checksum() const +{ + return nt_headers_.OptionalHeader.CheckSum; +} + +//Sets checksum of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_checksum(uint32_t checksum) +{ + nt_headers_.OptionalHeader.CheckSum = checksum; +} + +//Returns DLL Characteristics +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_dll_characteristics() const +{ + return nt_headers_.OptionalHeader.DllCharacteristics; +} + +//Returns timestamp of PE file from header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_time_date_stamp() const +{ + return nt_headers_.FileHeader.TimeDateStamp; +} + +//Sets timestamp of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_time_date_stamp(uint32_t timestamp) +{ + nt_headers_.FileHeader.TimeDateStamp = timestamp; +} + +//Sets DLL Characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_dll_characteristics(uint16_t characteristics) +{ + nt_headers_.OptionalHeader.DllCharacteristics = characteristics; +} + +//Returns Machine field value of PE file from header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_machine() const +{ + return nt_headers_.FileHeader.Machine; +} + +//Sets Machine field value of PE file +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_machine(uint16_t machine) +{ + nt_headers_.FileHeader.Machine = machine; +} + +//Sets PE characteristics +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_characteristics(uint16_t ch) +{ + nt_headers_.FileHeader.Characteristics = ch; +} + +//Returns size of headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_size_of_headers() const +{ + return nt_headers_.OptionalHeader.SizeOfHeaders; +} + +//Returns subsystem +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_subsystem() const +{ + return nt_headers_.OptionalHeader.Subsystem; +} + +//Sets subsystem +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem(uint16_t subsystem) +{ + nt_headers_.OptionalHeader.Subsystem = subsystem; +} + +//Returns size of optional header +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_size_of_optional_header() const +{ + return nt_headers_.FileHeader.SizeOfOptionalHeader; +} + +//Returns PE signature +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_pe_signature() const +{ + return nt_headers_.Signature; +} + +//Returns PE magic value +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_magic() const +{ + return nt_headers_.OptionalHeader.Magic; +} + +//Sets required operation system version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_os_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorOperatingSystemVersion = minor; + nt_headers_.OptionalHeader.MajorOperatingSystemVersion = major; +} + +//Returns required operation system version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_os_version() const +{ + return nt_headers_.OptionalHeader.MinorOperatingSystemVersion; +} + +//Returns required operation system version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_os_version() const +{ + return nt_headers_.OptionalHeader.MajorOperatingSystemVersion; +} + +//Sets required subsystem version +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_subsystem_version(uint16_t major, uint16_t minor) +{ + nt_headers_.OptionalHeader.MinorSubsystemVersion = minor; + nt_headers_.OptionalHeader.MajorSubsystemVersion = major; +} + +//Returns required subsystem version (minor word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_minor_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MinorSubsystemVersion; +} + +//Returns required subsystem version (major word) +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_major_subsystem_version() const +{ + return nt_headers_.OptionalHeader.MajorSubsystemVersion; +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint32_t va, bool bound_check) const +{ + if(bound_check && static_cast<uint64_t>(va) - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Virtual Address (VA) to Relative Virtual Address (RVA) convertions for PE32/PE64 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::va_to_rva(uint64_t va, bool bound_check) const +{ + if(bound_check && va - nt_headers_.OptionalHeader.ImageBase > pe_utils::max_dword) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(va - nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32 +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::rva_to_va_32(uint32_t rva) const +{ + if(!pe_utils::is_sum_safe(rva, static_cast<uint32_t>(nt_headers_.OptionalHeader.ImageBase))) + throw pe_exception("Incorrect address conversion", pe_exception::incorrect_address_conversion); + + return static_cast<uint32_t>(rva + nt_headers_.OptionalHeader.ImageBase); +} + +//Relative Virtual Address (RVA) to Virtual Address (VA) convertions for PE32/PE64 +template<typename PEClassType> +uint64_t pe_properties_generic<PEClassType>::rva_to_va_64(uint32_t rva) const +{ + return static_cast<uint64_t>(rva) + nt_headers_.OptionalHeader.ImageBase; +} + +//Returns number of sections +template<typename PEClassType> +uint16_t pe_properties_generic<PEClassType>::get_number_of_sections() const +{ + return nt_headers_.FileHeader.NumberOfSections; +} + +//Sets number of sections +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_number_of_sections(uint16_t number) +{ + nt_headers_.FileHeader.NumberOfSections = number; +} + +//Sets virtual size of image +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_image(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfImage = size; +} + +//Sets size of headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_headers(uint32_t size) +{ + nt_headers_.OptionalHeader.SizeOfHeaders = size; +} + +//Sets size of optional headers +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_size_of_optional_header(uint16_t size) +{ + nt_headers_.FileHeader.SizeOfOptionalHeader = size; +} + +//Returns nt headers data pointer +template<typename PEClassType> +char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() +{ + return reinterpret_cast<char*>(&nt_headers_); +} + +//Returns nt headers data pointer +template<typename PEClassType> +const char* pe_properties_generic<PEClassType>::get_nt_headers_ptr() const +{ + return reinterpret_cast<const char*>(&nt_headers_); +} + +//Returns size of NT header +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_nt_header() const +{ + return sizeof(typename PEClassType::NtHeaders); +} + +//Returns size of optional headers +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_sizeof_opt_headers() const +{ + return sizeof(typename PEClassType::OptHeaders); +} + +//Sets file alignment (no checks) +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_file_alignment_unchecked(uint32_t alignment) +{ + nt_headers_.OptionalHeader.FileAlignment = alignment; +} + +//Sets base of code +template<typename PEClassType> +void pe_properties_generic<PEClassType>::set_base_of_code(uint32_t base) +{ + nt_headers_.OptionalHeader.BaseOfCode = base; +} + +//Returns base of code +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_base_of_code() const +{ + return nt_headers_.OptionalHeader.BaseOfCode; +} + +//Returns needed PE magic for PE or PE+ (from template parameters) +template<typename PEClassType> +uint32_t pe_properties_generic<PEClassType>::get_needed_magic() const +{ + return PEClassType::Id; +} + +//Returns PE type of this image +template<typename PEClassType> +pe_type pe_properties_generic<PEClassType>::get_pe_type() const +{ + return PEClassType::Id == image_nt_optional_hdr32_magic ? pe_type_32 : pe_type_64; +} + +//Two used instantiations for PE32 (PE) and PE64 (PE+) +template class pe_properties_generic<pe_types_class_32>; +template class pe_properties_generic<pe_types_class_64>; +} diff --git a/tools/pe_bliss/pe_properties_generic.h b/tools/pe_bliss/pe_properties_generic.h new file mode 100644 index 0000000000..4ff906803c --- /dev/null +++ b/tools/pe_bliss/pe_properties_generic.h @@ -0,0 +1,277 @@ +/*************************************************************************/ +/* 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 "pe_properties.h" + +namespace pe_bliss +{ +//Helper class to reduce code size and ease its editing +template< + typename NtHeadersType, + typename OptHeadersType, + uint16_t IdVal, + typename BaseSizeType, + BaseSizeType ImportSnapFlagVal, + typename TLSStructType, + typename ConfigStructType> +class pe_types +{ +public: + typedef NtHeadersType NtHeaders; //NT HEADERS type + typedef OptHeadersType OptHeaders; //NT OPTIONAL HEADER type + typedef BaseSizeType BaseSize; //Base size of different values: DWORD or ULONGLONG + typedef TLSStructType TLSStruct; //TLS structure type + typedef ConfigStructType ConfigStruct; //Configuration structure type + + static const uint16_t Id = IdVal; //Magic of PE or PE+ + static const BaseSize ImportSnapFlag = ImportSnapFlagVal; //Import snap flag value +}; + +//Portable Executable derived class for PE and PE+ +//Describes PE/PE+ dependent things +template<typename PEClassType> +class pe_properties_generic : public pe_properties +{ +public: //Constructor + virtual std::auto_ptr<pe_properties> duplicate() const; + + //Fills properly PE structures + virtual void create_pe(uint32_t section_alignment, uint16_t subsystem); + +public: + //Destructor + virtual ~pe_properties_generic(); + + +public: //DIRECTORIES + //Returns true if directory exists + virtual bool directory_exists(uint32_t id) const; + + //Removes directory + virtual void remove_directory(uint32_t id); + + //Returns directory RVA + virtual uint32_t get_directory_rva(uint32_t id) const; + //Returns directory size + virtual uint32_t get_directory_size(uint32_t id) const; + + //Sets directory RVA (just a value of PE header, no moving occurs) + virtual void set_directory_rva(uint32_t id, uint32_t rva); + //Sets directory size (just a value of PE header, no moving occurs) + virtual 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 + virtual uint32_t strip_data_directories(uint32_t min_count = 1, bool strip_iat_directory = true); + + +public: //IMAGE + //Returns PE type of this image + virtual pe_type get_pe_type() const; + + +public: //PE HEADER + //Returns image base for PE32 and PE64 respectively + virtual uint32_t get_image_base_32() const; + virtual uint64_t get_image_base_64() const; + + //Sets new image base for PE32 + virtual void set_image_base(uint32_t base); + //Sets new image base for PE32/PE+ + virtual void set_image_base_64(uint64_t base); + + //Returns image entry point + virtual uint32_t get_ep() const; + //Sets image entry point + virtual void set_ep(uint32_t new_ep); + + //Returns file alignment + virtual uint32_t get_file_alignment() const; + //Returns section alignment + virtual uint32_t get_section_alignment() const; + + //Sets heap size commit for PE32 and PE64 respectively + virtual void set_heap_size_commit(uint32_t size); + virtual void set_heap_size_commit(uint64_t size); + //Sets heap size reserve for PE32 and PE64 respectively + virtual void set_heap_size_reserve(uint32_t size); + virtual void set_heap_size_reserve(uint64_t size); + //Sets stack size commit for PE32 and PE64 respectively + virtual void set_stack_size_commit(uint32_t size); + virtual void set_stack_size_commit(uint64_t size); + //Sets stack size reserve for PE32 and PE64 respectively + virtual void set_stack_size_reserve(uint32_t size); + virtual void set_stack_size_reserve(uint64_t size); + + //Returns heap size commit for PE32 and PE64 respectively + virtual uint32_t get_heap_size_commit_32() const; + virtual uint64_t get_heap_size_commit_64() const; + //Returns heap size reserve for PE32 and PE64 respectively + virtual uint32_t get_heap_size_reserve_32() const; + virtual uint64_t get_heap_size_reserve_64() const; + //Returns stack size commit for PE32 and PE64 respectively + virtual uint32_t get_stack_size_commit_32() const; + virtual uint64_t get_stack_size_commit_64() const; + //Returns stack size reserve for PE32 and PE64 respectively + virtual uint32_t get_stack_size_reserve_32() const; + virtual uint64_t get_stack_size_reserve_64() const; + + //Returns virtual size of image + virtual uint32_t get_size_of_image() const; + + //Returns number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual uint32_t get_number_of_rvas_and_sizes() const; + //Sets number of RVA and sizes (number of DATA_DIRECTORY entries) + virtual void set_number_of_rvas_and_sizes(uint32_t number); + + //Returns PE characteristics + virtual uint16_t get_characteristics() const; + //Sets PE characteristics + virtual void set_characteristics(uint16_t ch); + + //Returns size of headers + virtual uint32_t get_size_of_headers() const; + + //Returns subsystem + virtual uint16_t get_subsystem() const; + + //Sets subsystem + virtual void set_subsystem(uint16_t subsystem); + + //Returns size of optional header + virtual uint16_t get_size_of_optional_header() const; + + //Returns PE signature + virtual uint32_t get_pe_signature() const; + + //Returns PE magic value + virtual uint32_t get_magic() const; + + //Returns checksum of PE file from header + virtual uint32_t get_checksum() const; + + //Sets checksum of PE file + virtual void set_checksum(uint32_t checksum); + + //Returns timestamp of PE file from header + virtual uint32_t get_time_date_stamp() const; + + //Sets timestamp of PE file + virtual void set_time_date_stamp(uint32_t timestamp); + + //Returns Machine field value of PE file from header + virtual uint16_t get_machine() const; + + //Sets Machine field value of PE file + virtual void set_machine(uint16_t machine); + + //Returns DLL Characteristics + virtual uint16_t get_dll_characteristics() const; + + //Sets DLL Characteristics + virtual void set_dll_characteristics(uint16_t characteristics); + + //Sets required operation system version + virtual void set_os_version(uint16_t major, uint16_t minor); + + //Returns required operation system version (minor word) + virtual uint16_t get_minor_os_version() const; + + //Returns required operation system version (major word) + virtual uint16_t get_major_os_version() const; + + //Sets required subsystem version + virtual void set_subsystem_version(uint16_t major, uint16_t minor); + + //Returns required subsystem version (minor word) + virtual uint16_t get_minor_subsystem_version() const; + + //Returns required subsystem version (major word) + virtual uint16_t get_major_subsystem_version() const; + +public: //ADDRESS CONVERTIONS + //Virtual Address (VA) to Relative Virtual Address (RVA) convertions + //for PE32 and PE64 respectively + //bound_check checks integer overflow + virtual uint32_t va_to_rva(uint32_t va, bool bound_check = true) const; + virtual 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 + virtual uint32_t rva_to_va_32(uint32_t rva) const; + virtual uint64_t rva_to_va_64(uint32_t rva) const; + + +public: //SECTIONS + //Returns number of sections + virtual uint16_t get_number_of_sections() const; + +protected: + typename PEClassType::NtHeaders nt_headers_; //NT headers (PE32 or PE64) + +public: + //Sets number of sections + virtual void set_number_of_sections(uint16_t number); + //Sets virtual size of image + virtual void set_size_of_image(uint32_t size); + //Sets size of headers + virtual void set_size_of_headers(uint32_t size); + //Sets size of optional headers + virtual void set_size_of_optional_header(uint16_t size); + //Returns nt headers data pointer + virtual char* get_nt_headers_ptr(); + //Returns nt headers data pointer + virtual const char* get_nt_headers_ptr() const; + //Returns size of NT header + virtual uint32_t get_sizeof_nt_header() const; + //Returns size of optional headers + virtual uint32_t get_sizeof_opt_headers() const; + //Sets file alignment (no checks) + virtual void set_file_alignment_unchecked(uint32_t alignment); + //Sets base of code + virtual void set_base_of_code(uint32_t base); + //Returns base of code + virtual uint32_t get_base_of_code() const; + //Returns needed PE magic for PE or PE+ (from template parameters) + virtual uint32_t get_needed_magic() const; +}; + +//Two used typedefs for PE32 (PE) and PE64 (PE+) +typedef pe_types<pe_win::image_nt_headers32, + pe_win::image_optional_header32, + pe_win::image_nt_optional_hdr32_magic, + uint32_t, + pe_win::image_ordinal_flag32, + pe_win::image_tls_directory32, + pe_win::image_load_config_directory32> pe_types_class_32; + +typedef pe_types<pe_win::image_nt_headers64, + pe_win::image_optional_header64, + pe_win::image_nt_optional_hdr64_magic, + uint64_t, + pe_win::image_ordinal_flag64, + pe_win::image_tls_directory64, + pe_win::image_load_config_directory64> pe_types_class_64; + +typedef pe_properties_generic<pe_types_class_32> pe_properties_32; +typedef pe_properties_generic<pe_types_class_64> pe_properties_64; +} diff --git a/tools/pe_bliss/pe_rebuilder.cpp b/tools/pe_bliss/pe_rebuilder.cpp new file mode 100644 index 0000000000..faf5803b8c --- /dev/null +++ b/tools/pe_bliss/pe_rebuilder.cpp @@ -0,0 +1,214 @@ +/*************************************************************************/ +/* 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 "pe_rebuilder.h" +#include "pe_base.h" +#include "pe_structures.h" +#include "pe_exception.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Rebuilds PE image headers +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, image_dos_header& dos_header, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + dos_header = pe.get_dos_header(); + + if(strip_dos_header) + { + //Strip stub overlay + pe.strip_stub_overlay(); + //BaseOfCode NT Headers field now overlaps + //e_lfanew field, so we're acrually setting + //e_lfanew with this call + pe.set_base_of_code(8 * sizeof(uint16_t)); + } + else + { + //Set start of PE headers + dos_header.e_lfanew = sizeof(image_dos_header) + + pe_utils::align_up(static_cast<uint32_t>(pe.get_stub_overlay().size()), sizeof(uint32_t)); + } + + section_list& sections = pe.get_image_sections(); + + //Calculate pointer to section data + size_t ptr_to_section_data = (strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)) + pe.get_sizeof_nt_header() + + pe_utils::align_up(pe.get_stub_overlay().size(), sizeof(uint32_t)) + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()) + + sections.size() * sizeof(image_section_header); + + if(save_bound_import && pe.has_bound_import()) + { + //It will be aligned to DWORD, because we're aligning to DWORD everything above it + pe.set_directory_rva(image_directory_entry_bound_import, static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += pe.get_directory_size(image_directory_entry_bound_import); + } + + ptr_to_section_data = pe_utils::align_up(ptr_to_section_data, pe.get_file_alignment()); + + //Set size of headers and size of optional header + if(change_size_of_headers) + { + if(!pe.get_image_sections().empty()) + { + if(static_cast<uint32_t>(ptr_to_section_data) > (*sections.begin()).get_virtual_address()) + throw pe_exception("Headers of PE file are too long. Try to strip STUB or don't build bound import", pe_exception::cannot_rebuild_image); + } + + pe.set_size_of_headers(static_cast<uint32_t>(ptr_to_section_data)); + } + + //Set number of sections in PE header + pe.update_number_of_sections(); + + pe.update_image_size(); + + pe.set_size_of_optional_header(static_cast<uint16_t>(pe.get_sizeof_opt_headers() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes()))); + + //Recalculate pointer to raw data according to section list + for(section_list::iterator it = sections.begin(); it != sections.end(); ++it) + { + //Save section headers PointerToRawData + (*it).set_pointer_to_raw_data(static_cast<uint32_t>(ptr_to_section_data)); + ptr_to_section_data += (*it).get_aligned_raw_size(pe.get_file_alignment()); + } +} + +//Rebuild PE image and write it to "out" ostream +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + if(out.bad()) + throw pe_exception("Stream is bad", pe_exception::stream_is_bad); + + if(save_bound_import && pe.has_bound_import()) + { + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_bound_import), pe.get_directory_rva(image_directory_entry_bound_import), section_data_raw, true) + < pe.get_directory_size(image_directory_entry_bound_import)) + throw pe_exception("Incorrect bound import directory", pe_exception::incorrect_bound_import_directory); + } + + //Change ostream state + out.exceptions(std::ios::goodbit); + out.clear(); + + uint32_t original_bound_import_rva = pe.has_bound_import() ? pe.get_directory_rva(image_directory_entry_bound_import) : 0; + if(original_bound_import_rva && original_bound_import_rva > pe.get_size_of_headers()) + { + //No need to do anything with bound import directory + //if it is placed inside of any section, not headers + original_bound_import_rva = 0; + save_bound_import = false; + } + + { + image_dos_header dos_header; + + //Rebuild PE image headers + rebuild_pe(pe, dos_header, strip_dos_header, change_size_of_headers, save_bound_import); + + //Write DOS header + out.write(reinterpret_cast<const char*>(&dos_header), strip_dos_header ? 8 * sizeof(uint16_t) : sizeof(image_dos_header)); + } + + //If we have stub overlay, write it too + { + const std::string& stub = pe.get_stub_overlay(); + if(stub.size()) + { + out.write(stub.data(), stub.size()); + size_t aligned_size = pe_utils::align_up(stub.size(), sizeof(uint32_t)); + //Align PE header, which is right after rich overlay + while(aligned_size > stub.size()) + { + out.put('\0'); + --aligned_size; + } + } + } + + //Write NT headers + out.write(static_cast<const pe_base&>(pe).get_nt_headers_ptr(), pe.get_sizeof_nt_header() + - sizeof(image_data_directory) * (image_numberof_directory_entries - pe.get_number_of_rvas_and_sizes())); + + //Write section headers + const section_list& sections = pe.get_image_sections(); + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + if(it == sections.end() - 1) //If last section encountered + { + image_section_header header((*it).get_raw_header()); + header.SizeOfRawData = static_cast<uint32_t>((*it).get_raw_data().length()); //Set non-aligned actual data length for it + out.write(reinterpret_cast<const char*>(&header), sizeof(image_section_header)); + } + else + { + out.write(reinterpret_cast<const char*>(&(*it).get_raw_header()), sizeof(image_section_header)); + } + } + + //Write bound import data if requested + if(save_bound_import && pe.has_bound_import()) + { + out.write(pe.section_data_from_rva(original_bound_import_rva, section_data_raw, true), + pe.get_directory_size(image_directory_entry_bound_import)); + } + + //Write section data finally + for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) + { + const section& s = *it; + + std::streamoff wpos = out.tellp(); + + //Fill unused overlay data between sections with null bytes + for(unsigned int i = 0; i < s.get_pointer_to_raw_data() - wpos; i++) + out.put(0); + + //Write raw section data + out.write(s.get_raw_data().data(), s.get_raw_data().length()); + } +} + +//Rebuild PE image and write it to "out" file +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header, bool change_size_of_headers, bool save_bound_import) +{ + std::ofstream pe_file(out, std::ios::out | std::ios::binary | std::ios::trunc); + if(!pe_file) + { + throw pe_exception("Error in open file.", pe_exception::stream_is_bad); + } + rebuild_pe(pe, pe_file, strip_dos_header, change_size_of_headers, save_bound_import); +} + + +} diff --git a/tools/pe_bliss/pe_rebuilder.h b/tools/pe_bliss/pe_rebuilder.h new file mode 100644 index 0000000000..319807e5c9 --- /dev/null +++ b/tools/pe_bliss/pe_rebuilder.h @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* 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 <ostream> +#include <fstream> + +namespace pe_bliss +{ +class pe_base; +//Rebuilds PE image, writes resulting image to ostream "out". If strip_dos_header == true, DOS header will be stripped a little +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, std::ostream& out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true); + +//Rebuild PE image and write it to "out" file +//If strip_dos_header is true, DOS headers partially will be used for PE headers +//If change_size_of_headers == true, SizeOfHeaders will be recalculated automatically +//If save_bound_import == true, existing bound import directory will be saved correctly (because some compilers and bind.exe put it to PE headers) +void rebuild_pe(pe_base& pe, const char* out, bool strip_dos_header = false, bool change_size_of_headers = true, bool save_bound_import = true); + +} diff --git a/tools/pe_bliss/pe_relocations.cpp b/tools/pe_bliss/pe_relocations.cpp new file mode 100644 index 0000000000..d5357dd219 --- /dev/null +++ b/tools/pe_bliss/pe_relocations.cpp @@ -0,0 +1,320 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "pe_relocations.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RELOCATIONS +//Default constructor +relocation_entry::relocation_entry() + :rva_(0), type_(0) +{} + +//Constructor from relocation item (WORD) +relocation_entry::relocation_entry(uint16_t relocation_value) + :rva_(relocation_value & ((1 << 12) - 1)), type_(relocation_value >> 12) +{} + +//Constructor from relative rva and relocation type +relocation_entry::relocation_entry(uint16_t rrva, uint16_t type) + :rva_(rrva), type_(type) +{} + +//Returns RVA of relocation +uint16_t relocation_entry::get_rva() const +{ + return rva_; +} + +//Returns type of relocation +uint16_t relocation_entry::get_type() const +{ + return type_; +} + +//Sets RVA of relocation +void relocation_entry::set_rva(uint16_t rva) +{ + rva_ = rva; +} + +//Sets type of relocation +void relocation_entry::set_type(uint16_t type) +{ + type_ = type; +} + +//Returns relocation item (rrva + type) +uint16_t relocation_entry::get_item() const +{ + return rva_ | (type_ << 12); +} + +//Sets relocation item (rrva + type) +void relocation_entry::set_item(uint16_t item) +{ + rva_ = item & ((1 << 12) - 1); + type_ = item >> 12; +} + +//Returns relocation list +const relocation_table::relocation_list& relocation_table::get_relocations() const +{ + return relocations_; +} + +//Adds relocation to table +void relocation_table::add_relocation(const relocation_entry& entry) +{ + relocations_.push_back(entry); +} + +//Default constructor +relocation_table::relocation_table() + :rva_(0) +{} + +//Constructor from RVA of relocation table +relocation_table::relocation_table(uint32_t rva) + :rva_(rva) +{} + +//Returns RVA of block +uint32_t relocation_table::get_rva() const +{ + return rva_; +} + +//Sets RVA of block +void relocation_table::set_rva(uint32_t rva) +{ + rva_ = rva; +} + +//Returns changeable relocation list +relocation_table::relocation_list& relocation_table::get_relocations() +{ + return relocations_; +} + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries) +{ + relocation_table_list ret; + + //If image does not have relocations + if(!pe.has_reloc()) + return ret; + + //Check the length in bytes of the section containing relocation directory + if(pe.section_data_length_from_rva(pe.get_directory_rva(image_directory_entry_basereloc), + pe.get_directory_rva(image_directory_entry_basereloc), section_data_virtual, true) + < sizeof(image_base_relocation)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long current_pos = pe.get_directory_rva(image_directory_entry_basereloc); + //First IMAGE_BASE_RELOCATION table + image_base_relocation reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + + if(reloc_table.SizeOfBlock % 2) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + unsigned long reloc_size = pe.get_directory_size(image_directory_entry_basereloc); + unsigned long read_size = 0; + + //reloc_table.VirtualAddress is not checked (not so important) + while(reloc_table.SizeOfBlock && read_size < reloc_size) + { + //Create relocation table + relocation_table table; + //Save RVA + table.set_rva(reloc_table.VirtualAddress); + + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + //List all relocations + for(unsigned long i = sizeof(image_base_relocation); i < reloc_table.SizeOfBlock; i += sizeof(uint16_t)) + { + relocation_entry entry(pe.section_data_from_rva<uint16_t>(current_pos + i, section_data_virtual, true)); + if(list_absolute_entries || entry.get_type() != image_rel_based_absolute) + table.add_relocation(entry); + } + + //Save table + ret.push_back(table); + + //Go to next relocation block + if(!pe_utils::is_sum_safe(current_pos, reloc_table.SizeOfBlock)) + throw pe_exception("Incorrect relocation directory", pe_exception::incorrect_relocation_directory); + + current_pos += reloc_table.SizeOfBlock; + read_size += reloc_table.SizeOfBlock; + reloc_table = pe.section_data_from_rva<image_base_relocation>(current_pos, section_data_virtual, true); + } + + return ret; +} + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that reloc_section is attached to this PE image + if(!pe.section_attached(reloc_section)) + throw pe_exception("Relocations section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t current_reloc_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + + uint32_t needed_size = current_reloc_data_pos - offset_from_section_start; //Calculate needed size for relocation tables + uint32_t size_delta = needed_size; + + uint32_t start_reloc_pos = current_reloc_data_pos; + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + needed_size += static_cast<uint32_t>((*it).get_relocations().size() * sizeof(uint16_t) /* relocations */ + sizeof(image_base_relocation) /* table header */); + //End of each table will be DWORD-aligned + if((start_reloc_pos + needed_size - size_delta) % sizeof(uint32_t)) + needed_size += sizeof(uint16_t); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + } + + //Check if reloc_section is last one. If it's not, check if there's enough place for relocations data + if(&reloc_section != &*(pe.get_image_sections().end() - 1) && + (reloc_section.empty() || pe_utils::align_up(reloc_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + current_reloc_data_pos)) + throw pe_exception("Insufficient space for relocations directory", pe_exception::insufficient_space); + + std::string& raw_data = reloc_section.get_raw_data(); + + //This will be done only if reloc_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + current_reloc_data_pos) + raw_data.resize(needed_size + current_reloc_data_pos); //Expand section raw data + + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = relocs.begin(); it != relocs.end(); ++it) + { + //Create relocation table header + image_base_relocation reloc; + reloc.VirtualAddress = (*it).get_rva(); + const relocation_table::relocation_list& reloc_list = (*it).get_relocations(); + reloc.SizeOfBlock = static_cast<uint32_t>(sizeof(image_base_relocation) + sizeof(uint16_t) * reloc_list.size()); + if((reloc_list.size() * sizeof(uint16_t)) % sizeof(uint32_t)) //If we must align end of relocation table + reloc.SizeOfBlock += sizeof(uint16_t); + + memcpy(&raw_data[current_reloc_data_pos], &reloc, sizeof(reloc)); + current_reloc_data_pos += sizeof(reloc); + + //Enumerate relocations in table + for(relocation_table::relocation_list::const_iterator r = reloc_list.begin(); r != reloc_list.end(); ++r) + { + //Save relocations + uint16_t reloc_value = (*r).get_item(); + memcpy(&raw_data[current_reloc_data_pos], &reloc_value, sizeof(reloc_value)); + current_reloc_data_pos += sizeof(reloc_value); + } + + if(current_reloc_data_pos % sizeof(uint32_t)) //If end of table is not DWORD-aligned + { + memset(&raw_data[current_reloc_data_pos], 0, sizeof(uint16_t)); //Align it with IMAGE_REL_BASED_ABSOLUTE relocation + current_reloc_data_pos += sizeof(uint16_t); + } + } + + image_directory ret(pe.rva_from_section_offset(reloc_section, start_reloc_pos), needed_size - size_delta); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(reloc_section, auto_strip_last_section); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_basereloc, ret.get_rva()); + pe.set_directory_size(image_directory_entry_basereloc, ret.get_size()); + + pe.clear_characteristics_flags(image_file_relocs_stripped); + pe.set_dll_characteristics(pe.get_dll_characteristics() | image_dllcharacteristics_dynamic_base); + } + + return ret; +} + +//Recalculates image base with the help of relocation tables +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + pe.get_pe_type() == pe_type_32 + ? rebase_image_base<pe_types_class_32>(pe, tables, new_base) + : rebase_image_base<pe_types_class_64>(pe, tables, new_base); +} + +//RELOCATIONS +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base) +{ + //Get current image base value + typename PEClassType::BaseSize image_base; + pe.get_image_base(image_base); + + //ImageBase difference + typename PEClassType::BaseSize base_rel = static_cast<typename PEClassType::BaseSize>(static_cast<int64_t>(new_base) - image_base); + + //We need to fix addresses from relocation tables + //Enumerate relocation tables + for(relocation_table_list::const_iterator it = tables.begin(); it != tables.end(); ++it) + { + const relocation_table::relocation_list& relocs = (*it).get_relocations(); + + uint32_t base_rva = (*it).get_rva(); + + //Enumerate relocations + for(relocation_table::relocation_list::const_iterator rel = relocs.begin(); rel != relocs.end(); ++rel) + { + //Skip ABSOLUTE entries + if((*rel).get_type() == pe_win::image_rel_based_absolute) + continue; + + //Recalculate value by RVA and rewrite it + uint32_t current_rva = base_rva + (*rel).get_rva(); + typename PEClassType::BaseSize value = pe.section_data_from_rva<typename PEClassType::BaseSize>(current_rva, section_data_raw, true); + value += base_rel; + memcpy(pe.section_data_from_rva(current_rva, true), &value, sizeof(value)); + } + } + + //Finally, save new image base + pe.set_image_base_64(new_base); +} +} diff --git a/tools/pe_bliss/pe_relocations.h b/tools/pe_bliss/pe_relocations.h new file mode 100644 index 0000000000..1bc8b2a405 --- /dev/null +++ b/tools/pe_bliss/pe_relocations.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* 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 <vector> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing relocation entry +//RVA of relocation is not actually RVA, but +//(real RVA) - (RVA of table) +class relocation_entry +{ +public: + //Default constructor + relocation_entry(); + //Constructor from relocation item (WORD) + explicit relocation_entry(uint16_t relocation_value); + //Constructor from relative rva and relocation type + relocation_entry(uint16_t rrva, uint16_t type); + + //Returns RVA of relocation (actually, relative RVA from relocation table RVA) + uint16_t get_rva() const; + //Returns type of relocation + uint16_t get_type() const; + + //Returns relocation item (rrva + type) + uint16_t get_item() const; + +public: //Setters do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Sets RVA of relocation (actually, relative RVA from relocation table RVA) + void set_rva(uint16_t rva); + //Sets type of relocation + void set_type(uint16_t type); + + //Sets relocation item (rrva + type) + void set_item(uint16_t item); + +private: + uint16_t rva_; + uint16_t type_; +}; + +//Class representing relocation table +class relocation_table +{ +public: + typedef std::vector<relocation_entry> relocation_list; + +public: + //Default constructor + relocation_table(); + //Constructor from RVA of relocation table + explicit relocation_table(uint32_t rva); + + //Returns relocation list + const relocation_list& get_relocations() const; + //Returns RVA of block + uint32_t get_rva() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild relocations using rebuild_relocations() + + //Adds relocation to table + void add_relocation(const relocation_entry& entry); + //Returns changeable relocation list + relocation_list& get_relocations(); + //Sets RVA of block + void set_rva(uint32_t rva); + +private: + uint32_t rva_; + relocation_list relocations_; +}; + +typedef std::vector<relocation_table> relocation_table_list; + +//Get relocation list of pe file, supports one-word sized relocations only +//If list_absolute_entries = true, IMAGE_REL_BASED_ABSOLUTE will be listed +const relocation_table_list get_relocations(const pe_base& pe, bool list_absolute_entries = false); + +//Simple relocations rebuilder +//To keep PE file working, don't remove any of existing relocations in +//relocation_table_list returned by a call to get_relocations() function +//auto_strip_last_section - if true and relocations are placed in the last section, it will be automatically stripped +//offset_from_section_start - offset from the beginning of reloc_section, where relocations data will be situated +//If save_to_pe_header is true, PE header will be modified automatically +const image_directory rebuild_relocations(pe_base& pe, const relocation_table_list& relocs, section& reloc_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +//Recalculates image base with the help of relocation tables +//Recalculates VAs of DWORDS/QWORDS in image according to relocations +//Notice: if you move some critical structures like TLS, image relocations will not fix new +//positions of TLS VAs. Instead, some bytes that now doesn't belong to TLS will be fixed. +//It is recommended to rebase image in the very beginning and move all structures afterwards. +void rebase_image(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); + +template<typename PEClassType> +void rebase_image_base(pe_base& pe, const relocation_table_list& tables, uint64_t new_base); +} diff --git a/tools/pe_bliss/pe_resource_manager.cpp b/tools/pe_bliss/pe_resource_manager.cpp new file mode 100644 index 0000000000..0ee7840ff0 --- /dev/null +++ b/tools/pe_bliss/pe_resource_manager.cpp @@ -0,0 +1,286 @@ +/*************************************************************************/ +/* 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 <algorithm> +#include <sstream> +#include <iomanip> +#include <math.h> +#include <string.h> +#include "pe_resource_manager.h" +#include "resource_internal.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor from root resource directory +pe_resource_manager::pe_resource_manager(resource_directory& root_directory) + :pe_resource_viewer(root_directory), root_dir_edit_(root_directory) +{} + +resource_directory& pe_resource_manager::get_root_directory() +{ + return root_dir_edit_; +} + +//Removes all resources of given type or root name +//If there's more than one directory entry of a given type, only the +//first one will be deleted (that's an unusual situation) +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource_type(resource_type type) +{ + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)); + if(it != entries.end()) + { + //Remove it, if found + entries.erase(it); + return true; + } + + return false; +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name) +{ + //Search for resource type + resource_directory::entry_list& entries = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)); + if(it != entries.end()) + { + //Remove it, if found + entries.erase(it); + return true; + } + + return false; +} + +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder) +{ + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) + { + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Erase resource, if found + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); + + return true; + } + } + + return false; +} + +//Removes all resource languages by resource type/root name and name +//Deletes only one entry of given type and name +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name)); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name)); +} + +//Removes all resource languages by resource type/root name and ID +//Deletes only one entry of given type and ID +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id)); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id)); +} + +//Helper to remove resource +bool pe_resource_manager::remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language) +{ + //Search for resource type + resource_directory::entry_list& entries_type = root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it_type = std::find_if(entries_type.begin(), entries_type.end(), root_finder); + if(it_type != entries_type.end()) + { + //Search for resource name/ID with "finder" + resource_directory::entry_list& entries_name = (*it_type).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_name = std::find_if(entries_name.begin(), entries_name.end(), finder); + if(it_name != entries_name.end()) + { + //Search for resource language + resource_directory::entry_list& entries_lang = (*it_name).get_resource_directory().get_entry_list(); + resource_directory::entry_list::iterator it_lang = std::find_if(entries_lang.begin(), entries_lang.end(), resource_directory::id_entry_finder(language)); + if(it_lang != entries_lang.end()) + { + //Erase resource, if found + entries_lang.erase(it_lang); + if(entries_lang.empty()) + { + entries_name.erase(it_name); + if(entries_name.empty()) + entries_type.erase(it_type); + } + + return true; + } + } + } + + return false; +} + +//Removes resource language by resource type/root name and name +//Deletes only one entry of given type, name and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, const std::wstring& name, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(name), language); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(name), language); +} + +//Removes recource language by resource type/root name and ID +//Deletes only one entry of given type, ID and language +//Returns true if resource was deleted +bool pe_resource_manager::remove_resource(resource_type type, uint32_t id, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(type), resource_directory::entry_finder(id), language); +} + +bool pe_resource_manager::remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language) +{ + return remove_resource(resource_directory::entry_finder(root_name), resource_directory::entry_finder(id), language); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_type type, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_type_entry; + new_type_entry.set_id(type); + + add_resource(data, new_type_entry, resource_directory::entry_finder(type), new_entry, finder, language, codepage, timestamp); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_type_entry; + new_type_entry.set_name(root_name); + + add_resource(data, new_type_entry, resource_directory::entry_finder(root_name), new_entry, finder, language, codepage, timestamp); +} + +//Helper to add/replace resource +void pe_resource_manager::add_resource(const std::string& data, resource_directory_entry& new_root_entry, const resource_directory::entry_finder& root_finder, resource_directory_entry& new_entry, const resource_directory::entry_finder& finder, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Search for resource type + resource_directory::entry_list* entries = &root_dir_edit_.get_entry_list(); + resource_directory::entry_list::iterator it = std::find_if(entries->begin(), entries->end(), root_finder); + if(it == entries->end()) + { + //Add resource type directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_root_entry.add_resource_directory(dir); + entries->push_back(new_root_entry); + it = entries->end() - 1; + } + + //Search for resource name/ID directory with "finder" + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), finder); + if(it == entries->end()) + { + //Add resource name/ID directory, if it was not found + resource_directory dir; + dir.set_timestamp(timestamp); + new_entry.add_resource_directory(dir); + entries->push_back(new_entry); + it = entries->end() - 1; + } + + //Search for data resource entry by language + entries = &(*it).get_resource_directory().get_entry_list(); + it = std::find_if(entries->begin(), entries->end(), resource_directory::id_entry_finder(language)); + if(it != entries->end()) + entries->erase(it); //Erase it, if found + + //Add new data entry + resource_directory_entry new_dir_data_entry; + resource_data_entry data_dir(data, codepage); + new_dir_data_entry.add_data_entry(data_dir); + new_dir_data_entry.set_id(language); + entries->push_back(new_dir_data_entry); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_name(name); + + add_resource(data, type, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_name(name); + + add_resource(data, root_name, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_id(id); + + add_resource(data, type, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} + +//Adds resource. If resource already exists, replaces it +void pe_resource_manager::add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_entry; + new_entry.set_id(id); + + add_resource(data, root_name, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} +} diff --git a/tools/pe_bliss/pe_resource_manager.h b/tools/pe_bliss/pe_resource_manager.h new file mode 100644 index 0000000000..85d7f44a8a --- /dev/null +++ b/tools/pe_bliss/pe_resource_manager.h @@ -0,0 +1,113 @@ +/*************************************************************************/ +/* 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 <map> +#include <sstream> +#include <string> +#include <memory> +#include "pe_base.h" +#include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "file_version_info.h" +#include "pe_resource_viewer.h" +#include "resource_data_info.h" + +namespace pe_bliss +{ +//Derived class to edit PE resources +class pe_resource_manager : public pe_resource_viewer +{ +public: + //Constructor from root resource directory + explicit pe_resource_manager(resource_directory& root_directory); + + resource_directory& get_root_directory(); + +public: //Resource editing + //Removes all resources of given type or root name + //If there's more than one directory entry of a given type, only the + //first one will be deleted (that's an unusual situation) + //Returns true if resource was deleted + bool remove_resource_type(resource_type type); + bool remove_resource(const std::wstring& root_name); + + //Removes all resource languages by resource type/root name and name + //Deletes only one entry of given type and name + //Returns true if resource was deleted + bool remove_resource(resource_type type, const std::wstring& name); + bool remove_resource(const std::wstring& root_name, const std::wstring& name); + //Removes all resource languages by resource type/root name and ID + //Deletes only one entry of given type and ID + //Returns true if resource was deleted + bool remove_resource(resource_type type, uint32_t id); + bool remove_resource(const std::wstring& root_name, uint32_t id); + + //Removes resource language by resource type/root name and name + //Deletes only one entry of given type, name and language + //Returns true if resource was deleted + bool remove_resource(resource_type type, const std::wstring& name, uint32_t language); + bool remove_resource(const std::wstring& root_name, const std::wstring& name, uint32_t language); + //Removes recource language by resource type/root name and ID + //Deletes only one entry of given type, ID and language + //Returns true if resource was deleted + bool remove_resource(resource_type type, uint32_t id, uint32_t language); + bool remove_resource(const std::wstring& root_name, uint32_t id, uint32_t language); + + //Adds resource. If resource already exists, replaces it + //timestamp will be used for directories that will be added + void add_resource(const std::string& data, resource_type type, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_resource(const std::string& data, const std::wstring& root_name, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + //Adds resource. If resource already exists, replaces it + //timestamp will be used for directories that will be added + void add_resource(const std::string& data, resource_type type, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_resource(const std::string& data, const std::wstring& root_name, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + +public: + //Helpers to add/replace resource + void add_resource(const std::string& data, resource_type type, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, const std::wstring& root_name, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + + void add_resource(const std::string& data, resource_directory_entry& new_root_entry, + const resource_directory::entry_finder& root_finder, + resource_directory_entry& new_entry, + const resource_directory::entry_finder& finder, + uint32_t language, uint32_t codepage, uint32_t timestamp); + +private: + //Root resource directory. We're not copying it, because it might be heavy + resource_directory& root_dir_edit_; + + //Helper to remove resource + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder); + + //Helper to remove resource + bool remove_resource(const resource_directory::entry_finder& root_finder, const resource_directory::entry_finder& finder, uint32_t language); +}; +} diff --git a/tools/pe_bliss/pe_resource_viewer.cpp b/tools/pe_bliss/pe_resource_viewer.cpp new file mode 100644 index 0000000000..712cc28d9b --- /dev/null +++ b/tools/pe_bliss/pe_resource_viewer.cpp @@ -0,0 +1,382 @@ +/*************************************************************************/ +/* 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 <algorithm> +#include <cmath> +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Constructor from root resource_directory +pe_resource_viewer::pe_resource_viewer(const resource_directory& root_directory) + :root_dir_(root_directory) +{} + +const resource_directory& pe_resource_viewer::get_root_directory() const +{ + return root_dir_; +} + +//Finder helpers +bool pe_resource_viewer::has_name::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named(); +} + +bool pe_resource_viewer::has_id::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named(); +} + +//Lists resource types existing in PE file (non-named only) +const pe_resource_viewer::resource_type_list pe_resource_viewer::list_resource_types() const +{ + resource_type_list ret; + + //Get root directory entries list + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Returns true if resource type exists +bool pe_resource_viewer::resource_exists(resource_type type) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::id_entry_finder(type)) != entries.end(); +} + +//Returns true if resource name exists +bool pe_resource_viewer::resource_exists(const std::wstring& root_name) const +{ + const resource_directory::entry_list& entries = root_dir_.get_entry_list(); + return std::find_if(entries.begin(), entries.end(), resource_directory::name_entry_finder(root_name)) != entries.end(); +} + +//Helper function to get name list from entry list +const pe_resource_viewer::resource_name_list pe_resource_viewer::get_name_list(const resource_directory::entry_list& entries) +{ + resource_name_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all named items + if((*it).is_named()) + ret.push_back((*it).get_name()); + } + + return ret; +} + +//Helper function to get ID list from entry list +const pe_resource_viewer::resource_id_list pe_resource_viewer::get_id_list(const resource_directory::entry_list& entries) +{ + resource_id_list ret; + + for(resource_directory::entry_list::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + //List all non-named items + if(!(*it).is_named()) + ret.push_back((*it).get_id()); + } + + return ret; +} + +//Lists resource names existing in PE file by resource type +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(resource_type type) const +{ + return get_name_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource names existing in PE file by resource name +const pe_resource_viewer::resource_name_list pe_resource_viewer::list_resource_names(const std::wstring& root_name) const +{ + return get_name_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource type +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(resource_type type) const +{ + return get_id_list(root_dir_.entry_by_id(type).get_resource_directory().get_entry_list()); +} + +//Lists resource IDs existing in PE file by resource name +const pe_resource_viewer::resource_id_list pe_resource_viewer::list_resource_ids(const std::wstring& root_name) const +{ + return get_id_list(root_dir_.entry_by_name(root_name).get_resource_directory().get_entry_list()); +} + +//Returns resource count by type +unsigned long pe_resource_viewer::get_resource_count(resource_type type) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns resource count by name +unsigned long pe_resource_viewer::get_resource_count(const std::wstring& root_name) const +{ + return static_cast<unsigned long>( + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .get_entry_list() + .size()); +} + +//Returns language count of resource by resource type and name +unsigned long pe_resource_viewer::get_language_count(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource names +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource type and ID +unsigned long pe_resource_viewer::get_language_count(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Returns language count of resource by resource name and ID +unsigned long pe_resource_viewer::get_language_count(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return static_cast<unsigned long>(std::count_if(entries.begin(), entries.end(), has_id())); +} + +//Lists resource languages by resource type and name +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource names +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, const std::wstring& name) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource type and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(resource_type type, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Lists resource languages by resource name and ID +const pe_resource_viewer::resource_language_list pe_resource_viewer::list_resource_languages(const std::wstring& root_name, uint32_t id) const +{ + const resource_directory::entry_list& entries = + root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + return get_id_list(entries); +} + +//Returns raw resource data by type, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and language +const resource_data_info pe_resource_viewer::get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and language +const resource_data_info pe_resource_viewer::get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const +{ + return resource_data_info(root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry()); //Data directory +} + +//Returns raw resource data by type, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, name and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_name(name) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by type, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_id(type) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} + +//Returns raw resource data by root name, ID and index in language directory (instead of language) +const resource_data_info pe_resource_viewer::get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index) const +{ + const resource_directory::entry_list& entries = root_dir_ //Type directory + .entry_by_name(root_name) + .get_resource_directory() //Name/ID directory + .entry_by_id(id) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return resource_data_info(entries.at(index).get_data_entry()); //Data directory +} +} diff --git a/tools/pe_bliss/pe_resource_viewer.h b/tools/pe_bliss/pe_resource_viewer.h new file mode 100644 index 0000000000..e585da6a87 --- /dev/null +++ b/tools/pe_bliss/pe_resource_viewer.h @@ -0,0 +1,153 @@ +/*************************************************************************/ +/* 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 <map> +#include <string> +#include "pe_structures.h" +#include "pe_resources.h" +#include "message_table.h" +#include "resource_data_info.h" + +namespace pe_bliss +{ + //PE resource manager allows to read resources from PE files +class pe_resource_viewer +{ +public: + //Resource type enumeration + enum resource_type + { + resource_cursor = 1, + resource_bitmap = 2, + resource_icon = 3, + resource_menu = 4, + resource_dialog = 5, + resource_string = 6, + resource_fontdir = 7, + resource_font = 8, + resource_accelerator = 9, + resource_rcdata = 10, + resource_message_table = 11, + resource_cursor_group = 12, + resource_icon_group = 14, + resource_version = 16, + resource_dlginclude = 17, + resource_plugplay = 19, + resource_vxd = 20, + resource_anicursor = 21, + resource_aniicon = 22, + resource_html = 23, + resource_manifest = 24 + }; + +public: + //Some useful typedefs + typedef std::vector<uint32_t> resource_type_list; + typedef std::vector<uint32_t> resource_id_list; + typedef std::vector<std::wstring> resource_name_list; + typedef std::vector<uint32_t> resource_language_list; + +public: + //Constructor from root resource_directory from PE file + explicit pe_resource_viewer(const resource_directory& root_directory); + + const resource_directory& get_root_directory() const; + + //Lists resource types existing in PE file (non-named only) + const resource_type_list list_resource_types() const; + //Returns true if resource type exists + bool resource_exists(resource_type type) const; + //Returns true if resource name exists + bool resource_exists(const std::wstring& root_name) const; + + //Lists resource names existing in PE file by resource type + const resource_name_list list_resource_names(resource_type type) const; + //Lists resource names existing in PE file by resource name + const resource_name_list list_resource_names(const std::wstring& root_name) const; + //Lists resource IDs existing in PE file by resource type + const resource_id_list list_resource_ids(resource_type type) const; + //Lists resource IDs existing in PE file by resource name + const resource_id_list list_resource_ids(const std::wstring& root_name) const; + //Returns resource count by type + unsigned long get_resource_count(resource_type type) const; + //Returns resource count by name + unsigned long get_resource_count(const std::wstring& root_name) const; + + //Returns language count of resource by resource type and name + unsigned long get_language_count(resource_type type, const std::wstring& name) const; + //Returns language count of resource by resource names + unsigned long get_language_count(const std::wstring& root_name, const std::wstring& name) const; + //Returns language count of resource by resource type and ID + unsigned long get_language_count(resource_type type, uint32_t id) const; + //Returns language count of resource by resource name and ID + unsigned long get_language_count(const std::wstring& root_name, uint32_t id) const; + //Lists resource languages by resource type and name + const resource_language_list list_resource_languages(resource_type type, const std::wstring& name) const; + //Lists resource languages by resource names + const resource_language_list list_resource_languages(const std::wstring& root_name, const std::wstring& name) const; + //Lists resource languages by resource type and ID + const resource_language_list list_resource_languages(resource_type type, uint32_t id) const; + //Lists resource languages by resource name and ID + const resource_language_list list_resource_languages(const std::wstring& root_name, uint32_t id) const; + + //Returns raw resource data by type, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, resource_type type, const std::wstring& name) const; + //Returns raw resource data by root name, name and language + const resource_data_info get_resource_data_by_name(uint32_t language, const std::wstring& root_name, const std::wstring& name) const; + //Returns raw resource data by type, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, resource_type type, uint32_t id) const; + //Returns raw resource data by root name, ID and language + const resource_data_info get_resource_data_by_id(uint32_t language, const std::wstring& root_name, uint32_t id) const; + //Returns raw resource data by type, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(resource_type type, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by root name, name and index in language directory (instead of language) + const resource_data_info get_resource_data_by_name(const std::wstring& root_name, const std::wstring& name, uint32_t index = 0) const; + //Returns raw resource data by type, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(resource_type type, uint32_t id, uint32_t index = 0) const; + //Returns raw resource data by root name, ID and index in language directory (instead of language) + const resource_data_info get_resource_data_by_id(const std::wstring& root_name, uint32_t id, uint32_t index = 0) const; + +protected: + //Root resource directory. We're not copying it, because it might be heavy + const resource_directory& root_dir_; + + //Helper function to get ID list from entry list + static const resource_id_list get_id_list(const resource_directory::entry_list& entries); + //Helper function to get name list from entry list + static const resource_name_list get_name_list(const resource_directory::entry_list& entries); + +protected: + //Helper structure - finder of resource_directory_entry that is named + struct has_name + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; + + //Helper structure - finder of resource_directory_entry that is not named (has id) + struct has_id + { + public: + bool operator()(const resource_directory_entry& entry) const; + }; +}; +} diff --git a/tools/pe_bliss/pe_resources.cpp b/tools/pe_bliss/pe_resources.cpp new file mode 100644 index 0000000000..189aba1f76 --- /dev/null +++ b/tools/pe_bliss/pe_resources.cpp @@ -0,0 +1,726 @@ +/*************************************************************************/ +/* 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 <algorithm> +#include <string.h> +#include "pe_resources.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//RESOURCES +//Default constructor +resource_data_entry::resource_data_entry() + :codepage_(0) +{} + +//Constructor from data +resource_data_entry::resource_data_entry(const std::string& data, uint32_t codepage) + :codepage_(codepage), data_(data) +{} + +//Returns resource data codepage +uint32_t resource_data_entry::get_codepage() const +{ + return codepage_; +} + +//Returns resource data +const std::string& resource_data_entry::get_data() const +{ + return data_; +} + +//Sets resource data codepage +void resource_data_entry::set_codepage(uint32_t codepage) +{ + codepage_ = codepage; +} + +//Sets resource data +void resource_data_entry::set_data(const std::string& data) +{ + data_ = data; +} + +//Default constructor +resource_directory_entry::includes::includes() + :data_(0) +{} + +//Default constructor +resource_directory_entry::resource_directory_entry() + :id_(0), includes_data_(false), named_(false) +{} + +//Copy constructor +resource_directory_entry::resource_directory_entry(const resource_directory_entry& other) + :id_(other.id_), name_(other.name_), includes_data_(other.includes_data_), named_(other.named_) +{ + //If union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } +} + +//Copy assignment operator +resource_directory_entry& resource_directory_entry::operator=(const resource_directory_entry& other) +{ + release(); + + id_ = other.id_; + name_ = other.name_; + includes_data_ = other.includes_data_; + named_ = other.named_; + + //If other union'ed pointer is not zero + if(other.ptr_.data_) + { + if(other.includes_data()) + ptr_.data_ = new resource_data_entry(*other.ptr_.data_); + else + ptr_.dir_ = new resource_directory(*other.ptr_.dir_); + } + + return *this; +} + +//Destroys included data +void resource_directory_entry::release() +{ + //If union'ed pointer is not zero + if(ptr_.data_) + { + if(includes_data()) + delete ptr_.data_; + else + delete ptr_.dir_; + + ptr_.data_ = 0; + } +} + +//Destructor +resource_directory_entry::~resource_directory_entry() +{ + release(); +} + +//Returns entry ID +uint32_t resource_directory_entry::get_id() const +{ + return id_; +} + +//Returns entry name +const std::wstring& resource_directory_entry::get_name() const +{ + return name_; +} + +//Returns true, if entry has name +//Returns false, if entry has ID +bool resource_directory_entry::is_named() const +{ + return named_; +} + +//Returns true, if entry includes resource_data_entry +//Returns false, if entry includes resource_directory +bool resource_directory_entry::includes_data() const +{ + return includes_data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +const resource_directory& resource_directory_entry::get_resource_directory() const +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +const resource_data_entry& resource_directory_entry::get_data_entry() const +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Returns resource_directory if entry includes it, otherwise throws an exception +resource_directory& resource_directory_entry::get_resource_directory() +{ + if(!ptr_.dir_ || includes_data_) + throw pe_exception("Resource directory entry does not contain resource directory", pe_exception::resource_directory_entry_error); + + return *ptr_.dir_; +} + +//Returns resource_data_entry if entry includes it, otherwise throws an exception +resource_data_entry& resource_directory_entry::get_data_entry() +{ + if(!ptr_.data_ || !includes_data_) + throw pe_exception("Resource directory entry does not contain resource data entry", pe_exception::resource_directory_entry_error); + + return *ptr_.data_; +} + +//Sets entry name +void resource_directory_entry::set_name(const std::wstring& name) +{ + name_ = name; + named_ = true; + id_ = 0; +} + +//Sets entry ID +void resource_directory_entry::set_id(uint32_t id) +{ + id_ = id; + named_ = false; + name_.clear(); +} + +//Adds resource_data_entry +void resource_directory_entry::add_data_entry(const resource_data_entry& entry) +{ + release(); + ptr_.data_ = new resource_data_entry(entry); + includes_data_ = true; +} + +//Adds resource_directory +void resource_directory_entry::add_resource_directory(const resource_directory& dir) +{ + release(); + ptr_.dir_ = new resource_directory(dir); + includes_data_ = false; +} + +//Default constructor +resource_directory::resource_directory() + :characteristics_(0), + timestamp_(0), + major_version_(0), minor_version_(0), + number_of_named_entries_(0), number_of_id_entries_(0) +{} + +//Constructor from data +resource_directory::resource_directory(const image_resource_directory& dir) + :characteristics_(dir.Characteristics), + timestamp_(dir.TimeDateStamp), + major_version_(dir.MajorVersion), minor_version_(dir.MinorVersion), + number_of_named_entries_(0), number_of_id_entries_(0) //Set to zero here, calculate on add +{} + +//Returns characteristics of directory +uint32_t resource_directory::get_characteristics() const +{ + return characteristics_; +} + +//Returns date and time stamp of directory +uint32_t resource_directory::get_timestamp() const +{ + return timestamp_; +} + +//Returns major version of directory +uint16_t resource_directory::get_major_version() const +{ + return major_version_; +} + +//Returns minor version of directory +uint16_t resource_directory::get_minor_version() const +{ + return minor_version_; +} + +//Returns number of named entries +uint32_t resource_directory::get_number_of_named_entries() const +{ + return number_of_named_entries_; +} + +//Returns number of ID entries +uint32_t resource_directory::get_number_of_id_entries() const +{ + return number_of_id_entries_; +} + +//Returns resource_directory_entry array +const resource_directory::entry_list& resource_directory::get_entry_list() const +{ + return entries_; +} + +//Returns resource_directory_entry array +resource_directory::entry_list& resource_directory::get_entry_list() +{ + return entries_; +} + +//Adds resource_directory_entry +void resource_directory::add_resource_directory_entry(const resource_directory_entry& entry) +{ + entries_.push_back(entry); + if(entry.is_named()) + ++number_of_named_entries_; + else + ++number_of_id_entries_; +} + +//Clears resource_directory_entry array +void resource_directory::clear_resource_directory_entry_list() +{ + entries_.clear(); + number_of_named_entries_ = 0; + number_of_id_entries_ = 0; +} + +//Sets characteristics of directory +void resource_directory::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets date and time stamp of directory +void resource_directory::set_timestamp(uint32_t timestamp) +{ + timestamp_ = timestamp; +} + +//Sets number of named entries +void resource_directory::set_number_of_named_entries(uint32_t number) +{ + number_of_named_entries_ = number; +} + +//Sets number of ID entries +void resource_directory::set_number_of_id_entries(uint32_t number) +{ + number_of_id_entries_ = number; +} + +//Sets major version of directory +void resource_directory::set_major_version(uint16_t major_version) +{ + major_version_ = major_version; +} + +//Sets minor version of directory +void resource_directory::get_minor_version(uint16_t minor_version) +{ + minor_version_ = minor_version; +} + +//Processes resource directory +const resource_directory process_resource_directory(const pe_base& pe, uint32_t res_rva, uint32_t offset_to_directory, std::set<uint32_t>& processed) +{ + resource_directory ret; + + //Check for resource loops + if(!processed.insert(offset_to_directory).second) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(res_rva, offset_to_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Get root IMAGE_RESOURCE_DIRECTORY + image_resource_directory directory = pe.section_data_from_rva<image_resource_directory>(res_rva + offset_to_directory, section_data_virtual, true); + + ret = resource_directory(directory); + + //Check DWORDs for possible overflows + if(!pe_utils::is_sum_safe(directory.NumberOfIdEntries, directory.NumberOfNamedEntries) + || directory.NumberOfIdEntries + directory.NumberOfNamedEntries >= pe_utils::max_dword / sizeof(image_resource_directory_entry) + sizeof(image_resource_directory)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + if(!pe_utils::is_sum_safe(offset_to_directory, sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry)) + || !pe_utils::is_sum_safe(res_rva, offset_to_directory + sizeof(image_resource_directory) + (directory.NumberOfIdEntries + directory.NumberOfNamedEntries) * sizeof(image_resource_directory_entry))) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + for(unsigned long i = 0; i != static_cast<unsigned long>(directory.NumberOfIdEntries) + directory.NumberOfNamedEntries; ++i) + { + //Read directory entries one by one + image_resource_directory_entry dir_entry = pe.section_data_from_rva<image_resource_directory_entry>( + res_rva + sizeof(image_resource_directory) + i * sizeof(image_resource_directory_entry) + offset_to_directory, section_data_virtual, true); + + //Create directory entry structure + resource_directory_entry entry; + + //If directory is named + if(dir_entry.NameIsString) + { + if(!pe_utils::is_sum_safe(res_rva + sizeof(uint16_t) /* safe */, dir_entry.NameOffset)) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //get directory name length + uint16_t directory_name_length = pe.section_data_from_rva<uint16_t>(res_rva + dir_entry.NameOffset, section_data_virtual, true); + + //Check name length + if(pe.section_data_length_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true) + < directory_name_length) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + +#ifdef PE_BLISS_WINDOWS + //Set entry UNICODE name + entry.set_name(std::wstring( + reinterpret_cast<const wchar_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length)); +#else + //Set entry UNICODE name + entry.set_name(pe_utils::from_ucs2(u16string( + reinterpret_cast<const unicode16_t*>(pe.section_data_from_rva(res_rva + dir_entry.NameOffset + sizeof(uint16_t), section_data_virtual, true)), + directory_name_length))); +#endif + } + else + { + //Else - set directory ID + entry.set_id(dir_entry.Id); + } + + //If directory entry has another resource directory + if(dir_entry.DataIsDirectory) + { + entry.add_resource_directory(process_resource_directory(pe, res_rva, dir_entry.OffsetToDirectory, processed)); + } + else + { + //If directory entry has data + image_resource_data_entry data_entry = pe.section_data_from_rva<image_resource_data_entry>( + res_rva + dir_entry.OffsetToData, section_data_virtual, true); + + //Check byte count that stated by data entry + if(pe.section_data_length_from_rva(data_entry.OffsetToData, data_entry.OffsetToData, section_data_virtual, true) < data_entry.Size) + throw pe_exception("Incorrect resource directory", pe_exception::incorrect_resource_directory); + + //Add data entry to directory entry + entry.add_data_entry(resource_data_entry( + std::string(pe.section_data_from_rva(data_entry.OffsetToData, section_data_virtual, true), data_entry.Size), + data_entry.CodePage)); + } + + //Save directory entry + ret.add_resource_directory_entry(entry); + } + + //Return resource directory + return ret; +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t aligned_offset_from_section_start, uint32_t& needed_size_for_structures, uint32_t& needed_size_for_strings) +{ + needed_size_for_structures += sizeof(image_resource_directory); + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + needed_size_for_structures += sizeof(image_resource_directory_entry); + + if((*it).is_named()) + needed_size_for_strings += static_cast<uint32_t>(((*it).get_name().length() + 1) * 2 /* unicode */ + sizeof(uint16_t) /* for string length */); + + if(!(*it).includes_data()) + calculate_resource_data_space((*it).get_resource_directory(), aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + } +} + +//Helper function to calculate needed space for resource data +void calculate_resource_data_space(const resource_directory& root, uint32_t needed_size_for_structures, uint32_t needed_size_for_strings, uint32_t& needed_size_for_data, uint32_t& current_data_pos) +{ + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).includes_data()) + { + uint32_t data_size = static_cast<uint32_t>((*it).get_data_entry().get_data().length() + + sizeof(image_resource_data_entry) + + (pe_utils::align_up(current_data_pos, sizeof(uint32_t)) - current_data_pos) /* alignment */); + needed_size_for_data += data_size; + current_data_pos += data_size; + } + else + { + calculate_resource_data_space((*it).get_resource_directory(), needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + } +} + +//Helper: sorts resource directory entries +struct entry_sorter +{ +public: + bool operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const; +}; + +//Helper function to rebuild resource directory +void rebuild_resource_directory(pe_base& pe, section& resource_section, resource_directory& root, uint32_t& current_structures_pos, uint32_t& current_data_pos, uint32_t& current_strings_pos, uint32_t offset_from_section_start) +{ + //Create resource directory + image_resource_directory dir = {0}; + dir.Characteristics = root.get_characteristics(); + dir.MajorVersion = root.get_major_version(); + dir.MinorVersion = root.get_minor_version(); + dir.TimeDateStamp = root.get_timestamp(); + + { + resource_directory::entry_list& entries = root.get_entry_list(); + std::sort(entries.begin(), entries.end(), entry_sorter()); + } + + //Calculate number of named and ID entries + for(resource_directory::entry_list::const_iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + if((*it).is_named()) + ++dir.NumberOfNamedEntries; + else + ++dir.NumberOfIdEntries; + } + + std::string& raw_data = resource_section.get_raw_data(); + + //Save resource directory + memcpy(&raw_data[current_structures_pos], &dir, sizeof(dir)); + current_structures_pos += sizeof(dir); + + uint32_t this_current_structures_pos = current_structures_pos; + + current_structures_pos += sizeof(image_resource_directory_entry) * (dir.NumberOfNamedEntries + dir.NumberOfIdEntries); + + //Create all resource directory entries + for(resource_directory::entry_list::iterator it = root.get_entry_list().begin(); it != root.get_entry_list().end(); ++it) + { + image_resource_directory_entry entry; + if((*it).is_named()) + { + entry.Name = 0x80000000 | (current_strings_pos - offset_from_section_start); + uint16_t unicode_length = static_cast<uint16_t>((*it).get_name().length()); + memcpy(&raw_data[current_strings_pos], &unicode_length, sizeof(unicode_length)); + current_strings_pos += sizeof(unicode_length); + +#ifdef PE_BLISS_WINDOWS + memcpy(&raw_data[current_strings_pos], (*it).get_name().c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); +#else + { + u16string str(pe_utils::to_ucs2((*it).get_name())); + memcpy(&raw_data[current_strings_pos], str.c_str(), (*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } +#endif + + current_strings_pos += static_cast<unsigned long>((*it).get_name().length() * sizeof(uint16_t) + sizeof(uint16_t) /* unicode */); + } + else + { + entry.Name = (*it).get_id(); + } + + if((*it).includes_data()) + { + current_data_pos = pe_utils::align_up(current_data_pos, sizeof(uint32_t)); + image_resource_data_entry data_entry = {0}; + data_entry.CodePage = (*it).get_data_entry().get_codepage(); + data_entry.Size = static_cast<uint32_t>((*it).get_data_entry().get_data().length()); + data_entry.OffsetToData = pe.rva_from_section_offset(resource_section, current_data_pos + sizeof(data_entry)); + + entry.OffsetToData = current_data_pos - offset_from_section_start; + + memcpy(&raw_data[current_data_pos], &data_entry, sizeof(data_entry)); + current_data_pos += sizeof(data_entry); + + memcpy(&raw_data[current_data_pos], (*it).get_data_entry().get_data().data(), data_entry.Size); + current_data_pos += data_entry.Size; + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + } + else + { + entry.OffsetToData = 0x80000000 | (current_structures_pos - offset_from_section_start); + + memcpy(&raw_data[this_current_structures_pos], &entry, sizeof(entry)); + this_current_structures_pos += sizeof(entry); + + rebuild_resource_directory(pe, resource_section, (*it).get_resource_directory(), current_structures_pos, current_data_pos, current_strings_pos, offset_from_section_start); + } + } +} + +//Helper function to rebuild resource directory +bool entry_sorter::operator()(const resource_directory_entry& entry1, const resource_directory_entry& entry2) const +{ + if(entry1.is_named() && entry2.is_named()) + return entry1.get_name() < entry2.get_name(); + else if(!entry1.is_named() && !entry2.is_named()) + return entry1.get_id() < entry2.get_id(); + else + return entry1.is_named(); +} + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//offset_from_section_start - offset from resources_section raw data start +//resource_directory is non-constant, because it will be sorted +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that resources_section is attached to this PE image + if(!pe.section_attached(resources_section)) + throw pe_exception("Resource section must be attached to PE file", pe_exception::section_is_not_attached); + + //Check resource directory correctness + if(info.get_entry_list().empty()) + throw pe_exception("Empty resource directory", pe_exception::incorrect_resource_directory); + + uint32_t aligned_offset_from_section_start = pe_utils::align_up(offset_from_section_start, sizeof(uint32_t)); + uint32_t needed_size_for_structures = aligned_offset_from_section_start - offset_from_section_start; //Calculate needed size for resource tables and data + uint32_t needed_size_for_strings = 0; + uint32_t needed_size_for_data = 0; + + calculate_resource_data_space(info, aligned_offset_from_section_start, needed_size_for_structures, needed_size_for_strings); + + { + uint32_t current_data_pos = aligned_offset_from_section_start + needed_size_for_structures + needed_size_for_strings; + calculate_resource_data_space(info, needed_size_for_structures, needed_size_for_strings, needed_size_for_data, current_data_pos); + } + + uint32_t needed_size = needed_size_for_structures + needed_size_for_strings + needed_size_for_data; + + //Check if resources_section is last one. If it's not, check if there's enough place for resource data + if(&resources_section != &*(pe.get_image_sections().end() - 1) && + (resources_section.empty() || pe_utils::align_up(resources_section.get_size_of_raw_data(), pe.get_file_alignment()) + < needed_size + aligned_offset_from_section_start)) + throw pe_exception("Insufficient space for resource directory", pe_exception::insufficient_space); + + std::string& raw_data = resources_section.get_raw_data(); + + //This will be done only if resources_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + aligned_offset_from_section_start) + raw_data.resize(needed_size + aligned_offset_from_section_start); //Expand section raw data + + uint32_t current_structures_pos = aligned_offset_from_section_start; + uint32_t current_strings_pos = current_structures_pos + needed_size_for_structures; + uint32_t current_data_pos = current_strings_pos + needed_size_for_strings; + rebuild_resource_directory(pe, resources_section, info, current_structures_pos, current_data_pos, current_strings_pos, aligned_offset_from_section_start); + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(resources_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(resources_section, aligned_offset_from_section_start), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_resource, ret.get_rva()); + pe.set_directory_size(image_directory_entry_resource, ret.get_size()); + } + + return ret; +} + +//Returns resources from PE file +const resource_directory get_resources(const pe_base& pe) +{ + resource_directory ret; + + if(!pe.has_resources()) + return ret; + + //Get resource directory RVA + uint32_t res_rva = pe.get_directory_rva(image_directory_entry_resource); + + //Store already processed directories to avoid resource loops + std::set<uint32_t> processed; + + //Process all directories (recursion) + ret = process_resource_directory(pe, res_rva, 0, processed); + + return ret; +} + +//Finds resource_directory_entry by ID +resource_directory::id_entry_finder::id_entry_finder(uint32_t id) + :id_(id) +{} + +bool resource_directory::id_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return !entry.is_named() && entry.get_id() == id_; +} + +//Finds resource_directory_entry by name +resource_directory::name_entry_finder::name_entry_finder(const std::wstring& name) + :name_(name) +{} + +bool resource_directory::name_entry_finder::operator()(const resource_directory_entry& entry) const +{ + return entry.is_named() && entry.get_name() == name_; +} + +//Finds resource_directory_entry by name or ID (universal) +resource_directory::entry_finder::entry_finder(const std::wstring& name) + :name_(name), named_(true) +{} + +resource_directory::entry_finder::entry_finder(uint32_t id) + :id_(id), named_(false) +{} + +bool resource_directory::entry_finder::operator()(const resource_directory_entry& entry) const +{ + if(named_) + return entry.is_named() && entry.get_name() == name_; + else + return !entry.is_named() && entry.get_id() == id_; +} + +//Returns resource_directory_entry by ID. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_id(uint32_t id) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), id_entry_finder(id)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} + +//Returns resource_directory_entry by name. If not found - throws an exception +const resource_directory_entry& resource_directory::entry_by_name(const std::wstring& name) const +{ + entry_list::const_iterator i = std::find_if(entries_.begin(), entries_.end(), name_entry_finder(name)); + if(i == entries_.end()) + throw pe_exception("Resource directory entry not found", pe_exception::resource_directory_entry_not_found); + + return *i; +} +} diff --git a/tools/pe_bliss/pe_resources.h b/tools/pe_bliss/pe_resources.h new file mode 100644 index 0000000000..1eb6437563 --- /dev/null +++ b/tools/pe_bliss/pe_resources.h @@ -0,0 +1,245 @@ +/*************************************************************************/ +/* 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 <vector> +#include <string> +#include <set> +#include "pe_structures.h" +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing resource data entry +class resource_data_entry +{ +public: + //Default constructor + resource_data_entry(); + //Constructor from data + resource_data_entry(const std::string& data, uint32_t codepage); + + //Returns resource data codepage + uint32_t get_codepage() const; + //Returns resource data + const std::string& get_data() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets resource data codepage + void set_codepage(uint32_t codepage); + //Sets resource data + void set_data(const std::string& data); + +private: + uint32_t codepage_; //Resource data codepage + std::string data_; //Resource data +}; + +//Forward declaration +class resource_directory; + +//Class representing resource directory entry +class resource_directory_entry +{ +public: + //Default constructor + resource_directory_entry(); + //Copy constructor + resource_directory_entry(const resource_directory_entry& other); + //Copy assignment operator + resource_directory_entry& operator=(const resource_directory_entry& other); + + //Returns entry ID + uint32_t get_id() const; + //Returns entry name + const std::wstring& get_name() const; + //Returns true, if entry has name + //Returns false, if entry has ID + bool is_named() const; + + //Returns true, if entry includes resource_data_entry + //Returns false, if entry includes resource_directory + bool includes_data() const; + //Returns resource_directory if entry includes it, otherwise throws an exception + const resource_directory& get_resource_directory() const; + //Returns resource_data_entry if entry includes it, otherwise throws an exception + const resource_data_entry& get_data_entry() const; + + //Destructor + ~resource_directory_entry(); + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Sets entry name + void set_name(const std::wstring& name); + //Sets entry ID + void set_id(uint32_t id); + + //Returns resource_directory if entry includes it, otherwise throws an exception + resource_directory& get_resource_directory(); + //Returns resource_data_entry if entry includes it, otherwise throws an exception + resource_data_entry& get_data_entry(); + + //Adds resource_data_entry + void add_data_entry(const resource_data_entry& entry); + //Adds resource_directory + void add_resource_directory(const resource_directory& dir); + +private: + //Destroys included data + void release(); + +private: + uint32_t id_; + std::wstring name_; + + union includes + { + //Default constructor + includes(); + + //We use pointers, we're doing manual copying here + class resource_data_entry* data_; + class resource_directory* dir_; //We use pointer, because structs include each other + }; + + includes ptr_; + + bool includes_data_, named_; +}; + +//Class representing resource directory +class resource_directory +{ +public: + typedef std::vector<resource_directory_entry> entry_list; + +public: + //Default constructor + resource_directory(); + //Constructor from data + explicit resource_directory(const pe_win::image_resource_directory& dir); + + //Returns characteristics of directory + uint32_t get_characteristics() const; + //Returns date and time stamp of directory + uint32_t get_timestamp() const; + //Returns number of named entries + uint32_t get_number_of_named_entries() const; + //Returns number of ID entries + uint32_t get_number_of_id_entries() const; + //Returns major version of directory + uint16_t get_major_version() const; + //Returns minor version of directory + uint16_t get_minor_version() const; + //Returns resource_directory_entry array + const entry_list& get_entry_list() const; + //Returns resource_directory_entry by ID. If not found - throws an exception + const resource_directory_entry& entry_by_id(uint32_t id) const; + //Returns resource_directory_entry by name. If not found - throws an exception + const resource_directory_entry& entry_by_name(const std::wstring& name) const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild resource directory + + //Adds resource_directory_entry + void add_resource_directory_entry(const resource_directory_entry& entry); + //Clears resource_directory_entry array + void clear_resource_directory_entry_list(); + + //Sets characteristics of directory + void set_characteristics(uint32_t characteristics); + //Sets date and time stamp of directory + void set_timestamp(uint32_t timestamp); + //Sets number of named entries + void set_number_of_named_entries(uint32_t number); + //Sets number of ID entries + void set_number_of_id_entries(uint32_t number); + //Sets major version of directory + void set_major_version(uint16_t major_version); + //Sets minor version of directory + void get_minor_version(uint16_t minor_version); + + //Returns resource_directory_entry array + entry_list& get_entry_list(); + +private: + uint32_t characteristics_; + uint32_t timestamp_; + uint16_t major_version_, minor_version_; + uint32_t number_of_named_entries_, number_of_id_entries_; + entry_list entries_; + +public: //Finder helpers + //Finds resource_directory_entry by ID + struct id_entry_finder + { + public: + explicit id_entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + uint32_t id_; + }; + + //Finds resource_directory_entry by name + struct name_entry_finder + { + public: + explicit name_entry_finder(const std::wstring& name); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + }; + + //Finds resource_directory_entry by name or ID (universal) + struct entry_finder + { + public: + explicit entry_finder(const std::wstring& name); + explicit entry_finder(uint32_t id); + bool operator()(const resource_directory_entry& entry) const; + + private: + std::wstring name_; + uint32_t id_; + bool named_; + }; +}; + +//Returns resources (root resource_directory) from PE file +const resource_directory get_resources(const pe_base& pe); + +//Resources rebuilder +//resource_directory - root resource directory +//resources_section - section where resource directory will be placed (must be attached to PE image) +//resource_directory is non-constant, because it will be sorted +//offset_from_section_start - offset from resources_section raw data start +//save_to_pe_headers - if true, new resource directory information will be saved to PE image headers +//auto_strip_last_section - if true and resources are placed in the last section, it will be automatically stripped +//number_of_id_entries and number_of_named_entries for resource directories are recalculated and not used +const image_directory rebuild_resources(pe_base& pe, resource_directory& info, section& resources_section, uint32_t offset_from_section_start = 0, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/pe_rich_data.cpp b/tools/pe_bliss/pe_rich_data.cpp new file mode 100644 index 0000000000..e92f7ddc1b --- /dev/null +++ b/tools/pe_bliss/pe_rich_data.cpp @@ -0,0 +1,152 @@ +/*************************************************************************/ +/* 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 "pe_rich_data.h" + +namespace pe_bliss +{ +//STUB OVERLAY +//Default constructor +rich_data::rich_data() + :number_(0), version_(0), times_(0) +{} + +//Who knows, what these fields mean... +uint32_t rich_data::get_number() const +{ + return number_; +} + +uint32_t rich_data::get_version() const +{ + return version_; +} + +uint32_t rich_data::get_times() const +{ + return times_; +} + +void rich_data::set_number(uint32_t number) +{ + number_ = number; +} + +void rich_data::set_version(uint32_t version) +{ + version_ = version; +} + +void rich_data::set_times(uint32_t times) +{ + times_ = times; +} + +//Returns MSVC rich data +const rich_data_list get_rich_data(const pe_base& pe) +{ + //Returned value + rich_data_list ret; + + const std::string& rich_overlay = pe.get_stub_overlay(); + + //If there's no rich overlay, return empty vector + if(rich_overlay.size() < sizeof(uint32_t)) + return ret; + + //True if rich data was found + bool found = false; + + //Rich overlay ID ("Rich" word) + static const uint32_t rich_overlay_id = 0x68636952; + + //Search for rich data overlay ID + const char* begin = &rich_overlay[0]; + const char* end = begin + rich_overlay.length(); + for(; begin != end; ++begin) + { + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + { + found = true; //We've found it! + break; + } + } + + //If we found it + if(found) + { + //Check remaining length + if(static_cast<size_t>(end - begin) < sizeof(uint32_t)) + return ret; + + //The XOR key is after "Rich" word, we should get it + uint32_t xorkey = *reinterpret_cast<const uint32_t*>(begin + sizeof(uint32_t)); + + //True if rich data was found + found = false; + + //Second search for signature "DanS" + begin = &rich_overlay[0]; + for(; begin != end; ++begin) + { + if((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) == 0x536e6144) //"DanS" + { + found = true; + break; + } + } + + //If second signature is found + if(found) + { + begin += sizeof(uint32_t) * 3; + //List all rich data structures + while(begin < end) + { + begin += sizeof(uint32_t); + if(begin >= end) + break; + + //Check for rich overlay data end ("Rich" word reached) + if(*reinterpret_cast<const uint32_t*>(begin) == rich_overlay_id) + break; + + //Create rich_data structure + rich_data data; + data.set_number((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) >> 16); + data.set_version((*reinterpret_cast<const uint32_t*>(begin) ^ xorkey) & 0xFFFF); + + begin += sizeof(uint32_t); + if(begin >= end) + break; + + data.set_times(*reinterpret_cast<const uint32_t*>(begin) ^ xorkey); + + //Save rich data structure + ret.push_back(data); + } + } + } + + //Return rich data structures list + return ret; +} +} diff --git a/tools/pe_bliss/pe_rich_data.h b/tools/pe_bliss/pe_rich_data.h new file mode 100644 index 0000000000..3e01d3c011 --- /dev/null +++ b/tools/pe_bliss/pe_rich_data.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* 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 <vector> +#include "pe_structures.h" +#include "pe_base.h" + +namespace pe_bliss +{ +//Rich data overlay class of Microsoft Visual Studio +class rich_data +{ +public: + //Default constructor + rich_data(); + +public: //Getters + //Who knows, what these fields mean... + uint32_t get_number() const; + uint32_t get_version() const; + uint32_t get_times() const; + +public: //Setters, used by PE library only + void set_number(uint32_t number); + void set_version(uint32_t version); + void set_times(uint32_t times); + +private: + uint32_t number_; + uint32_t version_; + uint32_t times_; +}; + +//Rich data list typedef +typedef std::vector<rich_data> rich_data_list; + +//Returns a vector with rich data (stub overlay) +const rich_data_list get_rich_data(const pe_base& pe); +} diff --git a/tools/pe_bliss/pe_section.cpp b/tools/pe_bliss/pe_section.cpp new file mode 100644 index 0000000000..72127e22e2 --- /dev/null +++ b/tools/pe_bliss/pe_section.cpp @@ -0,0 +1,303 @@ +/*************************************************************************/ +/* 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 <algorithm> +#include <string.h> +#include "utils.h" +#include "pe_section.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Section structure default constructor +section::section() + :old_size_(static_cast<size_t>(-1)) +{ + memset(&header_, 0, sizeof(image_section_header)); +} + +//Sets the name of section (8 characters maximum) +void section::set_name(const std::string& name) +{ + memset(header_.Name, 0, sizeof(header_.Name)); + memcpy(header_.Name, name.c_str(), std::min<size_t>(name.length(), sizeof(header_.Name))); +} + +//Returns section name +const std::string section::get_name() const +{ + char buf[9] = {0}; + memcpy(buf, header_.Name, 8); + return std::string(buf); +} + +//Set flag (attribute) of section +section& section::set_flag(uint32_t flag, bool setflag) +{ + if(setflag) + header_.Characteristics |= flag; + else + header_.Characteristics &= ~flag; + + return *this; +} + +//Sets "readable" attribute of section +section& section::readable(bool readable) +{ + return set_flag(image_scn_mem_read, readable); +} + +//Sets "writeable" attribute of section +section& section::writeable(bool writeable) +{ + return set_flag(image_scn_mem_write, writeable); +} + +//Sets "executable" attribute of section +section& section::executable(bool executable) +{ + return set_flag(image_scn_mem_execute, executable); +} + +//Sets "shared" attribute of section +section& section::shared(bool shared) +{ + return set_flag(image_scn_mem_shared, shared); +} + +//Sets "discardable" attribute of section +section& section::discardable(bool discardable) +{ + return set_flag(image_scn_mem_discardable, discardable); +} + +//Returns true if section is readable +bool section::readable() const +{ + return (header_.Characteristics & image_scn_mem_read) != 0; +} + +//Returns true if section is writeable +bool section::writeable() const +{ + return (header_.Characteristics & image_scn_mem_write) != 0; +} + +//Returns true if section is executable +bool section::executable() const +{ + return (header_.Characteristics & image_scn_mem_execute) != 0; +} + +bool section::shared() const +{ + return (header_.Characteristics & image_scn_mem_shared) != 0; +} + +bool section::discardable() const +{ + return (header_.Characteristics & image_scn_mem_discardable) != 0; +} + +//Returns true if section has no RAW data +bool section::empty() const +{ + if(old_size_ != static_cast<size_t>(-1)) //If virtual memory is mapped, check raw data length (old_size_) + return old_size_ == 0; + else + return raw_data_.empty(); +} + +//Returns raw section data from file image +std::string& section::get_raw_data() +{ + unmap_virtual(); + return raw_data_; +} + +//Sets raw section data from file image +void section::set_raw_data(const std::string& data) +{ + old_size_ = static_cast<size_t>(-1); + raw_data_ = data; +} + +//Returns raw section data from file image +const std::string& section::get_raw_data() const +{ + unmap_virtual(); + return raw_data_; +} + +//Returns mapped virtual section data +const std::string& section::get_virtual_data(uint32_t section_alignment) const +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Returns mapped virtual section data +std::string& section::get_virtual_data(uint32_t section_alignment) +{ + map_virtual(section_alignment); + return raw_data_; +} + +//Maps virtual section data +void section::map_virtual(uint32_t section_alignment) const +{ + uint32_t aligned_virtual_size = get_aligned_virtual_size(section_alignment); + if(old_size_ == static_cast<size_t>(-1) && aligned_virtual_size && aligned_virtual_size > raw_data_.length()) + { + old_size_ = raw_data_.length(); + raw_data_.resize(aligned_virtual_size, 0); + } +} + +//Unmaps virtual section data +void section::unmap_virtual() const +{ + if(old_size_ != static_cast<size_t>(-1)) + { + raw_data_.resize(old_size_, 0); + old_size_ = static_cast<size_t>(-1); + } +} + +//Returns section virtual size +uint32_t section::get_virtual_size() const +{ + return header_.Misc.VirtualSize; +} + +//Returns section virtual address +uint32_t section::get_virtual_address() const +{ + return header_.VirtualAddress; +} + +//Returns size of section raw data +uint32_t section::get_size_of_raw_data() const +{ + return header_.SizeOfRawData; +} + +//Returns pointer to raw section data in PE file +uint32_t section::get_pointer_to_raw_data() const +{ + return header_.PointerToRawData; +} + +//Returns section characteristics +uint32_t section::get_characteristics() const +{ + return header_.Characteristics; +} + +//Returns raw image section header +const pe_win::image_section_header& section::get_raw_header() const +{ + return header_; +} + +//Returns raw image section header +pe_win::image_section_header& section::get_raw_header() +{ + return header_; +} + +//Calculates aligned virtual section size +uint32_t section::get_aligned_virtual_size(uint32_t section_alignment) const +{ + if(get_size_of_raw_data()) + { + if(!get_virtual_size()) + { + //If section virtual size is zero + //Set aligned virtual size of section as aligned raw size + return pe_utils::align_up(get_size_of_raw_data(), section_alignment); + } + } + + return pe_utils::align_up(get_virtual_size(), section_alignment); +} + +//Calculates aligned raw section size +uint32_t section::get_aligned_raw_size(uint32_t file_alignment) const +{ + if(get_size_of_raw_data()) + return pe_utils::align_up(get_size_of_raw_data(), file_alignment); + else + return 0; +} + +//Sets size of raw section data +void section::set_size_of_raw_data(uint32_t size_of_raw_data) +{ + header_.SizeOfRawData = size_of_raw_data; +} + +//Sets pointer to section raw data +void section::set_pointer_to_raw_data(uint32_t pointer_to_raw_data) +{ + header_.PointerToRawData = pointer_to_raw_data; +} + +//Sets section characteristics +void section::set_characteristics(uint32_t characteristics) +{ + header_.Characteristics = characteristics; +} + +//Sets section virtual size +void section::set_virtual_size(uint32_t virtual_size) +{ + header_.Misc.VirtualSize = virtual_size; +} + +//Sets section virtual address +void section::set_virtual_address(uint32_t virtual_address) +{ + header_.VirtualAddress = virtual_address; +} + +//Section by file offset finder helper (4gb max) +section_by_raw_offset::section_by_raw_offset(uint32_t offset) + :offset_(offset) +{} + +bool section_by_raw_offset::operator()(const section& s) const +{ + return (s.get_pointer_to_raw_data() <= offset_) + && (s.get_pointer_to_raw_data() + s.get_size_of_raw_data() > offset_); +} + +section_ptr_finder::section_ptr_finder(const section& s) + :s_(s) +{} + +bool section_ptr_finder::operator()(const section& s) const +{ + return &s == &s_; +} +} diff --git a/tools/pe_bliss/pe_section.h b/tools/pe_bliss/pe_section.h new file mode 100644 index 0000000000..617ecebe26 --- /dev/null +++ b/tools/pe_bliss/pe_section.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* 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 "pe_structures.h" + +namespace pe_bliss +{ +//Enumeration of section data types, used in functions below +enum section_data_type +{ + section_data_raw, + section_data_virtual +}; + +//Class representing image section +class section +{ +public: + //Default constructor + section(); + + //Sets the name of section (stripped to 8 characters) + void set_name(const std::string& name); + + //Returns the name of section + const std::string get_name() const; + + //Changes attributes of section + section& readable(bool readable); + section& writeable(bool writeable); + section& executable(bool executable); + section& shared(bool shared); + section& discardable(bool discardable); + + //Returns attributes of section + bool readable() const; + bool writeable() const; + bool executable() const; + bool shared() const; + bool discardable() const; + + //Returns true if section has no RAW data + bool empty() const; + + //Returns raw section data from file image + std::string& get_raw_data(); + //Returns raw section data from file image + const std::string& get_raw_data() const; + //Returns mapped virtual section data + const std::string& get_virtual_data(uint32_t section_alignment) const; + //Returns mapped virtual section data + std::string& get_virtual_data(uint32_t section_alignment); + +public: //Header getters + //Returns section virtual size + uint32_t get_virtual_size() const; + //Returns section virtual address (RVA) + uint32_t get_virtual_address() const; + //Returns size of section raw data + uint32_t get_size_of_raw_data() const; + //Returns pointer to raw section data in PE file + uint32_t get_pointer_to_raw_data() const; + //Returns section characteristics + uint32_t get_characteristics() const; + + //Returns raw image section header + const pe_win::image_section_header& get_raw_header() const; + +public: //Aligned sizes calculation + //Calculates aligned virtual section size + uint32_t get_aligned_virtual_size(uint32_t section_alignment) const; + //Calculates aligned raw section size + uint32_t get_aligned_raw_size(uint32_t file_alignment) const; + +public: //Setters + //Sets size of raw section data + void set_size_of_raw_data(uint32_t size_of_raw_data); + //Sets pointer to section raw data + void set_pointer_to_raw_data(uint32_t pointer_to_raw_data); + //Sets section characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw section data from file image + void set_raw_data(const std::string& data); + +public: //Setters, be careful + //Sets section virtual size (doesn't set internal aligned virtual size, changes only header value) + //Better use pe_base::set_section_virtual_size + void set_virtual_size(uint32_t virtual_size); + //Sets section virtual address + void set_virtual_address(uint32_t virtual_address); + //Returns raw image section header + pe_win::image_section_header& get_raw_header(); + +private: + //Section header + pe_win::image_section_header header_; + + //Maps virtual section data + void map_virtual(uint32_t section_alignment) const; + + //Unmaps virtual section data + void unmap_virtual() const; + + //Set flag (attribute) of section + section& set_flag(uint32_t flag, bool setflag); + + //Old size of section (stored after mapping of virtual section memory) + mutable std::size_t old_size_; + + //Section raw/virtual data + mutable std::string raw_data_; +}; + +//Section by file offset finder helper (4gb max) +struct section_by_raw_offset +{ +public: + explicit section_by_raw_offset(uint32_t offset); + bool operator()(const section& s) const; + +private: + uint32_t offset_; +}; + +//Helper: finder of section* in sections list +struct section_ptr_finder +{ +public: + explicit section_ptr_finder(const section& s); + bool operator()(const section& s) const; + +private: + const section& s_; +}; + +typedef std::vector<section> section_list; +} diff --git a/tools/pe_bliss/pe_structures.h b/tools/pe_bliss/pe_structures.h new file mode 100644 index 0000000000..efc99103b2 --- /dev/null +++ b/tools/pe_bliss/pe_structures.h @@ -0,0 +1,1028 @@ +/*************************************************************************/ +/* 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 <sstream> +#include "stdint_defs.h" +#if defined(_MSC_VER) or defined(__MINGW32__) +#define PE_BLISS_WINDOWS +#endif + +namespace pe_bliss +{ +//Enumeration of PE types +enum pe_type +{ + pe_type_32, + pe_type_64 +}; + +namespace pe_win +{ +const uint32_t image_numberof_directory_entries = 16; +const uint32_t image_nt_optional_hdr32_magic = 0x10b; +const uint32_t image_nt_optional_hdr64_magic = 0x20b; +const uint32_t image_resource_name_is_string = 0x80000000; +const uint32_t image_resource_data_is_directory = 0x80000000; + +const uint32_t image_dllcharacteristics_dynamic_base = 0x0040; // DLL can move. +const uint32_t image_dllcharacteristics_force_integrity = 0x0080; // Code Integrity Image +const uint32_t image_dllcharacteristics_nx_compat = 0x0100; // Image is NX compatible +const uint32_t image_dllcharacteristics_no_isolation = 0x0200; // Image understands isolation and doesn't want it +const uint32_t image_dllcharacteristics_no_seh = 0x0400; // Image does not use SEH. No SE handler may reside in this image +const uint32_t image_dllcharacteristics_no_bind = 0x0800; // Do not bind this image. +const uint32_t image_dllcharacteristics_wdm_driver = 0x2000; // Driver uses WDM model +const uint32_t image_dllcharacteristics_terminal_server_aware = 0x8000; + +const uint32_t image_sizeof_file_header = 20; + +const uint32_t image_file_relocs_stripped = 0x0001; // Relocation info stripped from file. +const uint32_t image_file_executable_image = 0x0002; // File is executable (i.e. no unresolved externel references). +const uint32_t image_file_line_nums_stripped = 0x0004; // Line nunbers stripped from file. +const uint32_t image_file_local_syms_stripped = 0x0008; // Local symbols stripped from file. +const uint32_t image_file_aggresive_ws_trim = 0x0010; // Agressively trim working set +const uint32_t image_file_large_address_aware = 0x0020; // App can handle >2gb addresses +const uint32_t image_file_bytes_reversed_lo = 0x0080; // Bytes of machine word are reversed. +const uint32_t image_file_32bit_machine = 0x0100; // 32 bit word machine. +const uint32_t image_file_debug_stripped = 0x0200; // Debugging info stripped from file in .DBG file +const uint32_t image_file_removable_run_from_swap = 0x0400; // If Image is on removable media, copy and run from the swap file. +const uint32_t image_file_net_run_from_swap = 0x0800; // If Image is on Net, copy and run from the swap file. +const uint32_t image_file_system = 0x1000; // System File. +const uint32_t image_file_dll = 0x2000; // File is a DLL. +const uint32_t image_file_up_system_only = 0x4000; // File should only be run on a UP machine +const uint32_t image_file_bytes_reversed_hi = 0x8000; // Bytes of machine word are reversed. + +const uint32_t image_scn_lnk_nreloc_ovfl = 0x01000000; // Section contains extended relocations. +const uint32_t image_scn_mem_discardable = 0x02000000; // Section can be discarded. +const uint32_t image_scn_mem_not_cached = 0x04000000; // Section is not cachable. +const uint32_t image_scn_mem_not_paged = 0x08000000; // Section is not pageable. +const uint32_t image_scn_mem_shared = 0x10000000; // Section is shareable. +const uint32_t image_scn_mem_execute = 0x20000000; // Section is executable. +const uint32_t image_scn_mem_read = 0x40000000; // Section is readable. +const uint32_t image_scn_mem_write = 0x80000000; // Section is writeable. + +const uint32_t image_scn_cnt_code = 0x00000020; // Section contains code. +const uint32_t image_scn_cnt_initialized_data = 0x00000040; // Section contains initialized data. +const uint32_t image_scn_cnt_uninitialized_data = 0x00000080; // Section contains uninitialized data. + +//Directory Entries +const uint32_t image_directory_entry_export = 0; // Export Directory +const uint32_t image_directory_entry_import = 1; // Import Directory +const uint32_t image_directory_entry_resource = 2; // Resource Directory +const uint32_t image_directory_entry_exception = 3; // Exception Directory +const uint32_t image_directory_entry_security = 4; // Security Directory +const uint32_t image_directory_entry_basereloc = 5; // Base Relocation Table +const uint32_t image_directory_entry_debug = 6; // Debug Directory +const uint32_t image_directory_entry_architecture = 7; // Architecture Specific Data +const uint32_t image_directory_entry_globalptr = 8; // RVA of GP +const uint32_t image_directory_entry_tls = 9; // TLS Directory +const uint32_t image_directory_entry_load_config = 10; // Load Configuration Directory +const uint32_t image_directory_entry_bound_import = 11; // Bound Import Directory in headers +const uint32_t image_directory_entry_iat = 12; // Import Address Table +const uint32_t image_directory_entry_delay_import = 13; // Delay Load Import Descriptors +const uint32_t image_directory_entry_com_descriptor = 14; // COM Runtime descriptor + +//Subsystem Values +const uint32_t image_subsystem_unknown = 0; // Unknown subsystem. +const uint32_t image_subsystem_native = 1; // Image doesn't require a subsystem. +const uint32_t image_subsystem_windows_gui = 2; // Image runs in the Windows GUI subsystem. +const uint32_t image_subsystem_windows_cui = 3; // Image runs in the Windows character subsystem. +const uint32_t image_subsystem_os2_cui = 5; // image runs in the OS/2 character subsystem. +const uint32_t image_subsystem_posix_cui = 7; // image runs in the Posix character subsystem. +const uint32_t image_subsystem_native_windows = 8; // image is a native Win9x driver. +const uint32_t image_subsystem_windows_ce_gui = 9; // Image runs in the Windows CE subsystem. +const uint32_t image_subsystem_efi_application = 10; // +const uint32_t image_subsystem_efi_boot_service_driver = 11; // +const uint32_t image_subsystem_efi_runtime_driver = 12; // +const uint32_t image_subsystem_efi_rom = 13; +const uint32_t image_subsystem_xbox = 14; +const uint32_t image_subsystem_windows_boot_application = 16; + +//Imports +const uint64_t image_ordinal_flag64 = 0x8000000000000000ull; +const uint32_t image_ordinal_flag32 = 0x80000000; + +//Based relocation types +const uint32_t image_rel_based_absolute = 0; +const uint32_t image_rel_based_high = 1; +const uint32_t image_rel_based_low = 2; +const uint32_t image_rel_based_highlow = 3; +const uint32_t image_rel_based_highadj = 4; +const uint32_t image_rel_based_mips_jmpaddr = 5; +const uint32_t image_rel_based_mips_jmpaddr16 = 9; +const uint32_t image_rel_based_ia64_imm64 = 9; +const uint32_t image_rel_based_dir64 = 10; + +//Exception directory +//The function has an exception handler that should be called when looking for functions that need to examine exceptions +const uint32_t unw_flag_ehandler = 0x01; +//The function has a termination handler that should be called when unwinding an exception +const uint32_t unw_flag_uhandler = 0x02; +//This unwind info structure is not the primary one for the procedure. +//Instead, the chained unwind info entry is the contents of a previous RUNTIME_FUNCTION entry. +//If this flag is set, then the UNW_FLAG_EHANDLER and UNW_FLAG_UHANDLER flags must be cleared. +//Also, the frame register and fixed-stack allocation fields must have the same values as in the primary unwind info +const uint32_t unw_flag_chaininfo = 0x04; + +//Debug +const uint32_t image_debug_misc_exename = 1; +const uint32_t image_debug_type_unknown = 0; +const uint32_t image_debug_type_coff = 1; +const uint32_t image_debug_type_codeview = 2; +const uint32_t image_debug_type_fpo = 3; +const uint32_t image_debug_type_misc = 4; +const uint32_t image_debug_type_exception = 5; +const uint32_t image_debug_type_fixup = 6; +const uint32_t image_debug_type_omap_to_src = 7; +const uint32_t image_debug_type_omap_from_src = 8; +const uint32_t image_debug_type_borland = 9; +const uint32_t image_debug_type_reserved10 = 10; +const uint32_t image_debug_type_clsid = 11; + + +//Storage classes +const uint32_t image_sym_class_end_of_function = static_cast<uint8_t>(-1); +const uint32_t image_sym_class_null = 0x0000; +const uint32_t image_sym_class_automatic = 0x0001; +const uint32_t image_sym_class_external = 0x0002; +const uint32_t image_sym_class_static = 0x0003; +const uint32_t image_sym_class_register = 0x0004; +const uint32_t image_sym_class_external_def = 0x0005; +const uint32_t image_sym_class_label = 0x0006; +const uint32_t image_sym_class_undefined_label = 0x0007; +const uint32_t image_sym_class_member_of_struct = 0x0008; +const uint32_t image_sym_class_argument = 0x0009; +const uint32_t image_sym_class_struct_tag = 0x000a; +const uint32_t image_sym_class_member_of_union = 0x000b; +const uint32_t image_sym_class_union_tag = 0x000c; +const uint32_t image_sym_class_type_definition = 0x000d; +const uint32_t image_sym_class_undefined_static = 0x000e; +const uint32_t image_sym_class_enum_tag = 0x000f; +const uint32_t image_sym_class_member_of_enum = 0x0010; +const uint32_t image_sym_class_register_param = 0x0011; +const uint32_t image_sym_class_bit_field = 0x0012; + +const uint32_t image_sym_class_far_external = 0x0044; + +const uint32_t image_sym_class_block = 0x0064; +const uint32_t image_sym_class_function = 0x0065; +const uint32_t image_sym_class_end_of_struct = 0x0066; +const uint32_t image_sym_class_file = 0x0067; + +const uint32_t image_sym_class_section = 0x0068; +const uint32_t image_sym_class_weak_external = 0x0069; + +const uint32_t image_sym_class_clr_token = 0x006b; + +//type packing constants +const uint32_t n_btmask = 0x000f; +const uint32_t n_tmask = 0x0030; +const uint32_t n_tmask1 = 0x00c0; +const uint32_t n_tmask2 = 0x00f0; +const uint32_t n_btshft = 4; +const uint32_t n_tshift = 2; + +//Type (derived) values. +const uint32_t image_sym_dtype_null = 0; // no derived type. +const uint32_t image_sym_dtype_pointer = 1; // pointer. +const uint32_t image_sym_dtype_function = 2; // function. +const uint32_t image_sym_dtype_array = 3; // array. + +// Is x a function? +//TODO +#ifndef ISFCN +#define ISFCN(x) (((x) & n_tmask) == (image_sym_dtype_function << n_btshft)) +#endif + +//Version info +const uint32_t vs_ffi_fileflagsmask = 0x0000003FL; + +const uint32_t vs_ffi_signature = 0xFEEF04BDL; +const uint32_t vs_ffi_strucversion = 0x00010000L; + +/* ----- VS_VERSION.dwFileFlags ----- */ +const uint32_t vs_ff_debug = 0x00000001L; +const uint32_t vs_ff_prerelease = 0x00000002L; +const uint32_t vs_ff_patched = 0x00000004L; +const uint32_t vs_ff_privatebuild = 0x00000008L; +const uint32_t vs_ff_infoinferred = 0x00000010L; +const uint32_t vs_ff_specialbuild = 0x00000020L; + +/* ----- VS_VERSION.dwFileOS ----- */ +const uint32_t vos_unknown = 0x00000000L; +const uint32_t vos_dos = 0x00010000L; +const uint32_t vos_os216 = 0x00020000L; +const uint32_t vos_os232 = 0x00030000L; +const uint32_t vos_nt = 0x00040000L; +const uint32_t vos_wince = 0x00050000L; + +const uint32_t vos__base = 0x00000000L; +const uint32_t vos__windows16 = 0x00000001L; +const uint32_t vos__pm16 = 0x00000002L; +const uint32_t vos__pm32 = 0x00000003L; +const uint32_t vos__windows32 = 0x00000004L; + +const uint32_t vos_dos_windows16 = 0x00010001L; +const uint32_t vos_dos_windows32 = 0x00010004L; +const uint32_t vos_os216_pm16 = 0x00020002L; +const uint32_t vos_os232_pm32 = 0x00030003L; +const uint32_t vos_nt_windows32 = 0x00040004L; + +/* ----- VS_VERSION.dwFileType ----- */ +const uint32_t vft_unknown = 0x00000000L; +const uint32_t vft_app = 0x00000001L; +const uint32_t vft_dll = 0x00000002L; +const uint32_t vft_drv = 0x00000003L; +const uint32_t vft_font = 0x00000004L; +const uint32_t vft_vxd = 0x00000005L; +const uint32_t vft_static_lib = 0x00000007L; + +const uint32_t message_resource_unicode = 0x0001; + +#pragma pack(push, 1) + +//Windows GUID structure +struct guid +{ + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +}; + +//DOS .EXE header +struct image_dos_header +{ + uint16_t e_magic; // Magic number + uint16_t e_cblp; // Bytes on last page of file + uint16_t e_cp; // Pages in file + uint16_t e_crlc; // Relocations + uint16_t e_cparhdr; // Size of header in paragraphs + uint16_t e_minalloc; // Minimum extra paragraphs needed + uint16_t e_maxalloc; // Maximum extra paragraphs needed + uint16_t e_ss; // Initial (relative) SS value + uint16_t e_sp; // Initial SP value + uint16_t e_csum; // Checksum + uint16_t e_ip; // Initial IP value + uint16_t e_cs; // Initial (relative) CS value + uint16_t e_lfarlc; // File address of relocation table + uint16_t e_ovno; // Overlay number + uint16_t e_res[4]; // Reserved words + uint16_t e_oemid; // OEM identifier (for e_oeminfo) + uint16_t e_oeminfo; // OEM information; e_oemid specific + uint16_t e_res2[10]; // Reserved words + int32_t e_lfanew; // File address of new exe header +}; + +//Directory format +struct image_data_directory +{ + uint32_t VirtualAddress; + uint32_t Size; +}; + +//Optional header format +struct image_optional_header32 +{ + //Standard fields + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfData; + + //NT additional fields + uint32_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint32_t SizeOfStackReserve; + uint32_t SizeOfStackCommit; + uint32_t SizeOfHeapReserve; + uint32_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[image_numberof_directory_entries]; +}; + +struct image_optional_header64 +{ + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[image_numberof_directory_entries]; +}; + +struct image_file_header +{ + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +}; + +struct image_nt_headers64 +{ + uint32_t Signature; + image_file_header FileHeader; + image_optional_header64 OptionalHeader; +}; + +struct image_nt_headers32 +{ + uint32_t Signature; + image_file_header FileHeader; + image_optional_header32 OptionalHeader; +}; + +//Section header format +struct image_section_header +{ + uint8_t Name[8]; + union + { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + } Misc; + + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +}; + + +/// RESOURCES /// +struct image_resource_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint16_t NumberOfNamedEntries; + uint16_t NumberOfIdEntries; + // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; +}; + +struct vs_fixedfileinfo +{ + uint32_t dwSignature; /* e.g. 0xfeef04bd */ + uint32_t dwStrucVersion; /* e.g. 0x00000042 = "0.42" */ + uint32_t dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */ + uint32_t dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */ + uint32_t dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */ + uint32_t dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */ + uint32_t dwFileFlagsMask; /* = 0x3F for version "0.42" */ + uint32_t dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */ + uint32_t dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */ + uint32_t dwFileType; /* e.g. VFT_DRIVER */ + uint32_t dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */ + uint32_t dwFileDateMS; /* e.g. 0 */ + uint32_t dwFileDateLS; /* e.g. 0 */ +}; + +struct bitmapinfoheader +{ + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +}; + +struct message_resource_entry +{ + uint16_t Length; + uint16_t Flags; + uint8_t Text[1]; +}; + +struct message_resource_block +{ + uint32_t LowId; + uint32_t HighId; + uint32_t OffsetToEntries; +}; + +struct message_resource_data +{ + uint32_t NumberOfBlocks; + message_resource_block Blocks[1]; +}; + +struct image_resource_directory_entry +{ + union + { + struct + { + uint32_t NameOffset:31; + uint32_t NameIsString:1; + }; + uint32_t Name; + uint16_t Id; + }; + + union + { + uint32_t OffsetToData; + struct + { + uint32_t OffsetToDirectory:31; + uint32_t DataIsDirectory:1; + }; + }; +}; + +struct image_resource_data_entry +{ + uint32_t OffsetToData; + uint32_t Size; + uint32_t CodePage; + uint32_t Reserved; +}; + +#pragma pack(push, 2) +struct bitmapfileheader +{ + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +}; +#pragma pack(pop) + + + +//Structure representing ICON file header +struct ico_header +{ + uint16_t Reserved; + uint16_t Type; //1 + uint16_t Count; //Count of icons included in icon group +}; + +//Structure that is stored in icon group directory in PE resources +struct icon_group +{ + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint16_t Number; //Represents resource ID in PE icon list +}; + +//Structure representing ICON directory entry inside ICON file +struct icondirentry +{ + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint32_t ImageOffset; //Offset from start of header to the image +}; + +//Structure representing CURSOR file header +struct cursor_header +{ + uint16_t Reserved; + uint16_t Type; //2 + uint16_t Count; //Count of cursors included in cursor group +}; + +struct cursor_group +{ + uint16_t Width; + uint16_t Height; //Divide by 2 to get the actual height. + uint16_t Planes; + uint16_t BitCount; + uint32_t SizeInBytes; + uint16_t Number; //Represents resource ID in PE icon list +}; + +//Structure representing CURSOR directory entry inside CURSOR file +struct cursordirentry +{ + uint8_t Width; //Set to CURSOR_GROUP::Height/2. + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; + uint16_t HotspotX; + uint16_t HotspotY; + uint32_t SizeInBytes; + uint32_t ImageOffset; //Offset from start of header to the image +}; + +//Structure representing BLOCK in version info resource +struct version_info_block //(always aligned on 32-bit (DWORD) boundary) +{ + uint16_t Length; //Length of this block (doesn't include padding) + uint16_t ValueLength; //Value length (if any) + uint16_t Type; //Value type (0 = binary, 1 = text) + uint16_t Key[1]; //Value name (block key) (always NULL terminated) + + ////////// + //WORD padding1[]; //Padding, if any (ALIGNMENT) + //xxxxx Value[]; //Value data, if any (*ALIGNED*) + //WORD padding2[]; //Padding, if any (ALIGNMENT) + //xxxxx Child[]; //Child block(s), if any (*ALIGNED*) + ////////// +}; + + +/// IMPORTS /// +#pragma pack(push, 8) +struct image_thunk_data64 +{ + union + { + uint64_t ForwarderString; // PBYTE + uint64_t Function; // PDWORD + uint64_t Ordinal; + uint64_t AddressOfData; // PIMAGE_IMPORT_BY_NAME + } u1; +}; +#pragma pack(pop) + +struct image_thunk_data32 +{ + union + { + uint32_t ForwarderString; // PBYTE + uint32_t Function; // PDWORD + uint32_t Ordinal; + uint32_t AddressOfData; // PIMAGE_IMPORT_BY_NAME + } u1; +}; + +struct image_import_descriptor +{ + union + { + uint32_t Characteristics; // 0 for terminating null import descriptor + uint32_t OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + }; + + uint32_t TimeDateStamp; // 0 if not bound, + // -1 if bound, and real date\time stamp + // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + // O.W. date/time stamp of DLL bound to (Old BIND) + + uint32_t ForwarderChain; // -1 if no forwarders + uint32_t Name; + uint32_t FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) +}; + + +/// TLS /// +struct image_tls_directory64 +{ + uint64_t StartAddressOfRawData; + uint64_t EndAddressOfRawData; + uint64_t AddressOfIndex; // PDWORD + uint64_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *; + uint32_t SizeOfZeroFill; + uint32_t Characteristics; +}; + +struct image_tls_directory32 +{ + uint32_t StartAddressOfRawData; + uint32_t EndAddressOfRawData; + uint32_t AddressOfIndex; // PDWORD + uint32_t AddressOfCallBacks; // PIMAGE_TLS_CALLBACK * + uint32_t SizeOfZeroFill; + uint32_t Characteristics; +}; + + +/// Export Format /// +struct image_export_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Name; + uint32_t Base; + uint32_t NumberOfFunctions; + uint32_t NumberOfNames; + uint32_t AddressOfFunctions; // RVA from base of image + uint32_t AddressOfNames; // RVA from base of image + uint32_t AddressOfNameOrdinals; // RVA from base of image +}; + + +/// Based relocation format /// +struct image_base_relocation +{ + uint32_t VirtualAddress; + uint32_t SizeOfBlock; + // uint16_t TypeOffset[1]; +}; + + +/// New format import descriptors pointed to by DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ] /// +struct image_bound_import_descriptor +{ + uint32_t TimeDateStamp; + uint16_t OffsetModuleName; + uint16_t NumberOfModuleForwarderRefs; + // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows +}; + +struct image_bound_forwarder_ref +{ + uint32_t TimeDateStamp; + uint16_t OffsetModuleName; + uint16_t Reserved; +}; + + +/// Exception directory /// +struct image_runtime_function_entry +{ + uint32_t BeginAddress; + uint32_t EndAddress; + uint32_t UnwindInfoAddress; +}; + +enum unwind_op_codes +{ + uwop_push_nonvol = 0, /* info == register number */ + uwop_alloc_large, /* no info, alloc size in next 2 slots */ + uwop_alloc_small, /* info == size of allocation / 8 - 1 */ + uwop_set_fpreg, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ + uwop_save_nonvol, /* info == register number, offset in next slot */ + uwop_save_nonvol_far, /* info == register number, offset in next 2 slots */ + uwop_save_xmm128, /* info == XMM reg number, offset in next slot */ + uwop_save_xmm128_far, /* info == XMM reg number, offset in next 2 slots */ + uwop_push_machframe /* info == 0: no error-code, 1: error-code */ +}; + +union unwind_code +{ + struct s + { + uint8_t CodeOffset; + uint8_t UnwindOp : 4; + uint8_t OpInfo : 4; + }; + + uint16_t FrameOffset; +}; + +struct unwind_info +{ + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + unwind_code UnwindCode[1]; + /* unwind_code MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1]; + * union { + * OPTIONAL ULONG ExceptionHandler; + * OPTIONAL ULONG FunctionEntry; + * }; + * OPTIONAL ULONG ExceptionData[]; */ +}; + + + +/// Debug /// +struct image_debug_misc +{ + uint32_t DataType; // type of misc data, see defines + uint32_t Length; // total length of record, rounded to four + // byte multiple. + uint8_t Unicode; // TRUE if data is unicode string + uint8_t Reserved[3]; + uint8_t Data[1]; // Actual data +}; + +struct image_coff_symbols_header +{ + uint32_t NumberOfSymbols; + uint32_t LvaToFirstSymbol; + uint32_t NumberOfLinenumbers; + uint32_t LvaToFirstLinenumber; + uint32_t RvaToFirstByteOfCode; + uint32_t RvaToLastByteOfCode; + uint32_t RvaToFirstByteOfData; + uint32_t RvaToLastByteOfData; +}; + +struct image_debug_directory +{ + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Type; + uint32_t SizeOfData; + uint32_t AddressOfRawData; + uint32_t PointerToRawData; +}; + + +#pragma pack(push, 2) +struct image_symbol +{ + union + { + uint8_t ShortName[8]; + struct + { + uint32_t Short; // if 0, use LongName + uint32_t Long; // offset into string table + } Name; + uint32_t LongName[2]; // PBYTE [2] + } N; + uint32_t Value; + int16_t SectionNumber; + uint16_t Type; + uint8_t StorageClass; + uint8_t NumberOfAuxSymbols; +}; +#pragma pack(pop) + +//CodeView Debug OMF signature. The signature at the end of the file is +//a negative offset from the end of the file to another signature. At +//the negative offset (base address) is another signature whose filepos +//field points to the first OMFDirHeader in a chain of directories. +//The NB05 signature is used by the link utility to indicated a completely +//unpacked file. The NB06 signature is used by ilink to indicate that the +//executable has had CodeView information from an incremental link appended +//to the executable. The NB08 signature is used by cvpack to indicate that +//the CodeView Debug OMF has been packed. CodeView will only process +//executables with the NB08 signature. +struct OMFSignature +{ + char Signature[4]; // "NBxx" + uint32_t filepos; // offset in file +}; + +struct CV_INFO_PDB20 +{ + OMFSignature CvHeader; + uint32_t Signature; + uint32_t Age; + uint8_t PdbFileName[1]; +}; + +struct CV_INFO_PDB70 +{ + uint32_t CvSignature; + guid Signature; + uint32_t Age; + uint8_t PdbFileName[1]; +}; + +// directory information structure +// This structure contains the information describing the directory. +// It is pointed to by the signature at the base address or the directory +// link field of a preceeding directory. The directory entries immediately +// follow this structure. +struct OMFDirHeader +{ + uint16_t cbDirHeader; // length of this structure + uint16_t cbDirEntry; // number of bytes in each directory entry + uint32_t cDir; // number of directorie entries + int32_t lfoNextDir; // offset from base of next directory + uint32_t flags; // status flags +}; + +// directory structure +// The data in this structure is used to reference the data for each +// subsection of the CodeView Debug OMF information. Tables that are +// not associated with a specific module will have a module index of +// oxffff. These tables are the global types table, the global symbol +// table, the global public table and the library table. +struct OMFDirEntry +{ + uint16_t SubSection; // subsection type (sst...) + uint16_t iMod; // module index + int32_t lfo; // large file offset of subsection + uint32_t cb; // number of bytes in subsection +}; + + +/// CLR 2.0 header structure /// +struct image_cor20_header +{ + //Header versioning + uint32_t cb; + uint16_t MajorRuntimeVersion; + uint16_t MinorRuntimeVersion; + + // Symbol table and startup information + image_data_directory MetaData; + uint32_t Flags; + + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint. + // If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint. + union + { + uint32_t EntryPointToken; + uint32_t EntryPointRVA; + }; + + // Binding information + image_data_directory Resources; + image_data_directory StrongNameSignature; + + // Regular fixup and binding information + image_data_directory CodeManagerTable; + image_data_directory VTableFixups; + image_data_directory ExportAddressTableJumps; + + // Precompiled image info (internal use only - set to zero) + image_data_directory ManagedNativeHeader; +}; + +enum replaces_cor_hdr_numeric_defines +{ + // COM+ Header entry point flags. + comimage_flags_ilonly =0x00000001, + comimage_flags_32bitrequired =0x00000002, + comimage_flags_il_library =0x00000004, + comimage_flags_strongnamesigned =0x00000008, + comimage_flags_native_entrypoint =0x00000010, + comimage_flags_trackdebugdata =0x00010000, + + // Version flags for image. + cor_version_major_v2 =2, + cor_version_major =cor_version_major_v2, + cor_version_minor =0, + cor_deleted_name_length =8, + cor_vtablegap_name_length =8, + + // Maximum size of a NativeType descriptor. + native_type_max_cb =1, + cor_ilmethod_sect_small_max_datasize=0xff, + + // #defines for the MIH FLAGS + image_cor_mih_methodrva =0x01, + image_cor_mih_ehrva =0x02, + image_cor_mih_basicblock =0x08, + + // V-table constants + cor_vtable_32bit =0x01, // V-table slots are 32-bits in size. + cor_vtable_64bit =0x02, // V-table slots are 64-bits in size. + cor_vtable_from_unmanaged =0x04, // If set, transition from unmanaged. + cor_vtable_from_unmanaged_retain_appdomain =0x08, // If set, transition from unmanaged with keeping the current appdomain. + cor_vtable_call_most_derived =0x10, // Call most derived method described by + + // EATJ constants + image_cor_eatj_thunk_size =32, // Size of a jump thunk reserved range. + + // Max name lengths + //@todo: Change to unlimited name lengths. + max_class_name =1024, + max_package_name =1024 +}; + +/// Load Configuration Directory Entry /// +struct image_load_config_directory32 +{ + uint32_t Size; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t GlobalFlagsClear; + uint32_t GlobalFlagsSet; + uint32_t CriticalSectionDefaultTimeout; + uint32_t DeCommitFreeBlockThreshold; + uint32_t DeCommitTotalFreeThreshold; + uint32_t LockPrefixTable; // VA + uint32_t MaximumAllocationSize; + uint32_t VirtualMemoryThreshold; + uint32_t ProcessHeapFlags; + uint32_t ProcessAffinityMask; + uint16_t CSDVersion; + uint16_t Reserved1; + uint32_t EditList; // VA + uint32_t SecurityCookie; // VA + uint32_t SEHandlerTable; // VA + uint32_t SEHandlerCount; +}; + +struct image_load_config_directory64 +{ + uint32_t Size; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t GlobalFlagsClear; + uint32_t GlobalFlagsSet; + uint32_t CriticalSectionDefaultTimeout; + uint64_t DeCommitFreeBlockThreshold; + uint64_t DeCommitTotalFreeThreshold; + uint64_t LockPrefixTable; // VA + uint64_t MaximumAllocationSize; + uint64_t VirtualMemoryThreshold; + uint64_t ProcessAffinityMask; + uint32_t ProcessHeapFlags; + uint16_t CSDVersion; + uint16_t Reserved1; + uint64_t EditList; // VA + uint64_t SecurityCookie; // VA + uint64_t SEHandlerTable; // VA + uint64_t SEHandlerCount; +}; + +#pragma pack(pop) +} //namespace pe_win + +#ifdef PE_BLISS_WINDOWS +typedef wchar_t unicode16_t; +typedef std::basic_string<unicode16_t> u16string; +#else +//Instead of wchar_t for windows +typedef unsigned short unicode16_t; +typedef std::basic_string<unicode16_t> u16string; +#endif + +} //namespace pe_bliss diff --git a/tools/pe_bliss/pe_tls.cpp b/tools/pe_bliss/pe_tls.cpp new file mode 100644 index 0000000000..5ec68e3f10 --- /dev/null +++ b/tools/pe_bliss/pe_tls.cpp @@ -0,0 +1,396 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "pe_tls.h" +#include "pe_properties_generic.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//TLS +//Default constructor +tls_info::tls_info() + :start_rva_(0), end_rva_(0), index_rva_(0), callbacks_rva_(0), + size_of_zero_fill_(0), characteristics_(0) +{} + +//Returns start RVA of TLS raw data +uint32_t tls_info::get_raw_data_start_rva() const +{ + return start_rva_; +} + +//Returns end RVA of TLS raw data +uint32_t tls_info::get_raw_data_end_rva() const +{ + return end_rva_; +} + +//Returns TLS index RVA +uint32_t tls_info::get_index_rva() const +{ + return index_rva_; +} + +//Returns TLS callbacks RVA +uint32_t tls_info::get_callbacks_rva() const +{ + return callbacks_rva_; +} + +//Returns size of zero fill +uint32_t tls_info::get_size_of_zero_fill() const +{ + return size_of_zero_fill_; +} + +//Returns characteristics +uint32_t tls_info::get_characteristics() const +{ + return characteristics_; +} + +//Returns raw TLS data +const std::string& tls_info::get_raw_data() const +{ + return raw_data_; +} + +//Returns TLS callbacks addresses +const tls_info::tls_callback_list& tls_info::get_tls_callbacks() const +{ + return callbacks_; +} + +//Returns TLS callbacks addresses +tls_info::tls_callback_list& tls_info::get_tls_callbacks() +{ + return callbacks_; +} + +//Adds TLS callback +void tls_info::add_tls_callback(uint32_t rva) +{ + callbacks_.push_back(rva); +} + +//Clears TLS callbacks list +void tls_info::clear_tls_callbacks() +{ + callbacks_.clear(); +} + +//Recalculates end address of raw TLS data +void tls_info::recalc_raw_data_end_rva() +{ + end_rva_ = static_cast<uint32_t>(start_rva_ + raw_data_.length()); +} + +//Sets start RVA of TLS raw data +void tls_info::set_raw_data_start_rva(uint32_t rva) +{ + start_rva_ = rva; +} + +//Sets end RVA of TLS raw data +void tls_info::set_raw_data_end_rva(uint32_t rva) +{ + end_rva_ = rva; +} + +//Sets TLS index RVA +void tls_info::set_index_rva(uint32_t rva) +{ + index_rva_ = rva; +} + +//Sets TLS callbacks RVA +void tls_info::set_callbacks_rva(uint32_t rva) +{ + callbacks_rva_ = rva; +} + +//Sets size of zero fill +void tls_info::set_size_of_zero_fill(uint32_t size) +{ + size_of_zero_fill_ = size; +} + +//Sets characteristics +void tls_info::set_characteristics(uint32_t characteristics) +{ + characteristics_ = characteristics; +} + +//Sets raw TLS data +void tls_info::set_raw_data(const std::string& data) +{ + raw_data_ = data; +} + +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe) +{ + return pe.get_pe_type() == pe_type_32 + ? get_tls_info_base<pe_types_class_32>(pe) + : get_tls_info_base<pe_types_class_64>(pe); +} + +//TLS Rebuilder +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + return pe.get_pe_type() == pe_type_32 + ? rebuild_tls_base<pe_types_class_32>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section) + : rebuild_tls_base<pe_types_class_64>(pe, info, tls_section, offset_from_section_start, write_tls_callbacks, write_tls_data, expand, save_to_pe_header, auto_strip_last_section); +} + +//Get TLS info +//If image does not have TLS, throws an exception +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe) +{ + tls_info ret; + + //If there's no TLS directory, throw an exception + if(!pe.has_tls()) + throw pe_exception("Image does not have TLS directory", pe_exception::directory_does_not_exist); + + //Get TLS directory data + typename PEClassType::TLSStruct tls_directory_data = pe.section_data_from_rva<typename PEClassType::TLSStruct>(pe.get_directory_rva(image_directory_entry_tls), section_data_virtual, true); + + //Check data addresses + if(tls_directory_data.EndAddressOfRawData == tls_directory_data.StartAddressOfRawData) + { + try + { + pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)); + } + catch(const pe_exception&) + { + //Fix addressess on incorrect conversion + tls_directory_data.EndAddressOfRawData = tls_directory_data.StartAddressOfRawData = 0; + } + } + + if(tls_directory_data.StartAddressOfRawData && + pe.section_data_length_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), + static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true) + < (tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData)) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + //Fill TLS info + //VAs are not checked + ret.set_raw_data_start_rva(tls_directory_data.StartAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData)) : 0); + ret.set_raw_data_end_rva(tls_directory_data.EndAddressOfRawData ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.EndAddressOfRawData)) : 0); + ret.set_index_rva(tls_directory_data.AddressOfIndex ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfIndex)) : 0); + ret.set_callbacks_rva(tls_directory_data.AddressOfCallBacks ? pe.va_to_rva(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks)) : 0); + ret.set_size_of_zero_fill(tls_directory_data.SizeOfZeroFill); + ret.set_characteristics(tls_directory_data.Characteristics); + + if(tls_directory_data.StartAddressOfRawData && tls_directory_data.StartAddressOfRawData != tls_directory_data.EndAddressOfRawData) + { + //Read and save TLS RAW data + ret.set_raw_data(std::string( + pe.section_data_from_va(static_cast<typename PEClassType::BaseSize>(tls_directory_data.StartAddressOfRawData), section_data_virtual, true), + static_cast<uint32_t>(tls_directory_data.EndAddressOfRawData - tls_directory_data.StartAddressOfRawData))); + } + + //If file has TLS callbacks + if(ret.get_callbacks_rva()) + { + //Read callbacks VAs + uint32_t current_tls_callback = 0; + + while(true) + { + //Read TLS callback VA + typename PEClassType::BaseSize va = pe.section_data_from_va<typename PEClassType::BaseSize>(static_cast<typename PEClassType::BaseSize>(tls_directory_data.AddressOfCallBacks + current_tls_callback), section_data_virtual, true); + if(va == 0) + break; + + //Save it + ret.add_tls_callback(pe.va_to_rva(va, false)); + + //Move to next callback VA + current_tls_callback += sizeof(va); + } + } + + return ret; +} + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +//Note/TODO: TLS Callbacks array is not DWORD-aligned (seems to work on WinXP - Win7) +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start, bool write_tls_callbacks, bool write_tls_data, tls_data_expand_type expand, bool save_to_pe_header, bool auto_strip_last_section) +{ + //Check that tls_section is attached to this PE image + if(!pe.section_attached(tls_section)) + throw pe_exception("TLS section must be attached to PE file", pe_exception::section_is_not_attached); + + uint32_t tls_data_pos = pe_utils::align_up(offset_from_section_start, sizeof(typename PEClassType::BaseSize)); + uint32_t needed_size = sizeof(typename PEClassType::TLSStruct); //Calculate needed size for TLS table + + //Check if tls_section is last one. If it's not, check if there's enough place for TLS data + if(&tls_section != &*(pe.get_image_sections().end() - 1) && + (tls_section.empty() || pe_utils::align_up(tls_section.get_size_of_raw_data(), pe.get_file_alignment()) < needed_size + tls_data_pos)) + throw pe_exception("Insufficient space for TLS directory", pe_exception::insufficient_space); + + //Check raw data positions + if(info.get_raw_data_end_rva() < info.get_raw_data_start_rva() || info.get_index_rva() == 0) + throw pe_exception("Incorrect TLS directory", pe_exception::incorrect_tls_directory); + + std::string& raw_data = tls_section.get_raw_data(); + + //This will be done only if tls_section is the last section of image or for section with unaligned raw length of data + if(raw_data.length() < needed_size + tls_data_pos) + raw_data.resize(needed_size + tls_data_pos); //Expand section raw data + + //Create and fill TLS structure + typename PEClassType::TLSStruct tls_struct = {0}; + + typename PEClassType::BaseSize va; + if(info.get_raw_data_start_rva()) + { + pe.rva_to_va(info.get_raw_data_start_rva(), va); + tls_struct.StartAddressOfRawData = va; + tls_struct.SizeOfZeroFill = info.get_size_of_zero_fill(); + } + + if(info.get_raw_data_end_rva()) + { + pe.rva_to_va(info.get_raw_data_end_rva(), va); + tls_struct.EndAddressOfRawData = va; + } + + pe.rva_to_va(info.get_index_rva(), va); + tls_struct.AddressOfIndex = va; + + if(info.get_callbacks_rva()) + { + pe.rva_to_va(info.get_callbacks_rva(), va); + tls_struct.AddressOfCallBacks = va; + } + + tls_struct.Characteristics = info.get_characteristics(); + + //Save TLS structure + memcpy(&raw_data[tls_data_pos], &tls_struct, sizeof(tls_struct)); + + //If we are asked to rewrite TLS raw data + if(write_tls_data && info.get_raw_data_start_rva() && info.get_raw_data_start_rva() != info.get_raw_data_end_rva()) + { + try + { + //Check if we're going to write TLS raw data to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_raw_data_start_rva()); + pe.expand_section(raw_data_section, info.get_raw_data_start_rva(), info.get_raw_data_end_rva() - info.get_raw_data_start_rva(), expand == tls_data_expand_raw ? pe_base::expand_section_raw : pe_base::expand_section_virtual); + } + catch(const pe_exception&) + { + //If no section is presented by StartAddressOfRawData, just go to next step + } + + unsigned long write_raw_data_size = info.get_raw_data_end_rva() - info.get_raw_data_start_rva(); + unsigned long available_raw_length = 0; + + //Check if there's enough RAW space to write raw TLS data... + if((available_raw_length = pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_raw, true)) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + { + //Check if there's enough virtual space for it... + if(pe.section_data_length_from_rva(info.get_raw_data_start_rva(), info.get_raw_data_start_rva(), section_data_virtual, true) + < info.get_raw_data_end_rva() - info.get_raw_data_start_rva()) + throw pe_exception("Insufficient space for TLS raw data", pe_exception::insufficient_space); + else + write_raw_data_size = available_raw_length; //We'll write just a part of full raw data + } + + //Write raw TLS data, if any + if(write_raw_data_size != 0) + memcpy(pe.section_data_from_rva(info.get_raw_data_start_rva(), true), info.get_raw_data().data(), write_raw_data_size); + } + + //If we are asked to rewrite TLS callbacks addresses + if(write_tls_callbacks && info.get_callbacks_rva()) + { + unsigned long needed_callback_size = static_cast<unsigned long>((info.get_tls_callbacks().size() + 1 /* last null element */) * sizeof(typename PEClassType::BaseSize)); + + try + { + //Check if we're going to write TLS callbacks VAs to an existing section (not to PE headers) + section& raw_data_section = pe.section_from_rva(info.get_callbacks_rva()); + pe.expand_section(raw_data_section, info.get_callbacks_rva(), needed_callback_size, pe_base::expand_section_raw); + } + catch(const pe_exception&) + { + //If no section is presented by RVA of callbacks, just go to next step + } + + //Check if there's enough space to write callbacks TLS data... + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_raw, true) + < needed_callback_size - sizeof(typename PEClassType::BaseSize) /* last zero element can be virtual only */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + if(pe.section_data_length_from_rva(info.get_callbacks_rva(), info.get_callbacks_rva(), section_data_virtual, true) + < needed_callback_size /* check here full virtual data length available */) + throw pe_exception("Insufficient space for TLS callbacks data", pe_exception::insufficient_space); + + std::vector<typename PEClassType::BaseSize> callbacks_virtual_addresses; + callbacks_virtual_addresses.reserve(info.get_tls_callbacks().size() + 1 /* last null element */); + + //Convert TLS RVAs to VAs + for(tls_info::tls_callback_list::const_iterator it = info.get_tls_callbacks().begin(); it != info.get_tls_callbacks().end(); ++it) + { + typename PEClassType::BaseSize cb_va = 0; + pe.rva_to_va(*it, cb_va); + callbacks_virtual_addresses.push_back(cb_va); + } + + //Ending null element + callbacks_virtual_addresses.push_back(0); + + //Write callbacks TLS data + memcpy(pe.section_data_from_rva(info.get_callbacks_rva(), true), &callbacks_virtual_addresses[0], needed_callback_size); + } + + //Adjust section raw and virtual sizes + pe.recalculate_section_sizes(tls_section, auto_strip_last_section); + + image_directory ret(pe.rva_from_section_offset(tls_section, tls_data_pos), needed_size); + + //If auto-rewrite of PE headers is required + if(save_to_pe_header) + { + pe.set_directory_rva(image_directory_entry_tls, ret.get_rva()); + pe.set_directory_size(image_directory_entry_tls, ret.get_size()); + } + + return ret; +} +} diff --git a/tools/pe_bliss/pe_tls.h b/tools/pe_bliss/pe_tls.h new file mode 100644 index 0000000000..316e208147 --- /dev/null +++ b/tools/pe_bliss/pe_tls.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* 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 <memory> +#include <istream> +#include "pe_base.h" +#include "pe_directory.h" + +namespace pe_bliss +{ +//Class representing TLS info +//We use "DWORD" type to represent RVAs, because RVA is +//always 32bit even in PE+ +class tls_info +{ +public: + typedef std::vector<uint32_t> tls_callback_list; + +public: + //Default constructor + tls_info(); + + //Returns start RVA of TLS raw data + uint32_t get_raw_data_start_rva() const; + //Returns end RVA of TLS raw data + uint32_t get_raw_data_end_rva() const; + //Returns TLS index RVA + uint32_t get_index_rva() const; + //Returns TLS callbacks RVA + uint32_t get_callbacks_rva() const; + //Returns size of zero fill + uint32_t get_size_of_zero_fill() const; + //Returns characteristics + uint32_t get_characteristics() const; + //Returns raw TLS data + const std::string& get_raw_data() const; + //Returns TLS callbacks addresses + const tls_callback_list& get_tls_callbacks() const; + +public: //These functions do not change everything inside image, they are used by PE class + //You can also use them to rebuild TLS directory + + //Sets start RVA of TLS raw data + void set_raw_data_start_rva(uint32_t rva); + //Sets end RVA of TLS raw data + void set_raw_data_end_rva(uint32_t rva); + //Sets TLS index RVA + void set_index_rva(uint32_t rva); + //Sets TLS callbacks RVA + void set_callbacks_rva(uint32_t rva); + //Sets size of zero fill + void set_size_of_zero_fill(uint32_t size); + //Sets characteristics + void set_characteristics(uint32_t characteristics); + //Sets raw TLS data + void set_raw_data(const std::string& data); + //Returns TLS callbacks addresses + tls_callback_list& get_tls_callbacks(); + //Adds TLS callback + void add_tls_callback(uint32_t rva); + //Clears TLS callbacks list + void clear_tls_callbacks(); + //Recalculates end address of raw TLS data + void recalc_raw_data_end_rva(); + +private: + uint32_t start_rva_, end_rva_, index_rva_, callbacks_rva_; + uint32_t size_of_zero_fill_, characteristics_; + + //Raw TLS data + std::string raw_data_; + + //TLS callback RVAs + tls_callback_list callbacks_; +}; + +//Represents type of expanding of TLS section containing raw data +//(Works only if you are writing TLS raw data to tls_section and it is the last one in the PE image on the moment of TLS rebuild) +enum tls_data_expand_type +{ + tls_data_expand_raw, //If there is not enough raw space for raw TLS data, it can be expanded + tls_data_expand_virtual //If there is not enough virtual place for raw TLS data, it can be expanded +}; + + +//Get TLS info +//If image does not have TLS, throws an exception +const tls_info get_tls_info(const pe_base& pe); + +template<typename PEClassType> +const tls_info get_tls_info_base(const pe_base& pe); + +//Rebuilder of TLS structures +//If write_tls_callbacks = true, TLS callbacks VAs will be written to their place +//If write_tls_data = true, TLS data will be written to its place +//If you have chosen to rewrite raw data, only (EndAddressOfRawData - StartAddressOfRawData) bytes will be written, not the full length of string +//representing raw data content +//auto_strip_last_section - if true and TLS are placed in the last section, it will be automatically stripped +const image_directory rebuild_tls(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); + +template<typename PEClassType> +const image_directory rebuild_tls_base(pe_base& pe, const tls_info& info, section& tls_section, uint32_t offset_from_section_start = 0, bool write_tls_callbacks = true, bool write_tls_data = true, tls_data_expand_type expand = tls_data_expand_raw, bool save_to_pe_header = true, bool auto_strip_last_section = true); +} diff --git a/tools/pe_bliss/resource_bitmap_reader.cpp b/tools/pe_bliss/resource_bitmap_reader.cpp new file mode 100644 index 0000000000..3546461f53 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_reader.cpp @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* 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 <cmath> +#include "resource_bitmap_reader.h" +#include "pe_resource_viewer.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_reader::resource_bitmap_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(const std::wstring& name, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_name(pe_resource_viewer::resource_bitmap, name, index).get_data()); +} + +//Returns bitmap data by name and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_name(uint32_t language, const std::wstring& name) const +{ + return create_bitmap(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_bitmap, name).get_data()); +} + +//Returns bitmap data by ID and language (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id_lang(uint32_t language, uint32_t id) const +{ + return create_bitmap(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_bitmap, id).get_data()); +} + +//Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_bitmap_reader::get_bitmap_by_id(uint32_t id, uint32_t index) const +{ + return create_bitmap(res_.get_resource_data_by_id(pe_resource_viewer::resource_bitmap, id, index).get_data()); +} + +//Helper function of creating bitmap header +const std::string resource_bitmap_reader::create_bitmap(const std::string& resource_data) +{ + //Create bitmap file header + bitmapfileheader header = {0}; + header.bfType = 0x4d42; //Signature "BM" + header.bfOffBits = sizeof(bitmapfileheader) + sizeof(bitmapinfoheader); //Offset to bitmap bits + header.bfSize = static_cast<uint32_t>(sizeof(bitmapfileheader) + resource_data.length()); //Size of bitmap + + //Check size of resource data + if(resource_data.length() < sizeof(bitmapinfoheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + { + //Get bitmap info header + const bitmapinfoheader* info = reinterpret_cast<const bitmapinfoheader*>(resource_data.data()); + + //If color table is present, skip it + if(info->biClrUsed != 0) + header.bfOffBits += 4 * info->biClrUsed; //Add this size to offset to bitmap bits + else if(info->biBitCount <= 8) + header.bfOffBits += 4 * static_cast<uint32_t>(std::pow(2.f, info->biBitCount)); //Add this size to offset to bitmap bits + } + + //Return final bitmap data + return std::string(reinterpret_cast<const char*>(&header), sizeof(bitmapfileheader)) + resource_data; +} +} diff --git a/tools/pe_bliss/resource_bitmap_reader.h b/tools/pe_bliss/resource_bitmap_reader.h new file mode 100644 index 0000000000..f2b92bbde7 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_reader.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* 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 "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_bitmap_reader +{ +public: + resource_bitmap_reader(const pe_resource_viewer& res); + + //Returns bitmap data by name and language (minimum checks of format correctness) + const std::string get_bitmap_by_name(uint32_t language, const std::wstring& name) const; + //Returns bitmap data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_name(const std::wstring& name, uint32_t index = 0) const; + //Returns bitmap data by ID and language (minimum checks of format correctness) + const std::string get_bitmap_by_id_lang(uint32_t language, uint32_t id) const; + //Returns bitmap data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_bitmap_by_id(uint32_t id, uint32_t index = 0) const; + +private: + //Helper function of creating bitmap header + static const std::string create_bitmap(const std::string& resource_data); + + const pe_resource_viewer& res_; +}; +} diff --git a/tools/pe_bliss/resource_bitmap_writer.cpp b/tools/pe_bliss/resource_bitmap_writer.cpp new file mode 100644 index 0000000000..3445a08445 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_writer.cpp @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* 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 "resource_bitmap_writer.h" +#include "pe_resource_manager.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_bitmap_writer::resource_bitmap_writer(pe_resource_manager& res) + :res_(res) +{} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_id(id); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(id), language, codepage, timestamp); +} + +//Adds bitmap from bitmap file data. If bitmap already exists, replaces it +//timestamp will be used for directories that will be added +void resource_bitmap_writer::add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage, uint32_t timestamp) +{ + //Check bitmap data a little + if(bitmap_file.length() < sizeof(bitmapfileheader)) + throw pe_exception("Incorrect resource bitmap", pe_exception::resource_incorrect_bitmap); + + resource_directory_entry new_entry; + new_entry.set_name(name); + + //Add bitmap + res_.add_resource(bitmap_file.substr(sizeof(bitmapfileheader)), pe_resource_viewer::resource_bitmap, new_entry, resource_directory::entry_finder(name), language, codepage, timestamp); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(const std::wstring& name, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, name, language); +} + +//Removes bitmap by name/ID and language +bool resource_bitmap_writer::remove_bitmap(uint32_t id, uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_bitmap, id, language); +} +} diff --git a/tools/pe_bliss/resource_bitmap_writer.h b/tools/pe_bliss/resource_bitmap_writer.h new file mode 100644 index 0000000000..4b8ea72705 --- /dev/null +++ b/tools/pe_bliss/resource_bitmap_writer.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* 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 "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_bitmap_writer +{ +public: + resource_bitmap_writer(pe_resource_manager& res); + + //Adds bitmap from bitmap file data. If bitmap already exists, replaces it + //timestamp will be used for directories that will be added + void add_bitmap(const std::string& bitmap_file, uint32_t id, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_bitmap(const std::string& bitmap_file, const std::wstring& name, uint32_t language, uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes bitmap by name/ID and language + bool remove_bitmap(const std::wstring& name, uint32_t language); + bool remove_bitmap(uint32_t id, uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/tools/pe_bliss/resource_cursor_icon_reader.cpp b/tools/pe_bliss/resource_cursor_icon_reader.cpp new file mode 100644 index 0000000000..28a259163e --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_reader.cpp @@ -0,0 +1,521 @@ +/*************************************************************************/ +/* 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 <algorithm> +#include "resource_cursor_icon_reader.h" +#include "pe_structures.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_reader::resource_cursor_icon_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of creating icon headers from ICON_GROUP resource data +//Returns icon count +uint16_t resource_cursor_icon_reader::format_icon_headers(std::string& ico_data, const std::string& resource_data) +{ + //Check resource data size + if(resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(resource_data.data()); + + //Check resource data size + if(resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + info->Count * sizeof(icondirentry)); + ico_data.append(reinterpret_cast<const char*>(info), sizeof(ico_header)); + + //Iterate over all listed icons + uint32_t offset = sizeof(ico_header) + sizeof(icondirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = offset; + + //Add icon header to returned value + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(icondirentry)); + + offset += group->SizeInBytes; + } + + //Return icon count + return info->Count; +} + +//Returns single icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, language)); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, id).get_data()); + return icon_data; +} + +//Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_icon_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_icon, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + //Get icon headers + std::string icon_data(lookup_icon_group_data_by_icon(id, languages.at(index))); + //Append icon data + icon_data.append(res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, id, index).get_data()); + return icon_data; +} + +//Returns icon data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string data = res_.get_resource_data_by_name(pe_resource_viewer::resource_icon_group, name, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Returns icon data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, name).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by language and id + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, id).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon, group->Number).get_data(); + } + + return ret; +} + +//Returns icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_icon_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by id and index + const std::string data = res_.get_resource_data_by_id(pe_resource_viewer::resource_icon_group, id, index).get_data(); + + //Create icon headers + uint16_t icon_count = format_icon_headers(ret, data); + + //Append icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_icon, group->Number, index).get_data(); + } + + return ret; +} + +//Checks for icon presence inside icon group, fills icon headers if found +bool resource_cursor_icon_reader::check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data) +{ + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_resource_data.data()); + + //Check resource data size + if(icon_group_resource_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + for(uint16_t i = 0; i != info->Count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_resource_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + if(group->Number == icon_id) + { + //Reserve memory to speed up a little + ico_data.reserve(sizeof(ico_header) + sizeof(icondirentry)); + //Write single-icon icon header + ico_header new_header = *info; + new_header.Count = 1; + ico_data.append(reinterpret_cast<const char*>(&new_header), sizeof(ico_header)); + + //Fill icon data + icondirentry direntry; + direntry.BitCount = group->BitCount; + direntry.ColorCount = group->ColorCount; + direntry.Height = group->Height; + direntry.Planes = group->Planes; + direntry.Reserved = group->Reserved; + direntry.SizeInBytes = group->SizeInBytes; + direntry.Width = group->Width; + direntry.ImageOffset = sizeof(ico_header) + sizeof(icondirentry); + ico_data.append(reinterpret_cast<const char*>(&direntry), sizeof(direntry)); + + return true; + } + } + + return false; +} + +//Looks up icon group by icon id and returns full icon headers if found +const std::string resource_cursor_icon_reader::lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const +{ + std::string icon_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_icon_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_icon_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_icon_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_icon_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, *it).get_data(), icon_id, icon_header_data)) + return icon_header_data; + } + } + + throw pe_exception("No icon group find for requested icon", pe_exception::no_icon_group_found); +} + +//Returns single cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string raw_cursor_data(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, id).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, language, raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_single_cursor_by_id(uint32_t id, uint32_t index) const +{ + pe_resource_viewer::resource_language_list languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor, id)); + if(languages.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + std::string raw_cursor_data(res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, id, index).get_data()); + //Get cursor headers + std::string cursor_data(lookup_cursor_group_data_by_cursor(id, languages.at(index), raw_cursor_data)); + //Append cursor data + cursor_data.append(raw_cursor_data.substr(sizeof(uint16_t) * 2 /* hotspot position */)); + return cursor_data; +} + +//Helper function of creating cursor headers +//Returns cursor count +uint16_t resource_cursor_icon_reader::format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index) const +{ + //Check resource data length + if(resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(resource_data.data()); + + //Check resource data length + if(resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group) * info->Count) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Reserve needed space to speed up a little + cur_data.reserve(sizeof(cursor_header) + info->Count * sizeof(cursordirentry)); + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(info), sizeof(cursor_header)); + + //Iterate over all cursors listed in cursor group + uint32_t offset = sizeof(cursor_header) + sizeof(cursordirentry) * info->Count; + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + //Now read hotspot data from cursor data directory + const std::string cursor = index == 0xFFFFFFFF + ? res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data() + : res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data(); + if(cursor.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(cursor.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(cursor.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = offset; + + //Add cursor header + cur_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + offset += direntry.SizeInBytes; + } + + //Return cursor count + return info->Count; +} + +//Returns cursor data by name and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(uint32_t language, const std::wstring& name) const +{ + std::string ret; + + //Get resource by name and language + const std::string resource_data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, name).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_name(const std::wstring& name, uint32_t index) const +{ + std::string ret; + + //Get resource by name and index + const std::string resource_data = res_.get_resource_data_by_name(pe_resource_viewer::resource_cursor_group, name, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and language (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id_lang(uint32_t language, uint32_t id) const +{ + std::string ret; + + //Get resource by ID and language + const std::string resource_data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, id).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, language); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor, group->Number).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) +const std::string resource_cursor_icon_reader::get_cursor_by_id(uint32_t id, uint32_t index) const +{ + std::string ret; + + //Get resource by ID and index + const std::string resource_data = res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor_group, id, index).get_data(); + + //Create cursor headers + uint16_t cursor_count = format_cursor_headers(ret, resource_data, 0, index); + + //Add cursor data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + ret += res_.get_resource_data_by_id(pe_resource_viewer::resource_cursor, group->Number, index).get_data().substr(2 * sizeof(uint16_t)); + } + + return ret; +} + +//Checks for cursor presence inside cursor group, fills cursor headers if found +bool resource_cursor_icon_reader::check_cursor_presence(const std::string& cursor_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data) +{ + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_resource_data.data()); + + //Check resource data length + if(cursor_group_resource_data.length() < sizeof(cursor_header) + sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Iterate over all cursors listed in cursor group + for(uint16_t i = 0; i != info->Count; ++i) + { + const cursor_group* group = reinterpret_cast<const cursor_group*>(cursor_group_resource_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + + if(group->Number == cursor_id) + { + //Reserve needed space to speed up a little + cur_header_data.reserve(sizeof(cursor_header) + sizeof(cursordirentry)); + //Write single-cursor cursor header + cursor_header new_header = *info; + new_header.Count = 1; + cur_header_data.append(reinterpret_cast<const char*>(&new_header), sizeof(cursor_header)); + + //Fill cursor info + cursordirentry direntry; + direntry.ColorCount = 0; //OK + direntry.Width = static_cast<uint8_t>(group->Width); + direntry.Height = static_cast<uint8_t>(group->Height) / 2; + direntry.Reserved = 0; + + if(raw_cursor_data.length() < 2 * sizeof(uint16_t)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Here it is - two words in the very beginning of cursor data + direntry.HotspotX = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data()); + direntry.HotspotY = *reinterpret_cast<const uint16_t*>(raw_cursor_data.data() + sizeof(uint16_t)); + + //Fill the rest data + direntry.SizeInBytes = group->SizeInBytes - 2 * sizeof(uint16_t); + direntry.ImageOffset = sizeof(cursor_header) + sizeof(cursordirentry); + + //Add cursor header + cur_header_data.append(reinterpret_cast<const char*>(&direntry), sizeof(cursordirentry)); + + return true; + } + } + + return false; +} + +//Looks up cursor group by cursor id and returns full cursor headers if found +const std::string resource_cursor_icon_reader::lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const +{ + std::string cursor_header_data; + + { + //List all ID-resources + pe_resource_viewer::resource_id_list ids(res_.list_resource_ids(pe_resource_viewer::resource_cursor_group)); + + for(pe_resource_viewer::resource_id_list::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + { + //List all named resources + pe_resource_viewer::resource_name_list names(res_.list_resource_names(pe_resource_viewer::resource_cursor_group)); + for(pe_resource_viewer::resource_name_list::const_iterator it = names.begin(); it != names.end(); ++it) + { + pe_resource_viewer::resource_language_list group_languages(res_.list_resource_languages(pe_resource_viewer::resource_cursor_group, *it)); + if(std::find(group_languages.begin(), group_languages.end(), language) != group_languages.end() + && check_cursor_presence(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, *it).get_data(), cursor_id, cursor_header_data, raw_cursor_data)) + return cursor_header_data; + } + } + + throw pe_exception("No cursor group find for requested icon", pe_exception::no_cursor_group_found); +} +} diff --git a/tools/pe_bliss/resource_cursor_icon_reader.h b/tools/pe_bliss/resource_cursor_icon_reader.h new file mode 100644 index 0000000000..e34fff419b --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_reader.h @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* 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 "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_cursor_icon_reader +{ +public: + resource_cursor_icon_reader(const pe_resource_viewer& res); + + //Returns single icon data by ID and language (minimum checks of format correctness) + const std::string get_single_icon_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single icon data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_icon_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns icon data of group of icons by name and language (minimum checks of format correctness) + const std::string get_icon_by_name(uint32_t language, const std::wstring& icon_group_name) const; + //Returns icon data of group of icons by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_name(const std::wstring& icon_group_name, uint32_t index = 0) const; + //Returns icon data of group of icons by ID and language (minimum checks of format correctness) + const std::string get_icon_by_id_lang(uint32_t language, uint32_t icon_group_id) const; + //Returns icon data of group of icons by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_icon_by_id(uint32_t icon_group_id, uint32_t index = 0) const; + + //Returns single cursor data by ID and language (minimum checks of format correctness) + const std::string get_single_cursor_by_id_lang(uint32_t language, uint32_t id) const; + //Returns single cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_single_cursor_by_id(uint32_t id, uint32_t index = 0) const; + + //Returns cursor data by name and language (minimum checks of format correctness) + const std::string get_cursor_by_name(uint32_t language, const std::wstring& cursor_group_name) const; + //Returns cursor data by name and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_name(const std::wstring& cursor_group_name, uint32_t index = 0) const; + //Returns cursor data by ID and language (minimum checks of format correctness) + const std::string get_cursor_by_id_lang(uint32_t language, uint32_t cursor_group_id) const; + //Returns cursor data by ID and index in language directory (instead of language) (minimum checks of format correctness) + const std::string get_cursor_by_id(uint32_t cursor_group_id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of creating icon headers from ICON_GROUP resource data + //Returns icon count + static uint16_t format_icon_headers(std::string& ico_data, const std::string& resource_data); + + //Helper function of creating cursor headers from CURSOR_GROUP resource data + //Returns cursor count + uint16_t format_cursor_headers(std::string& cur_data, const std::string& resource_data, uint32_t language, uint32_t index = 0xFFFFFFFF) const; + + //Looks up icon group by icon id and returns full icon headers if found + const std::string lookup_icon_group_data_by_icon(uint32_t icon_id, uint32_t language) const; + //Checks for icon presence inside icon group, fills icon headers if found + static bool check_icon_presence(const std::string& icon_group_resource_data, uint32_t icon_id, std::string& ico_data); + + //Looks up cursor group by cursor id and returns full cursor headers if found + const std::string lookup_cursor_group_data_by_cursor(uint32_t cursor_id, uint32_t language, const std::string& raw_cursor_data) const; + //Checks for cursor presence inside cursor group, fills cursor headers if found + static bool check_cursor_presence(const std::string& icon_group_resource_data, uint32_t cursor_id, std::string& cur_header_data, const std::string& raw_cursor_data); +}; +} diff --git a/tools/pe_bliss/resource_cursor_icon_writer.cpp b/tools/pe_bliss/resource_cursor_icon_writer.cpp new file mode 100644 index 0000000000..2f1c4363c4 --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_writer.cpp @@ -0,0 +1,447 @@ +/*************************************************************************/ +/* 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 <algorithm> +#include <string.h> +#include "resource_cursor_icon_writer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_cursor_icon_writer::resource_cursor_icon_writer(pe_resource_manager& res) + :res_(res) +{} + +//Add icon helper +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check icon for correctness + if(icon_file.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + const ico_header* icon_header = reinterpret_cast<const ico_header*>(&icon_file[0]); + + unsigned long size_of_headers = sizeof(ico_header) + icon_header->Count * sizeof(icondirentry); + if(icon_file.length() < size_of_headers || icon_header->Count == 0) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + //Check icon entries + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + if(icon_entry->SizeInBytes == 0 + || icon_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(icon_entry->ImageOffset, icon_entry->SizeInBytes) + || icon_entry->ImageOffset + icon_entry->SizeInBytes > icon_file.length()) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + } + + std::string icon_group_data; + ico_header* info = 0; + + if(group_icon_info) + { + //If icon group already exists + { + icon_group_data = group_icon_info->get_data(); + codepage = group_icon_info->get_codepage(); //Don't change codepage of icon group entry + } + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + info->Count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + icon_group_data.resize(sizeof(ico_header) + (info->Count + icon_header->Count) * sizeof(icon_group)); + info = reinterpret_cast<ico_header*>(&icon_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - icon group doesn't exist + { + icon_group_data.resize(sizeof(ico_header) + icon_header->Count * sizeof(icon_group)); + memcpy(&icon_group_data[0], icon_header, sizeof(ico_header)); + } + + //Search for available icon IDs + std::vector<uint16_t> icon_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_icon, mode, icon_header->Count)); + + //Enumerate all icons in file + for(uint16_t i = 0; i != icon_header->Count; ++i) + { + const icondirentry* icon_entry = reinterpret_cast<const icondirentry*>(&icon_file[sizeof(ico_header) + i * sizeof(icondirentry)]); + icon_group group = {0}; + + //Fill icon resource header + group.BitCount = icon_entry->BitCount; + group.ColorCount = icon_entry->ColorCount; + group.Height = icon_entry->Height; + group.Planes = icon_entry->Planes; + group.Reserved = icon_entry->Reserved; + group.SizeInBytes = icon_entry->SizeInBytes; + group.Width = icon_entry->Width; + group.Number = icon_id_list.at(i); + + memcpy(&icon_group_data[sizeof(ico_header) + ((info ? info->Count : 0) + i) * sizeof(icon_group)], &group, sizeof(group)); + + //Add icon to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + res_.add_resource(icon_file.substr(icon_entry->ImageOffset, icon_entry->SizeInBytes), pe_resource_viewer::resource_icon, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += icon_header->Count; //Increase icon count, if we're adding icon to existing group + + { + //Add or replace icon group data entry + res_.add_resource(icon_group_data, pe_resource_viewer::resource_icon_group, new_icon_group_entry, finder, language, codepage, timestamp); + } +} + +//Returns free icon or cursor ID list depending on icon_place_mode +const std::vector<uint16_t> resource_cursor_icon_writer::get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_type type, icon_place_mode mode, uint32_t count) +{ + //Search for available icon/cursor IDs + std::vector<uint16_t> icon_cursor_id_list; + + try + { + //If any icon exists + //List icon IDs + std::vector<uint32_t> id_list(res_.list_resource_ids(type)); + std::sort(id_list.begin(), id_list.end()); + + //If we are placing icon on free spaces + //I.e., icon IDs 1, 3, 4, 7, 8 already exist + //We'll place five icons on IDs 2, 5, 6, 9, 10 + if(mode != icon_place_after_max_icon_id) + { + if(!id_list.empty()) + { + //Determine and list free icon IDs + for(std::vector<uint32_t>::const_iterator it = id_list.begin(); it != id_list.end(); ++it) + { + if(it == id_list.begin()) + { + if(*it > 1) + { + for(uint16_t i = 1; i != *it; ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + } + else if(*(it - 1) - *it > 1) + { + for(uint16_t i = static_cast<uint16_t>(*(it - 1) + 1); i != static_cast<uint16_t>(*it); ++i) + { + icon_cursor_id_list.push_back(i); + if(icon_cursor_id_list.size() == count) + break; + } + } + + if(icon_cursor_id_list.size() == count) + break; + } + } + } + + uint32_t max_id = id_list.empty() ? 0 : *std::max_element(id_list.begin(), id_list.end()); + for(uint32_t i = static_cast<uint32_t>(icon_cursor_id_list.size()); i != count; ++i) + icon_cursor_id_list.push_back(static_cast<uint16_t>(++max_id)); + } + catch(const pe_exception&) //Entry not found + { + for(uint16_t i = 1; i != count + 1; ++i) + icon_cursor_id_list.push_back(i); + } + + return icon_cursor_id_list; +} + +//Add cursor helper +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + //Check cursor for correctness + if(cursor_file.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + const cursor_header* cur_header = reinterpret_cast<const cursor_header*>(&cursor_file[0]); + + unsigned long size_of_headers = sizeof(cursor_header) + cur_header->Count * sizeof(cursordirentry); + if(cursor_file.length() < size_of_headers || cur_header->Count == 0) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + //Check cursor entries + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + if(cursor_entry->SizeInBytes == 0 + || cursor_entry->ImageOffset < size_of_headers + || !pe_utils::is_sum_safe(cursor_entry->ImageOffset, cursor_entry->SizeInBytes) + || cursor_entry->ImageOffset + cursor_entry->SizeInBytes > cursor_file.length()) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + } + + std::string cursor_group_data; + cursor_header* info = 0; + + if(group_cursor_info) + { + //If cursor group already exists + { + cursor_group_data = group_cursor_info->get_data(); + codepage = group_cursor_info->get_codepage(); //Don't change codepage of cursor group entry + } + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get cursor header + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + info->Count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + cursor_group_data.resize(sizeof(cursor_header) + (info->Count + cur_header->Count) * sizeof(cursor_group)); + info = reinterpret_cast<cursor_header*>(&cursor_group_data[0]); //In case if memory was reallocated + } + else //Entry not found - cursor group doesn't exist + { + cursor_group_data.resize(sizeof(cursor_header) + cur_header->Count * sizeof(cursor_group)); + memcpy(&cursor_group_data[0], cur_header, sizeof(cursor_header)); + } + + //Search for available cursor IDs + std::vector<uint16_t> cursor_id_list(get_icon_or_cursor_free_id_list(pe_resource_viewer::resource_cursor, mode, cur_header->Count)); + + //Enumerate all cursors in file + for(uint16_t i = 0; i != cur_header->Count; ++i) + { + const cursordirentry* cursor_entry = reinterpret_cast<const cursordirentry*>(&cursor_file[sizeof(cursor_header) + i * sizeof(cursordirentry)]); + cursor_group group = {0}; + + //Fill cursor resource header + group.Height = cursor_entry->Height * 2; + group.SizeInBytes = cursor_entry->SizeInBytes + 2 * sizeof(uint16_t) /* hotspot coordinates */; + group.Width = cursor_entry->Width; + group.Number = cursor_id_list.at(i); + + memcpy(&cursor_group_data[sizeof(cursor_header) + ((info ? info->Count : 0) + i) * sizeof(cursor_group)], &group, sizeof(group)); + + //Add cursor to resources + resource_directory_entry new_entry; + new_entry.set_id(group.Number); + + //Fill resource data (two WORDs for hotspot of cursor, and cursor bitmap data) + std::string cur_data; + cur_data.resize(sizeof(uint16_t) * 2); + memcpy(&cur_data[0], &cursor_entry->HotspotX, sizeof(uint16_t)); + memcpy(&cur_data[sizeof(uint16_t)], &cursor_entry->HotspotY, sizeof(uint16_t)); + cur_data.append(cursor_file.substr(cursor_entry->ImageOffset, cursor_entry->SizeInBytes)); + + res_.add_resource(cur_data, pe_resource_viewer::resource_cursor, new_entry, resource_directory::entry_finder(group.Number), language, codepage, timestamp); + } + + if(info) + info->Count += cur_header->Count; //Increase cursor count, if we're adding cursor to existing group + + { + //Add or replace cursor group data entry + res_.add_resource(cursor_group_data, pe_resource_viewer::resource_cursor_group, new_cursor_group_entry, finder, language, codepage, timestamp); + } +} + +//Adds icon(s) from icon file data +//timestamp will be used for directories that will be added +//If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) +//(Codepage of icon group and icons will not be changed in this case) +//icon_place_mode determines, how new icon(s) will be placed +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, const std::wstring& icon_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_name(icon_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_icon(const std::string& icon_file, uint32_t icon_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_icon_group_entry; + new_icon_group_entry.set_id(icon_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_icon(icon_file, data_info.get(), new_icon_group_entry, resource_directory::entry_finder(icon_group_id), language, mode, codepage, timestamp); +} + +//Adds cursor(s) from cursor file data +//timestamp will be used for directories that will be added +//If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) +//(Codepage of cursor group and cursors will not be changed in this case) +//icon_place_mode determines, how new cursor(s) will be placed +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_name(cursor_group_name); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_name), language, mode, codepage, timestamp); +} + +void resource_cursor_icon_writer::add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp) +{ + resource_directory_entry new_cursor_group_entry; + new_cursor_group_entry.set_id(cursor_group_id); + std::auto_ptr<resource_data_info> data_info; + + try + { + data_info.reset(new resource_data_info(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id))); + } + catch(const pe_exception&) //Entry not found + { + } + + add_cursor(cursor_file, data_info.get(), new_cursor_group_entry, resource_directory::entry_finder(cursor_group_id), language, mode, codepage, timestamp); +} + +//Remove icon group helper +void resource_cursor_icon_writer::remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language) +{ + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Get icon header + const ico_header* info = reinterpret_cast<const ico_header*>(icon_group_data.data()); + + uint16_t icon_count = info->Count; + + //Check resource data size + if(icon_group_data.length() < sizeof(ico_header) + icon_count * sizeof(icon_group)) + throw pe_exception("Incorrect resource icon", pe_exception::resource_incorrect_icon); + + //Remove icon data + for(uint16_t i = 0; i != icon_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(icon_group_data.data() + sizeof(ico_header) + i * sizeof(icon_group)); + res_.remove_resource(pe_resource_viewer::resource_icon, group->Number, language); + } +} + +//Remove cursor group helper +void resource_cursor_icon_writer::remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language) +{ + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Get icon header + const cursor_header* info = reinterpret_cast<const cursor_header*>(cursor_group_data.data()); + + uint16_t cursor_count = info->Count; + + //Check resource data size + if(cursor_group_data.length() < sizeof(cursor_header) + cursor_count * sizeof(cursor_group)) + throw pe_exception("Incorrect resource cursor", pe_exception::resource_incorrect_cursor); + + //Remove icon data + for(uint16_t i = 0; i != cursor_count; ++i) + { + const icon_group* group = reinterpret_cast<const icon_group*>(cursor_group_data.data() + sizeof(cursor_header) + i * sizeof(cursor_group)); + res_.remove_resource(pe_resource_viewer::resource_cursor, group->Number, language); + } +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_cursor_group, cursor_group_name).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_name, language); +} + +//Removes cursor group and all its cursors by name/ID and language +bool resource_cursor_icon_writer::remove_cursor_group(uint32_t cursor_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_cursor_group, cursor_group_id).get_data(); + remove_cursors_from_cursor_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_cursor_group, cursor_group_id, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(const std::wstring& icon_group_name, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_name(language, pe_resource_viewer::resource_icon_group, icon_group_name).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_name, language); +} + +//Removes icon group and all its icons by name/ID and language +bool resource_cursor_icon_writer::remove_icon_group(uint32_t icon_group_id, uint32_t language) +{ + //Get resource by name and language + const std::string data = res_.get_resource_data_by_id(language, pe_resource_viewer::resource_icon_group, icon_group_id).get_data(); + remove_icons_from_icon_group(data, language); + return res_.remove_resource(pe_resource_viewer::resource_icon_group, icon_group_id, language); +} +} diff --git a/tools/pe_bliss/resource_cursor_icon_writer.h b/tools/pe_bliss/resource_cursor_icon_writer.h new file mode 100644 index 0000000000..e73ac6a093 --- /dev/null +++ b/tools/pe_bliss/resource_cursor_icon_writer.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* 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 "stdint_defs.h" +#include "pe_resource_manager.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_cursor_icon_writer +{ +public: + //Determines, how new icon(s) or cursor(s) will be placed + enum icon_place_mode + { + icon_place_after_max_icon_id, //Icon(s) will be placed after all existing + icon_place_free_ids //New icon(s) will take all free IDs between existing icons + }; + +public: + resource_cursor_icon_writer(pe_resource_manager& res); + + //Removes icon group and all its icons by name/ID and language + bool remove_icon_group(const std::wstring& icon_group_name, uint32_t language); + bool remove_icon_group(uint32_t icon_group_id, uint32_t language); + + //Adds icon(s) from icon file data + //timestamp will be used for directories that will be added + //If icon group with name "icon_group_name" or ID "icon_group_id" already exists, it will be appended with new icon(s) + //(Codepage of icon group and icons will not be changed in this case) + //icon_place_mode determines, how new icon(s) will be placed + void add_icon(const std::string& icon_file, + const std::wstring& icon_group_name, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + void add_icon(const std::string& icon_file, + uint32_t icon_group_id, + uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, + uint32_t codepage = 0, uint32_t timestamp = 0); + + //Removes cursor group and all its cursors by name/ID and language + bool remove_cursor_group(const std::wstring& cursor_group_name, uint32_t language); + bool remove_cursor_group(uint32_t cursor_group_id, uint32_t language); + + //Adds cursor(s) from cursor file data + //timestamp will be used for directories that will be added + //If cursor group with name "cursor_group_name" or ID "cursor_group_id" already exists, it will be appended with new cursor(s) + //(Codepage of cursor group and cursors will not be changed in this case) + //icon_place_mode determines, how new cursor(s) will be placed + void add_cursor(const std::string& cursor_file, const std::wstring& cursor_group_name, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + void add_cursor(const std::string& cursor_file, uint32_t cursor_group_id, uint32_t language, icon_place_mode mode = icon_place_after_max_icon_id, uint32_t codepage = 0, uint32_t timestamp = 0); + +private: + pe_resource_manager& res_; + + //Add icon helper + void add_icon(const std::string& icon_file, const resource_data_info* group_icon_info /* or zero */, resource_directory_entry& new_icon_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove icon group helper + void remove_icons_from_icon_group(const std::string& icon_group_data, uint32_t language); + + //Add cursor helper + void add_cursor(const std::string& cursor_file, const resource_data_info* group_cursor_info /* or zero */, resource_directory_entry& new_cursor_group_entry, const resource_directory::entry_finder& finder, uint32_t language, icon_place_mode mode, uint32_t codepage, uint32_t timestamp); + + //Remove cursor group helper + void remove_cursors_from_cursor_group(const std::string& cursor_group_data, uint32_t language); + + //Returns free icon or cursor ID list depending on icon_place_mode + const std::vector<uint16_t> get_icon_or_cursor_free_id_list(pe_resource_manager::resource_type type, icon_place_mode mode, uint32_t count); +}; +} diff --git a/tools/pe_bliss/resource_data_info.cpp b/tools/pe_bliss/resource_data_info.cpp new file mode 100644 index 0000000000..75bb060eae --- /dev/null +++ b/tools/pe_bliss/resource_data_info.cpp @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* 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 "resource_data_info.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +//Default constructor +resource_data_info::resource_data_info(const std::string& data, uint32_t codepage) + :data_(data), codepage_(codepage) +{} + +//Constructor from data +resource_data_info::resource_data_info(const resource_data_entry& data) + :data_(data.get_data()), codepage_(data.get_codepage()) +{} + +//Returns resource data +const std::string& resource_data_info::get_data() const +{ + return data_; +} + +//Returns resource codepage +uint32_t resource_data_info::get_codepage() const +{ + return codepage_; +} +} diff --git a/tools/pe_bliss/resource_data_info.h b/tools/pe_bliss/resource_data_info.h new file mode 100644 index 0000000000..e2275ebbf5 --- /dev/null +++ b/tools/pe_bliss/resource_data_info.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* 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 "stdint_defs.h" + +namespace pe_bliss +{ +class resource_data_entry; + +//Class representing resource data +class resource_data_info +{ +public: + //Constructor from data + resource_data_info(const std::string& data, uint32_t codepage); + //Constructor from data + explicit resource_data_info(const resource_data_entry& data); + + //Returns resource data + const std::string& get_data() const; + //Returns resource codepage + uint32_t get_codepage() const; + +private: + std::string data_; + uint32_t codepage_; +}; +} diff --git a/tools/pe_bliss/resource_internal.h b/tools/pe_bliss/resource_internal.h new file mode 100644 index 0000000000..64a5bf3903 --- /dev/null +++ b/tools/pe_bliss/resource_internal.h @@ -0,0 +1,34 @@ +/*************************************************************************/ +/* 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 + +#define U16TEXT(t) reinterpret_cast<const unicode16_t*>( t ) + +#define StringFileInfo U16TEXT("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define SizeofStringFileInfo sizeof("S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define VarFileInfo U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0") +#define Translation U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0") + +#define VarFileInfoAligned U16TEXT("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define TranslationAligned U16TEXT("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") +#define SizeofVarFileInfoAligned sizeof("V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0\0") +#define SizeofTranslationAligned sizeof("T\0r\0a\0n\0s\0l\0a\0t\0i\0o\0n\0\0\0\0") diff --git a/tools/pe_bliss/resource_message_list_reader.cpp b/tools/pe_bliss/resource_message_list_reader.cpp new file mode 100644 index 0000000000..f2ea142bee --- /dev/null +++ b/tools/pe_bliss/resource_message_list_reader.cpp @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* 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 "resource_message_list_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_message_list_reader::resource_message_list_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Helper function of parsing message list table +const resource_message_list resource_message_list_reader::parse_message_list(const std::string& resource_data) +{ + resource_message_list ret; + + //Check resource data length + if(resource_data.length() < sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + const message_resource_data* message_data = reinterpret_cast<const message_resource_data*>(resource_data.data()); + + //Check resource data length more carefully and some possible overflows + if(message_data->NumberOfBlocks >= pe_utils::max_dword / sizeof(message_resource_block) + || !pe_utils::is_sum_safe(message_data->NumberOfBlocks * sizeof(message_resource_block), sizeof(message_resource_data)) + || resource_data.length() < message_data->NumberOfBlocks * sizeof(message_resource_block) + sizeof(message_resource_data)) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Iterate over all message resource blocks + for(unsigned long i = 0; i != message_data->NumberOfBlocks; ++i) + { + //Get block + const message_resource_block* block = + reinterpret_cast<const message_resource_block*>(resource_data.data() + sizeof(message_resource_data) - sizeof(message_resource_block) + sizeof(message_resource_block) * i); + + //Check resource data length and IDs + if(resource_data.length() < block->OffsetToEntries || block->LowId > block->HighId) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + unsigned long current_pos = 0; + static const unsigned long size_of_entry_headers = 4; + //List all message resource entries in block + for(uint32_t curr_id = block->LowId; curr_id <= block->HighId; curr_id++) + { + //Check resource data length and some possible overflows + if(!pe_utils::is_sum_safe(block->OffsetToEntries, current_pos) + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, size_of_entry_headers) + || resource_data.length() < block->OffsetToEntries + current_pos + size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Get entry + const message_resource_entry* entry = reinterpret_cast<const message_resource_entry*>(resource_data.data() + block->OffsetToEntries + current_pos); + + //Check resource data length and entry length and some possible overflows + if(entry->Length < size_of_entry_headers + || !pe_utils::is_sum_safe(block->OffsetToEntries + current_pos, entry->Length) + || resource_data.length() < block->OffsetToEntries + current_pos + entry->Length + || entry->Length < size_of_entry_headers) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + if(entry->Flags & message_resource_unicode) + { + //If string is UNICODE + //Check its length + if(entry->Length % 2) + throw pe_exception("Incorrect resource message table", pe_exception::resource_incorrect_message_table); + + //Add ID and string to message table +#ifdef PE_BLISS_WINDOWS + ret.insert(std::make_pair(curr_id, message_table_item( + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2) + ))); +#else + ret.insert(std::make_pair(curr_id, message_table_item( + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers), + (entry->Length - size_of_entry_headers) / 2)) + ))); +#endif + } + else + { + //If string is ANSI + //Add ID and string to message table + ret.insert(std::make_pair(curr_id, message_table_item( + std::string(resource_data.data() + block->OffsetToEntries + current_pos + size_of_entry_headers, + entry->Length - size_of_entry_headers) + ))); + } + + //Go to next entry + current_pos += entry->Length; + } + } + + return ret; +} + +//Returns message table data by ID and index in language directory (instead of language) +const resource_message_list resource_message_list_reader::get_message_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_message_list(res_.get_resource_data_by_id(pe_resource_viewer::resource_message_table, id, index).get_data()); +} + +//Returns message table data by ID and language +const resource_message_list resource_message_list_reader::get_message_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_message_list(res_.get_resource_data_by_id(language, pe_resource_viewer::resource_message_table, id).get_data()); +} +} diff --git a/tools/pe_bliss/resource_message_list_reader.h b/tools/pe_bliss/resource_message_list_reader.h new file mode 100644 index 0000000000..a0ac96eb8c --- /dev/null +++ b/tools/pe_bliss/resource_message_list_reader.h @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* 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 "message_table.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; message_table_item +typedef std::map<uint32_t, message_table_item> resource_message_list; + +class resource_message_list_reader +{ +public: + resource_message_list_reader(const pe_resource_viewer& res); + + //Returns message table data by ID and language + const resource_message_list get_message_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns message table data by ID and index in language directory (instead of language) + const resource_message_list get_message_table_by_id(uint32_t id, uint32_t index = 0) const; + + //Helper function of parsing message list table + //resource_data - raw message table resource data + static const resource_message_list parse_message_list(const std::string& resource_data); + +private: + const pe_resource_viewer& res_; +}; +} diff --git a/tools/pe_bliss/resource_string_table_reader.cpp b/tools/pe_bliss/resource_string_table_reader.cpp new file mode 100644 index 0000000000..8a51720e6a --- /dev/null +++ b/tools/pe_bliss/resource_string_table_reader.cpp @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* 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 "resource_string_table_reader.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +resource_string_table_reader::resource_string_table_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns string table data by ID and index in language directory (instead of language) +const resource_string_list resource_string_table_reader::get_string_table_by_id(uint32_t id, uint32_t index) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(pe_resource_viewer::resource_string, id, index).get_data()); +} + +//Returns string table data by ID and language +const resource_string_list resource_string_table_reader::get_string_table_by_id_lang(uint32_t language, uint32_t id) const +{ + return parse_string_list(id, res_.get_resource_data_by_id(language, pe_resource_viewer::resource_string, id).get_data()); +} + +//Helper function of parsing string list table +const resource_string_list resource_string_table_reader::parse_string_list(uint32_t id, const std::string& resource_data) +{ + resource_string_list ret; + + //16 is maximum count of strings in a string table + static const unsigned long max_string_list_entries = 16; + unsigned long passed_bytes = 0; + for(unsigned long i = 0; i != max_string_list_entries; ++i) + { + //Check resource data length + if(resource_data.length() < sizeof(uint16_t) + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + //Get string length - the first WORD + uint16_t string_length = *reinterpret_cast<const uint16_t*>(resource_data.data() + passed_bytes); + passed_bytes += sizeof(uint16_t); //WORD containing string length + + //Check resource data length again + if(resource_data.length() < string_length + passed_bytes) + throw pe_exception("Incorrect resource string table", pe_exception::resource_incorrect_string_table); + + if(string_length) + { + //Create and save string (UNICODE) +#ifdef PE_BLISS_WINDOWS + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + std::wstring(reinterpret_cast<const wchar_t*>(resource_data.data() + passed_bytes), string_length))); +#else + ret.insert( + std::make_pair(static_cast<uint16_t>(((id - 1) << 4) + i), //ID of string is calculated such way + pe_utils::from_ucs2(u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + passed_bytes), string_length)))); +#endif + } + + //Go to next string + passed_bytes += string_length * 2; + } + + return ret; +} + +//Returns string from string table by ID and language +const std::wstring resource_string_table_reader::get_string_by_id_lang(uint32_t language, uint16_t id) const +{ + //List strings by string table id and language + const resource_string_list strings(get_string_table_by_id_lang(language, (id >> 4) + 1)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} + +//Returns string from string table by ID and index in language directory (instead of language) +const std::wstring resource_string_table_reader::get_string_by_id(uint16_t id, uint32_t index) const +{ + //List strings by string table id and index + const resource_string_list strings(get_string_table_by_id((id >> 4) + 1, index)); + resource_string_list::const_iterator it = strings.find(id); //Find string by id + if(it == strings.end()) + throw pe_exception("Resource string not found", pe_exception::resource_string_not_found); + + return (*it).second; +} +} diff --git a/tools/pe_bliss/resource_string_table_reader.h b/tools/pe_bliss/resource_string_table_reader.h new file mode 100644 index 0000000000..e3ded1da85 --- /dev/null +++ b/tools/pe_bliss/resource_string_table_reader.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* 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 <map> +#include "stdint_defs.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +//ID; string +typedef std::map<uint16_t, std::wstring> resource_string_list; + +class resource_string_table_reader +{ +public: + resource_string_table_reader(const pe_resource_viewer& res); + +public: + //Returns string table data by ID and language + const resource_string_list get_string_table_by_id_lang(uint32_t language, uint32_t id) const; + //Returns string table data by ID and index in language directory (instead of language) + const resource_string_list get_string_table_by_id(uint32_t id, uint32_t index = 0) const; + //Returns string from string table by ID and language + const std::wstring get_string_by_id_lang(uint32_t language, uint16_t id) const; + //Returns string from string table by ID and index in language directory (instead of language) + const std::wstring get_string_by_id(uint16_t id, uint32_t index = 0) const; + +private: + const pe_resource_viewer& res_; + + //Helper function of parsing string list table + //Id of resource is needed to calculate string IDs correctly + //resource_data is raw string table resource data + static const resource_string_list parse_string_list(uint32_t id, const std::string& resource_data); +}; +} diff --git a/tools/pe_bliss/resource_version_info_reader.cpp b/tools/pe_bliss/resource_version_info_reader.cpp new file mode 100644 index 0000000000..8ad44c6856 --- /dev/null +++ b/tools/pe_bliss/resource_version_info_reader.cpp @@ -0,0 +1,311 @@ +/*************************************************************************/ +/* 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 "resource_version_info_reader.h" +#include "utils.h" +#include "pe_exception.h" +#include "resource_internal.h" +#include "pe_resource_viewer.h" + +namespace pe_bliss +{ +using namespace pe_win; + +//Root version info block key value +const u16string resource_version_info_reader::version_info_key(U16TEXT("V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0")); + +resource_version_info_reader::resource_version_info_reader(const pe_resource_viewer& res) + :res_(res) +{} + +//Returns aligned version block value position +uint32_t resource_version_info_reader::get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Returns aligned version block first child position +uint32_t resource_version_info_reader::get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key) +{ + uint32_t string_length = static_cast<uint32_t>(u16string(key).length()); + uint32_t ret = pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 /* headers before Key data */ + + base_pos + + (string_length + 1 /* nullbyte */) * 2), + sizeof(uint32_t)) + + pe_utils::align_up(value_length, sizeof(uint32_t)); + + //Check possible overflows + if(ret < base_pos || ret < value_length || ret < sizeof(uint16_t) * 3 || ret < (string_length + 1) * 2) + throw_incorrect_version_info(); + + return ret; +} + +//Throws an exception (id = resource_incorrect_version_info) +void resource_version_info_reader::throw_incorrect_version_info() +{ + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const +{ + //Fixed file version info + file_version_info ret; + + //Check resource data length + if(resource_data.length() < sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Root version info block + const version_info_block* root_block = reinterpret_cast<const version_info_block*>(resource_data.data()); + + //Check root block key for null-termination and its name + if(!pe_utils::is_null_terminated(root_block->Key, resource_data.length() - sizeof(uint16_t) * 3 /* headers before Key data */) + || version_info_key != reinterpret_cast<const unicode16_t*>(root_block->Key)) + throw_incorrect_version_info(); + + //If file has fixed version info + if(root_block->ValueLength) + { + //Get root block value position + uint32_t value_pos = get_version_block_value_pos(0, reinterpret_cast<const unicode16_t*>(root_block->Key)); + //Check value length + if(resource_data.length() < value_pos + sizeof(vs_fixedfileinfo)) + throw_incorrect_version_info(); + + //Get VS_FIXEDFILEINFO structure pointer + const vs_fixedfileinfo* file_info = reinterpret_cast<const vs_fixedfileinfo*>(resource_data.data() + value_pos); + //Check its signature and some other fields + if(file_info->dwSignature != vs_ffi_signature || file_info->dwStrucVersion != vs_ffi_strucversion) //Don't check if file_info->dwFileFlagsMask == VS_FFI_FILEFLAGSMASK + throw_incorrect_version_info(); + + //Save fixed version info + ret = file_version_info(*file_info); + } + + //Iterate over child elements of VS_VERSIONINFO (StringFileInfo or VarFileInfo) + for(uint32_t child_pos = get_version_block_first_child_pos(0, root_block->ValueLength, reinterpret_cast<const unicode16_t*>(root_block->Key)); + child_pos < root_block->Length;) + { + //Check block position + if(!pe_utils::is_sum_safe(child_pos, sizeof(version_info_block)) + || resource_data.length() < child_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer + const version_info_block* block = reinterpret_cast<const version_info_block*>(resource_data.data() + child_pos); + + //Check its length + if(block->Length == 0) + throw_incorrect_version_info(); + + //Check block key for null-termination + if(!pe_utils::is_null_terminated(block->Key, resource_data.length() - child_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string info_type(reinterpret_cast<const unicode16_t*>(block->Key)); + //If we encountered StringFileInfo... + if(info_type == StringFileInfo) + { + //Enumerate all string tables + for(uint32_t string_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + string_table_pos - child_pos < block->Length;) + { + //Check string table block position + if(resource_data.length() < string_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string table + const version_info_block* string_table = reinterpret_cast<const version_info_block*>(resource_data.data() + string_table_pos); + + //Check its length + if(string_table->Length == 0) + throw_incorrect_version_info(); + + //Check string table key for null-termination + if(!pe_utils::is_null_terminated(string_table->Key, resource_data.length() - string_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + string_values_map new_values; + + //Enumerate all strings in the string table + for(uint32_t string_pos = get_version_block_first_child_pos(string_table_pos, string_table->ValueLength, reinterpret_cast<const unicode16_t*>(string_table->Key)); + string_pos - string_table_pos < string_table->Length;) + { + //Check string block position + if(resource_data.length() < string_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for string block + const version_info_block* string_block = reinterpret_cast<const version_info_block*>(resource_data.data() + string_pos); + + //Check its length + if(string_block->Length == 0) + throw_incorrect_version_info(); + + //Check string block key for null-termination + if(!pe_utils::is_null_terminated(string_block->Key, resource_data.length() - string_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + u16string data; + //If string block has value + if(string_block->ValueLength != 0) + { + //Get value position + uint32_t value_pos = get_version_block_value_pos(string_pos, reinterpret_cast<const unicode16_t*>(string_block->Key)); + //Check it + if(resource_data.length() < value_pos + string_block->ValueLength) + throw pe_exception("Incorrect resource version info", pe_exception::resource_incorrect_version_info); + + //Get UNICODE string value + data = u16string(reinterpret_cast<const unicode16_t*>(resource_data.data() + value_pos), string_block->ValueLength); + pe_utils::strip_nullbytes(data); + } + + //Save name-value pair +#ifdef PE_BLISS_WINDOWS + new_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_block->Key), data)); +#else + new_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_block->Key)), + pe_utils::from_ucs2(data))); +#endif + + //Navigate to next string block + string_pos += pe_utils::align_up(string_block->Length, sizeof(uint32_t)); + } + +#ifdef PE_BLISS_WINDOWS + string_values.insert(std::make_pair(reinterpret_cast<const unicode16_t*>(string_table->Key), new_values)); +#else + string_values.insert(std::make_pair(pe_utils::from_ucs2(reinterpret_cast<const unicode16_t*>(string_table->Key)), new_values)); +#endif + + //Navigate to next string table block + string_table_pos += pe_utils::align_up(string_table->Length, sizeof(uint32_t)); + } + } + else if(info_type == VarFileInfo) //If we encountered VarFileInfo + { + for(uint32_t var_table_pos = get_version_block_first_child_pos(child_pos, block->ValueLength, reinterpret_cast<const unicode16_t*>(block->Key)); + var_table_pos - child_pos < block->Length;) + { + //Check var block position + if(resource_data.length() < var_table_pos + sizeof(version_info_block)) + throw_incorrect_version_info(); + + //Get VERSION_INFO_BLOCK structure pointer for var block + const version_info_block* var_table = reinterpret_cast<const version_info_block*>(resource_data.data() + var_table_pos); + + //Check its length + if(var_table->Length == 0) + throw_incorrect_version_info(); + + //Check its key for null-termination + if(!pe_utils::is_null_terminated(var_table->Key, resource_data.length() - var_table_pos - sizeof(uint16_t) * 3 /* headers before Key data */)) + throw_incorrect_version_info(); + + //If block is "Translation" (actually, there's no other types possible in VarFileInfo) and it has value + if(u16string(reinterpret_cast<const unicode16_t*>(var_table->Key)) == Translation && var_table->ValueLength) + { + //Get its value position + uint32_t value_pos = get_version_block_value_pos(var_table_pos, reinterpret_cast<const unicode16_t*>(var_table->Key)); + //Cherck value length + if(resource_data.length() < value_pos + var_table->ValueLength) + throw_incorrect_version_info(); + + //Get list of translations: pairs of LANGUAGE_ID - CODEPAGE_ID + for(unsigned long i = 0; i < var_table->ValueLength; i += sizeof(uint16_t) * 2) + { + //Pair of WORDs + uint16_t lang_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + i); + uint16_t codepage_id = *reinterpret_cast<const uint16_t*>(resource_data.data() + value_pos + sizeof(uint16_t) + i); + //Save translation + translations.insert(std::make_pair(lang_id, codepage_id)); + } + } + + //Navigate to next var block + var_table_pos += pe_utils::align_up(var_table->Length, sizeof(uint32_t)); + } + } + else + { + throw_incorrect_version_info(); + } + + //Navigate to next element in root block + child_pos += pe_utils::align_up(block->Length, sizeof(uint32_t)); + } + + return ret; +} + +//Returns full version information: +//file_version info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const +{ + const std::string& resource_data = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .entry_by_id(language) + .get_data_entry() //Data directory + .get_data(); + + return get_version_info(string_values, translations, resource_data); +} + +//Returns full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +const file_version_info resource_version_info_reader::get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index) const +{ + const resource_directory::entry_list& entries = res_.get_root_directory() //Type directory + .entry_by_id(pe_resource_viewer::resource_version) + .get_resource_directory() //Name/ID directory + .entry_by_id(1) + .get_resource_directory() //Language directory + .get_entry_list(); + + if(entries.size() <= index) + throw pe_exception("Resource data entry not found", pe_exception::resource_data_entry_not_found); + + return get_version_info(string_values, translations, entries.at(index).get_data_entry().get_data()); //Data directory +} +} diff --git a/tools/pe_bliss/resource_version_info_reader.h b/tools/pe_bliss/resource_version_info_reader.h new file mode 100644 index 0000000000..c1dfbffdc2 --- /dev/null +++ b/tools/pe_bliss/resource_version_info_reader.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* 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 <map> +#include "file_version_info.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +class pe_resource_viewer; + +class resource_version_info_reader +{ +public: //VERSION INFO + resource_version_info_reader(const pe_resource_viewer& res); + + //Returns full version information: + //file_version_info: versions and file info + //lang_lang_string_values_map: map of version info strings with encodings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, uint32_t index = 0) const; + const file_version_info get_version_info_by_lang(lang_string_values_map& string_values, translation_values_map& translations, uint32_t language) const; + +public: + //L"VS_VERSION_INFO" key of root version info block + static const u16string version_info_key; + +private: + const pe_resource_viewer& res_; + + //VERSION INFO helpers + //Returns aligned version block value position + static uint32_t get_version_block_value_pos(uint32_t base_pos, const unicode16_t* key); + + //Returns aligned version block first child position + static uint32_t get_version_block_first_child_pos(uint32_t base_pos, uint32_t value_length, const unicode16_t* key); + + //Returns full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + const file_version_info get_version_info(lang_string_values_map& string_values, translation_values_map& translations, const std::string& resource_data) const; + + //Throws an exception (id = resource_incorrect_version_info) + static void throw_incorrect_version_info(); +}; +} diff --git a/tools/pe_bliss/resource_version_info_writer.cpp b/tools/pe_bliss/resource_version_info_writer.cpp new file mode 100644 index 0000000000..ed95a0f7ea --- /dev/null +++ b/tools/pe_bliss/resource_version_info_writer.cpp @@ -0,0 +1,283 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "resource_version_info_writer.h" +#include "pe_structures.h" +#include "resource_internal.h" +#include "utils.h" +#include "pe_resource_manager.h" +#include "resource_version_info_reader.h" + +namespace pe_bliss +{ +using namespace pe_win; + +resource_version_info_writer::resource_version_info_writer(pe_resource_manager& res) + :res_(res) +{} + +//Sets/replaces full version information: +//file_version_info: versions and file info +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +void resource_version_info_writer::set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage, + uint32_t timestamp) +{ + std::string version_data; + + //Calculate total size of version resource data + uint32_t total_version_info_length = + static_cast<uint32_t>(sizeof(version_info_block) - sizeof(uint16_t) + sizeof(uint16_t) /* pading */ + + (resource_version_info_reader::version_info_key.length() + 1) * 2 + + sizeof(vs_fixedfileinfo)); + + //If we have any strings values + if(!string_values.empty()) + { + total_version_info_length += sizeof(version_info_block) - sizeof(uint16_t); //StringFileInfo block + total_version_info_length += SizeofStringFileInfo; //Name of block (key) + + //Add required size for version strings + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*table_it).first.length() + 1) * 2), sizeof(uint32_t)); //Name of child block and block size (key of string table block) + + const string_values_map& values = (*table_it).second; + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(sizeof(uint16_t) * 3 + ((*it).first.length() + 1) * 2), sizeof(uint32_t)); + total_version_info_length += pe_utils::align_up(static_cast<uint32_t>(((*it).second.length() + 1) * 2), sizeof(uint32_t)); + } + } + } + + //If we have translations + if(!translations.empty()) + { + total_version_info_length += (sizeof(version_info_block) - sizeof(uint16_t)) * 2; //VarFileInfo and Translation blocks + total_version_info_length += SizeofVarFileInfoAligned; //DWORD-aligned VarFileInfo block name + total_version_info_length += SizeofTranslationAligned; //DWORD-aligned Translation block name + total_version_info_length += static_cast<uint32_t>(translations.size() * sizeof(uint16_t) * 2); + } + + //Resize version data buffer + version_data.resize(total_version_info_length); + + //Create root version block + version_info_block root_block = {0}; + root_block.ValueLength = sizeof(vs_fixedfileinfo); + root_block.Length = static_cast<uint16_t>(total_version_info_length); + + //Fill fixed file info + vs_fixedfileinfo fixed_info = {0}; + fixed_info.dwFileDateLS = file_info.get_file_date_ls(); + fixed_info.dwFileDateMS = file_info.get_file_date_ms(); + fixed_info.dwFileFlags = file_info.get_file_flags(); + fixed_info.dwFileFlagsMask = vs_ffi_fileflagsmask; + fixed_info.dwFileOS = file_info.get_file_os_raw(); + fixed_info.dwFileSubtype = file_info.get_file_subtype(); + fixed_info.dwFileType = file_info.get_file_type_raw(); + fixed_info.dwFileVersionLS = file_info.get_file_version_ls(); + fixed_info.dwFileVersionMS = file_info.get_file_version_ms(); + fixed_info.dwSignature = vs_ffi_signature; + fixed_info.dwStrucVersion = vs_ffi_strucversion; + fixed_info.dwProductVersionLS = file_info.get_product_version_ls(); + fixed_info.dwProductVersionMS = file_info.get_product_version_ms(); + + //Write root block and fixed file info to buffer + uint32_t data_ptr = 0; + memcpy(&version_data[data_ptr], &root_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + memcpy(&version_data[data_ptr], resource_version_info_reader::version_info_key.c_str(), (resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + data_ptr += static_cast<uint32_t>((resource_version_info_reader::version_info_key.length() + 1) * sizeof(uint16_t)); + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + memcpy(&version_data[data_ptr], &fixed_info, sizeof(fixed_info)); + data_ptr += sizeof(fixed_info); + + //Write string values, if any + if(!string_values.empty()) + { + //Create string file info root block + version_info_block string_file_info_block = {0}; + string_file_info_block.Type = 1; //Block type is string + memcpy(&version_data[data_ptr], &string_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate string file info block length later + memcpy(&version_data[data_ptr], StringFileInfo, SizeofStringFileInfo); //Write block name + data_ptr += SizeofStringFileInfo; + + //Create string table root block (child of string file info) + version_info_block string_table_block = {0}; + string_table_block.Type = 1; //Block type is string + + for(lang_string_values_map::const_iterator table_it = string_values.begin(); table_it != string_values.end(); ++table_it) + { + const string_values_map& values = (*table_it).second; + + memcpy(&version_data[data_ptr], &string_table_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* string_table_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate string table block length later + uint32_t lang_key_length = static_cast<uint32_t>(((*table_it).first.length() + 1) * sizeof(uint16_t)); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*table_it).first.c_str(), lang_key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*table_it).first)); + memcpy(&version_data[data_ptr], str.c_str(), lang_key_length); //Write block key + } +#endif + + data_ptr += lang_key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + lang_key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Create string block (child of string table block) + version_info_block string_block = {0}; + string_block.Type = 1; //Block type is string + for(string_values_map::const_iterator it = values.begin(); it != values.end(); ++it) + { + //Calculate value length and key length of string block + string_block.ValueLength = static_cast<uint16_t>((*it).second.length() + 1); + uint32_t key_length = static_cast<uint32_t>(((*it).first.length() + 1) * sizeof(uint16_t)); + //Calculate length of block + string_block.Length = static_cast<uint16_t>(pe_utils::align_up(sizeof(uint16_t) * 3 + key_length, sizeof(uint32_t)) + string_block.ValueLength * sizeof(uint16_t)); + + //Write string block + memcpy(&version_data[data_ptr], &string_block, sizeof(version_info_block) - sizeof(uint16_t)); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).first.c_str(), key_length); //Write block key +#else + { + u16string str(pe_utils::to_ucs2((*it).first)); + memcpy(&version_data[data_ptr], str.c_str(), key_length); //Write block key + } +#endif + + data_ptr += key_length; + //Align key if necessary + if((sizeof(uint16_t) * 3 + key_length) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + + //Write block data (value) +#ifdef PE_BLISS_WINDOWS + memcpy(&version_data[data_ptr], (*it).second.c_str(), string_block.ValueLength * sizeof(uint16_t)); +#else + { + u16string str(pe_utils::to_ucs2((*it).second)); + memcpy(&version_data[data_ptr], str.c_str(), string_block.ValueLength * sizeof(uint16_t)); + } +#endif + + data_ptr += string_block.ValueLength * 2; + //Align data if necessary + if((string_block.ValueLength * 2) % sizeof(uint32_t)) + { + memset(&version_data[data_ptr], 0, sizeof(uint16_t)); + data_ptr += sizeof(uint16_t); + } + } + + //Calculate string table and string file info blocks lengths + string_table_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + } + + string_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //If we have transactions + if(!translations.empty()) + { + //Create root var file info block + version_info_block var_file_info_block = {0}; + var_file_info_block.Type = 1; //Type of block is string + //Write block header + memcpy(&version_data[data_ptr], &var_file_info_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* var_file_info_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr1 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], VarFileInfoAligned, SizeofVarFileInfoAligned); //Write block key (aligned) + data_ptr += SizeofVarFileInfoAligned; + + //Create root translation block (child of var file info block) + version_info_block translation_block = {0}; + //Write block header + memcpy(&version_data[data_ptr], &translation_block, sizeof(version_info_block) - sizeof(uint16_t)); + //We will calculate its length later + version_info_block* translation_block_ptr = reinterpret_cast<version_info_block*>(&version_data[data_ptr]); + data_ptr += sizeof(version_info_block) - sizeof(uint16_t); + + uint32_t old_ptr2 = data_ptr; //Used to calculate var file info block length later + memcpy(&version_data[data_ptr], TranslationAligned, SizeofTranslationAligned); //Write block key (aligned) + data_ptr += SizeofTranslationAligned; + + //Calculate translation block value length + translation_block_ptr->ValueLength = static_cast<uint16_t>(sizeof(uint16_t) * 2 * translations.size()); + + //Write translation values to block + for(translation_values_map::const_iterator it = translations.begin(); it != translations.end(); ++it) + { + uint16_t lang_id = (*it).first; //Language ID + uint16_t codepage_id = (*it).second; //Codepage ID + memcpy(&version_data[data_ptr], &lang_id, sizeof(lang_id)); + data_ptr += sizeof(lang_id); + memcpy(&version_data[data_ptr], &codepage_id, sizeof(codepage_id)); + data_ptr += sizeof(codepage_id); + } + + //Calculate Translation and VarFileInfo blocks lengths + translation_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr2 + sizeof(uint16_t) * 3); + var_file_info_block_ptr->Length = static_cast<uint16_t>(data_ptr - old_ptr1 + sizeof(uint16_t) * 3); + } + + //Add/replace version info resource + res_.add_resource(version_data, pe_resource_viewer::resource_version, 1, language, codepage, timestamp); +} + +//Removes version info by language (ID = 1) +bool resource_version_info_writer::remove_version_info(uint32_t language) +{ + return res_.remove_resource(pe_resource_viewer::resource_version, 1, language); +} +} diff --git a/tools/pe_bliss/resource_version_info_writer.h b/tools/pe_bliss/resource_version_info_writer.h new file mode 100644 index 0000000000..da279ddedb --- /dev/null +++ b/tools/pe_bliss/resource_version_info_writer.h @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* 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 "version_info_types.h" +#include "file_version_info.h" + +namespace pe_bliss +{ +class pe_resource_manager; + +class resource_version_info_writer +{ +public: + resource_version_info_writer(pe_resource_manager& res); + + //Sets/replaces full version information: + //file_version_info: versions and file info + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + void set_version_info(const file_version_info& file_info, + const lang_string_values_map& string_values, + const translation_values_map& translations, + uint32_t language, + uint32_t codepage = 0, + uint32_t timestamp = 0); + + //Removes version info by language (ID = 1) + bool remove_version_info(uint32_t language); + +private: + pe_resource_manager& res_; +}; +} diff --git a/tools/pe_bliss/stdint_defs.h b/tools/pe_bliss/stdint_defs.h new file mode 100644 index 0000000000..bbc003690a --- /dev/null +++ b/tools/pe_bliss/stdint_defs.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* 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 +#ifdef _MSC_VER +#if _MSC_VER < 1600 +namespace pe_bliss +{ + //stdint.h definitions for MSVC 2008 and earlier, as + //it doesn't have them + typedef signed char int8_t; + typedef short int16_t; + typedef int int32_t; + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + + typedef long long int64_t; + typedef unsigned long long uint64_t; +} +#else +#include <stdint.h> +#endif +#else +#include <stdint.h> +#endif diff --git a/tools/pe_bliss/utils.cpp b/tools/pe_bliss/utils.cpp new file mode 100644 index 0000000000..e6a75d5497 --- /dev/null +++ b/tools/pe_bliss/utils.cpp @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* 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 <string.h> +#include "utils.h" +#include "pe_exception.h" + + +namespace pe_bliss +{ +const double pe_utils::log_2 = 1.44269504088896340736; //instead of using M_LOG2E + +//Returns stream size +std::streamoff pe_utils::get_file_size(std::istream& file) +{ + //Get old istream offset + std::streamoff old_offset = file.tellg(); + file.seekg(0, std::ios::end); + std::streamoff filesize = file.tellg(); + //Set old istream offset + file.seekg(old_offset); + return filesize; +} + +#ifndef PE_BLISS_WINDOWS +const u16string pe_utils::to_ucs2(const std::wstring& str) +{ + u16string ret; + if(str.empty()) + return ret; + + int len = str.length(); + + ret.resize(len); + + for(int i=0;i<len;i++) { + ret[i]=str[i]&0xFFFF; + } + + return ret; +} + +const std::wstring pe_utils::from_ucs2(const u16string& str) +{ + std::wstring ret; + if(str.empty()) + return ret; + + int len = str.length(); + ret.resize(str.length()); + + for(int i=0;i<len;i++) { + ret[i]=str[i]; + } + + return ret; +} +#endif + +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2) +{ + return guid1.Data1 == guid2.Data1 + && guid1.Data2 == guid2.Data2 + && guid1.Data3 == guid2.Data3 + && !memcmp(guid1.Data4, guid2.Data4, sizeof(guid1.Data4)); +} +} diff --git a/tools/pe_bliss/utils.h b/tools/pe_bliss/utils.h new file mode 100644 index 0000000000..29125f8dc1 --- /dev/null +++ b/tools/pe_bliss/utils.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* 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 <istream> +#include <string> +#include "stdint_defs.h" +#include "pe_structures.h" + +namespace pe_bliss +{ +class pe_utils +{ +public: + //Returns true if string "data" with maximum length "raw_length" is null-terminated + template<typename T> + static bool is_null_terminated(const T* data, size_t raw_length) + { + raw_length /= sizeof(T); + for(size_t l = 0; l < raw_length; l++) + { + if(data[l] == static_cast<T>(L'\0')) + return true; + } + + return false; + } + + //Helper template function to strip nullbytes in the end of string + template<typename T> + static void strip_nullbytes(std::basic_string<T>& str) + { + while(!*(str.end() - 1) && !str.empty()) + str.erase(str.length() - 1); + } + + //Helper function to determine if number is power of 2 + template<typename T> + static inline bool is_power_of_2(T x) + { + return !(x & (x - 1)); + } + + //Helper function to align number down + template<typename T> + static inline T align_down(T x, uint32_t align) + { + return x & ~(static_cast<T>(align) - 1); + } + + //Helper function to align number up + template<typename T> + static inline T align_up(T x, uint32_t align) + { + return (x & static_cast<T>(align - 1)) ? align_down(x, align) + static_cast<T>(align) : x; + } + + //Returns true if sum of two unsigned integers is safe (no overflow occurs) + static inline bool is_sum_safe(uint32_t a, uint32_t b) + { + return a <= static_cast<uint32_t>(-1) - b; + } + + //Two gigabytes value in bytes + static const uint32_t two_gb = 0x80000000; + static const uint32_t max_dword = 0xFFFFFFFF; + static const uint32_t max_word = 0x0000FFFF; + static const double log_2; //instead of using M_LOG2E + + //Returns stream size + static std::streamoff get_file_size(std::istream& file); + +#ifndef PE_BLISS_WINDOWS +public: + static const u16string to_ucs2(const std::wstring& str); + static const std::wstring from_ucs2(const u16string& str); +#endif + +private: + pe_utils(); + pe_utils(pe_utils&); + pe_utils& operator=(const pe_utils&); +}; + +//Windows GUID comparison +bool operator==(const pe_win::guid& guid1, const pe_win::guid& guid2); +} diff --git a/tools/pe_bliss/version_info_editor.cpp b/tools/pe_bliss/version_info_editor.cpp new file mode 100644 index 0000000000..199eebfd54 --- /dev/null +++ b/tools/pe_bliss/version_info_editor.cpp @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* 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 <sstream> +#include <iomanip> +#include "version_info_types.h" +#include "version_info_editor.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_editor::version_info_editor(lang_string_values_map& strings, translation_values_map& translations) + :version_info_viewer(strings, translations), + strings_edit_(strings), + translations_edit_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Sets company name +void version_info_editor::set_company_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"CompanyName", value, translation); +} + +//Sets file description +void version_info_editor::set_file_description(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileDescription", value, translation); +} + +//Sets file version +void version_info_editor::set_file_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"FileVersion", value, translation); +} + +//Sets internal file name +void version_info_editor::set_internal_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"InternalName", value, translation); +} + +//Sets legal copyright +void version_info_editor::set_legal_copyright(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"LegalCopyright", value, translation); +} + +//Sets original file name +void version_info_editor::set_original_filename(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"OriginalFilename", value, translation); +} + +//Sets product name +void version_info_editor::set_product_name(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductName", value, translation); +} + +//Sets product version +void version_info_editor::set_product_version(const std::wstring& value, const std::wstring& translation) +{ + set_property(L"ProductVersion", value, translation); +} + +//Sets version info property value +//property_name - property name +//value - property value +//If translation does not exist, it will be added +//If property does not exist, it will be added +void version_info_editor::set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation) +{ + lang_string_values_map::iterator it = strings_edit_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_edit_.find(default_language_translation); //Find default translation table + if(it == strings_edit_.end()) //If there's no default translation table, take the first one + { + it = strings_edit_.begin(); + if(it == strings_edit_.end()) //If there's no any translation table, add default one + { + it = strings_edit_.insert(std::make_pair(default_language_translation, string_values_map())).first; + //Also add it to translations list + add_translation(default_language_translation); + } + } + } + else + { + it = strings_edit_.find(translation); //Find specified translation table + if(it == strings_edit_.end()) //If there's no translation, add it + { + it = strings_edit_.insert(std::make_pair(translation, string_values_map())).first; + //Also add it to translations list + add_translation(translation); + } + } + + //Change value of the required property + ((*it).second)[property_name] = value; +} + +//Adds translation to translation list +void version_info_editor::add_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + add_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::add_translation(uint16_t language_id, uint16_t codepage_id) +{ + std::pair<translation_values_map::const_iterator, translation_values_map::const_iterator> + range(translations_edit_.equal_range(language_id)); + + //If translation already exists + for(translation_values_map::const_iterator it = range.first; it != range.second; ++it) + { + if((*it).second == codepage_id) + return; + } + + translations_edit_.insert(std::make_pair(language_id, codepage_id)); +} + +//Removes translation from translations and strings lists +void version_info_editor::remove_translation(const std::wstring& translation) +{ + std::pair<uint16_t, uint16_t> translation_ids(translation_from_string(translation)); + remove_translation(translation_ids.first, translation_ids.second); +} + +void version_info_editor::remove_translation(uint16_t language_id, uint16_t codepage_id) +{ + { + //Erase string table (if exists) + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << language_id + << std::setw(4) << std::setfill(L'0') << codepage_id; + + strings_edit_.erase(ss.str()); + } + + //Find and erase translation from translations table + std::pair<translation_values_map::iterator, translation_values_map::iterator> + it_pair = translations_edit_.equal_range(language_id); + + for(translation_values_map::iterator it = it_pair.first; it != it_pair.second; ++it) + { + if((*it).second == codepage_id) + { + translations_edit_.erase(it); + break; + } + } +} +} diff --git a/tools/pe_bliss/version_info_editor.h b/tools/pe_bliss/version_info_editor.h new file mode 100644 index 0000000000..53d3dc62c1 --- /dev/null +++ b/tools/pe_bliss/version_info_editor.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* 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 "version_info_types.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ + //Helper class to read and edit version information + //lang_string_values_map: map of version info strings with encodings + //translation_values_map: map of translations + class version_info_editor : public version_info_viewer + { + public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_editor(lang_string_values_map& strings, translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Sets company name + void set_company_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file description + void set_file_description(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets file version + void set_file_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets internal file name + void set_internal_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets legal copyright + void set_legal_copyright(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets original file name + void set_original_filename(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product name + void set_product_name(const std::wstring& value, const std::wstring& translation = std::wstring()); + //Sets product version + void set_product_version(const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Sets version info property value + //property_name - property name + //value - property value + //If translation does not exist, it will be added to strings and translations lists + //If property does not exist, it will be added + void set_property(const std::wstring& property_name, const std::wstring& value, const std::wstring& translation = std::wstring()); + + //Adds translation to translation list + void add_translation(const std::wstring& translation); + void add_translation(uint16_t language_id, uint16_t codepage_id); + + //Removes translation from translations and strings lists + void remove_translation(const std::wstring& translation); + void remove_translation(uint16_t language_id, uint16_t codepage_id); + + private: + lang_string_values_map& strings_edit_; + translation_values_map& translations_edit_; + }; +} diff --git a/tools/pe_bliss/version_info_types.h b/tools/pe_bliss/version_info_types.h new file mode 100644 index 0000000000..6010c9691e --- /dev/null +++ b/tools/pe_bliss/version_info_types.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* 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 <map> +#include <string> +#include "stdint_defs.h" + +namespace pe_bliss +{ + //Typedef for version info functions: Name - Value + typedef std::map<std::wstring, std::wstring> string_values_map; + //Typedef for version info functions: Language string - String Values Map + //Language String consists of LangID and CharsetID + //E.g. 041904b0 for Russian UNICODE, 040004b0 for Process Default Language UNICODE + typedef std::map<std::wstring, string_values_map> lang_string_values_map; + + //Typedef for version info functions: Language - Character Set + typedef std::multimap<uint16_t, uint16_t> translation_values_map; +} diff --git a/tools/pe_bliss/version_info_viewer.cpp b/tools/pe_bliss/version_info_viewer.cpp new file mode 100644 index 0000000000..6e2d0d5c5b --- /dev/null +++ b/tools/pe_bliss/version_info_viewer.cpp @@ -0,0 +1,180 @@ +/*************************************************************************/ +/* 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 <iomanip> +#include <sstream> +#include "pe_exception.h" +#include "version_info_viewer.h" + +namespace pe_bliss +{ +//Default process language, UNICODE +const std::wstring version_info_viewer::default_language_translation(L"040904b0"); + +//Default constructor +//strings - version info strings with charsets +//translations - version info translations map +version_info_viewer::version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations) + :strings_(strings), translations_(translations) +{} + +//Below functions have parameter translation +//If it's empty, the default language translation will be taken +//If there's no default language translation, the first one will be taken + +//Returns company name +const std::wstring version_info_viewer::get_company_name(const std::wstring& translation) const +{ + return get_property(L"CompanyName", translation); +} + +//Returns file description +const std::wstring version_info_viewer::get_file_description(const std::wstring& translation) const +{ + return get_property(L"FileDescription", translation); +} + +//Returns file version +const std::wstring version_info_viewer::get_file_version(const std::wstring& translation) const +{ + return get_property(L"FileVersion", translation); +} + +//Returns internal file name +const std::wstring version_info_viewer::get_internal_name(const std::wstring& translation) const +{ + return get_property(L"InternalName", translation); +} + +//Returns legal copyright +const std::wstring version_info_viewer::get_legal_copyright(const std::wstring& translation) const +{ + return get_property(L"LegalCopyright", translation); +} + +//Returns original file name +const std::wstring version_info_viewer::get_original_filename(const std::wstring& translation) const +{ + return get_property(L"OriginalFilename", translation); +} + +//Returns product name +const std::wstring version_info_viewer::get_product_name(const std::wstring& translation) const +{ + return get_property(L"ProductName", translation); +} + +//Returns product version +const std::wstring version_info_viewer::get_product_version(const std::wstring& translation) const +{ + return get_property(L"ProductVersion", translation); +} + +//Returns list of translations in string representation +const version_info_viewer::translation_list version_info_viewer::get_translation_list() const +{ + translation_list ret; + + //Enumerate all translations + for(translation_values_map::const_iterator it = translations_.begin(); it != translations_.end(); ++it) + { + //Create string representation of translation value + std::wstringstream ss; + ss << std::hex + << std::setw(4) << std::setfill(L'0') << (*it).first + << std::setw(4) << std::setfill(L'0') << (*it).second; + + //Save it + ret.push_back(ss.str()); + } + + return ret; +} + +//Returns version info property value +//property_name - required property name +//If throw_if_absent = true, will throw exception if property does not exist +//If throw_if_absent = false, will return empty string if property does not exist +const std::wstring version_info_viewer::get_property(const std::wstring& property_name, const std::wstring& translation, bool throw_if_absent) const +{ + std::wstring ret; + + //If there're no strings + if(strings_.empty()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + lang_string_values_map::const_iterator it = strings_.begin(); + + if(translation.empty()) + { + //If no translation was specified + it = strings_.find(default_language_translation); //Find default translation table + if(it == strings_.end()) //If there's no default translation table, take the first one + it = strings_.begin(); + } + else + { + it = strings_.find(translation); //Find specified translation table + if(it == strings_.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + } + + //Find value of the required property + string_values_map::const_iterator str_it = (*it).second.find(property_name); + + if(str_it == (*it).second.end()) + { + if(throw_if_absent) + throw pe_exception("Version info string does not exist", pe_exception::version_info_string_does_not_exist); + + return ret; + } + + ret = (*str_it).second; + + return ret; +} + +//Converts translation HEX-string to pair of language ID and codepage ID +const version_info_viewer::translation_pair version_info_viewer::translation_from_string(const std::wstring& translation) +{ + uint32_t translation_id = 0; + + { + //Convert string to DWORD + std::wstringstream ss; + ss << std::hex << translation; + ss >> translation_id; + } + + return std::make_pair(static_cast<uint16_t>(translation_id >> 16), static_cast<uint16_t>(translation_id & 0xFFFF)); +} +} diff --git a/tools/pe_bliss/version_info_viewer.h b/tools/pe_bliss/version_info_viewer.h new file mode 100644 index 0000000000..bc2f6f2ba7 --- /dev/null +++ b/tools/pe_bliss/version_info_viewer.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* 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 <map> +#include <vector> +#include <string> +#include "pe_resource_viewer.h" +#include "pe_structures.h" +#include "version_info_types.h" + +namespace pe_bliss +{ +//Helper class to read version information +//lang_string_values_map: map of version info strings with encodings +//translation_values_map: map of translations +class version_info_viewer +{ +public: + //Useful typedefs + typedef std::pair<uint16_t, uint16_t> translation_pair; + typedef std::vector<std::wstring> translation_list; + +public: + //Default constructor + //strings - version info strings with charsets + //translations - version info translations map + version_info_viewer(const lang_string_values_map& strings, const translation_values_map& translations); + + //Below functions have parameter translation + //If it's empty, the default language translation will be taken + //If there's no default language translation, the first one will be taken + + //Returns company name + const std::wstring get_company_name(const std::wstring& translation = std::wstring()) const; + //Returns file description + const std::wstring get_file_description(const std::wstring& translation = std::wstring()) const; + //Returns file version + const std::wstring get_file_version(const std::wstring& translation = std::wstring()) const; + //Returns internal file name + const std::wstring get_internal_name(const std::wstring& translation = std::wstring()) const; + //Returns legal copyright + const std::wstring get_legal_copyright(const std::wstring& translation = std::wstring()) const; + //Returns original file name + const std::wstring get_original_filename(const std::wstring& translation = std::wstring()) const; + //Returns product name + const std::wstring get_product_name(const std::wstring& translation = std::wstring()) const; + //Returns product version + const std::wstring get_product_version(const std::wstring& translation = std::wstring()) const; + + //Returns list of translations in string representation + const translation_list get_translation_list() const; + + //Returns version info property value + //property_name - required property name + //If throw_if_absent = true, will throw exception if property does not exist + //If throw_if_absent = false, will return empty string if property does not exist + const std::wstring get_property(const std::wstring& property_name, const std::wstring& translation = std::wstring(), bool throw_if_absent = false) const; + + //Converts translation HEX-string to pair of language ID and codepage ID + static const translation_pair translation_from_string(const std::wstring& translation); + +public: + //Default process language, UNICODE + static const std::wstring default_language_translation; + +private: + const lang_string_values_map& strings_; + const translation_values_map& translations_; +}; +} |