2015-11-08 22:54:41 +00:00
|
|
|
/*************************************************************************/
|
|
|
|
/* 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. */
|
|
|
|
/*************************************************************************/
|
2015-11-08 22:53:58 +00:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
}
|