726 lines
25 KiB
C++
726 lines
25 KiB
C++
/*************************************************************************/
|
|
/* 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;
|
|
}
|
|
}
|