/* Copyright 2017 - 2026 R. Thomas
* Copyright 2017 - 2026 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <utility>
#include <algorithm>
#include <iterator>
#include <numeric>
#include <limits>
#include "logging.hpp"
#include "hash_stream.hpp"
#include "internal_utils.hpp"
#include "LIEF/utils.hpp"
#include "LIEF/PE/hash.hpp"
#include "LIEF/PE/Binary.hpp"
#include "LIEF/PE/Builder.hpp"
#include "LIEF/PE/DataDirectory.hpp"
#include <LIEF/PE/debug/Debug.hpp>
#include <LIEF/PE/debug/CodeView.hpp>
#include <LIEF/PE/debug/Pogo.hpp>
#include <LIEF/PE/debug/PogoEntry.hpp>
#include <LIEF/PE/debug/Repro.hpp>
#include <LIEF/PE/debug/CodeViewPDB.hpp>
#include <LIEF/PE/debug/VCFeature.hpp>
#include <LIEF/PE/debug/ExDllCharacteristics.hpp>
#include <LIEF/PE/debug/FPO.hpp>
#include "LIEF/PE/EnumToString.hpp"
#include "LIEF/PE/Export.hpp"
#include "LIEF/PE/ExportEntry.hpp"
#include "LIEF/PE/ImportEntry.hpp"
#include "LIEF/PE/LoadConfigurations/LoadConfiguration.hpp"
#include "LIEF/PE/Relocation.hpp"
#include "LIEF/PE/RelocationEntry.hpp"
#include "LIEF/PE/ResourceData.hpp"
#include "LIEF/PE/ResourceDirectory.hpp"
#include "LIEF/PE/ResourcesManager.hpp"
#include "LIEF/PE/RichHeader.hpp"
#include "LIEF/PE/Section.hpp"
#include "LIEF/PE/ExceptionInfo.hpp"
#include "LIEF/PE/exceptions_info/RuntimeFunctionX64.hpp"
#include "LIEF/PE/TLS.hpp"
#include "LIEF/PE/utils.hpp"
#include "LIEF/COFF/Symbol.hpp"
#include "LIEF/PE/signature/SpcIndirectData.hpp"
#include "PE/Structures.hpp"
#include "PE/checksum.hpp"
#include "frozen.hpp"
#include "internal_utils.hpp"
namespace LIEF::PE {
Binary::~Binary() = default;
Binary::Binary() :
LIEF::Binary(Binary::FORMATS::PE) {}
inline bool has_hybrid_metadata_ptr(const Binary& pe) {
const LoadConfiguration* LC = pe.load_configuration();
if (LC == nullptr) {
return false;
}
if (auto opt = LC->hybrid_metadata_pointer(); opt.value_or(0) > 0) {
return true;
}
return false;
}
template<typename T>
inline std::unique_ptr<Builder>
write_impl(Binary& binary, const Builder::config_t& config, T&& dest) {
auto builder = std::make_unique<Builder>(binary, config);
builder->build();
builder->write(dest);
return builder;
}
std::unique_ptr<Builder> Binary::write(const std::string& filename,
const Builder::config_t& config) {
return write_impl(*this, config, filename);
}
std::unique_ptr<Builder> Binary::write(std::ostream& os,
const Builder::config_t& config) {
return write_impl(*this, config, os);
}
TLS& Binary::tls(const TLS& tls) {
auto new_tls = std::make_unique<TLS>(tls);
new_tls->directory_ = tls_dir();
tls_ = std::move(new_tls);
return *tls_;
}
void Binary::remove_tls() {
if (tls_ == nullptr) {
// No TLS so nothing to do
return;
}
DataDirectory* tls_dir = this->tls_dir();
if (tls_dir == nullptr) {
return;
}
const size_t ptr_size = optional_header().magic() == PE_TYPE::PE32 ?
sizeof(uint32_t) :
sizeof(uint64_t);
const uint64_t imagebase = optional_header().imagebase();
uint32_t tls_hdr_start = tls_dir->RVA();
uint32_t tls_hdr_end = tls_hdr_start + tls_dir->size();
uint32_t tls_cbk_start = 0;
uint32_t tls_cbk_end = 0;
// Clear the TLS header with 0
fill_address(tls_dir->RVA(), tls_dir->size(), /*value=*/0, VA_TYPES::RVA);
// Clear the callbacks
if (uint64_t addr = tls_->addressof_callbacks(); addr > 0) {
const size_t cbk_size = tls_->callbacks().size() * ptr_size;
fill_address(addr, cbk_size, /*value=*/0, Binary::VA_TYPES::VA);
tls_cbk_start = addr - imagebase;
tls_cbk_end = tls_cbk_start + cbk_size;
}
// Clear the template data
if (const auto& data = tls_->addressof_raw_data(); data.first > 0) {
const size_t size = data.second - data.first;
fill_address(data.first, size, 0, Binary::VA_TYPES::VA);
}
// Remove relocations associated with the TLS structure
for (Relocation& R : relocations()) {
R.entries_.erase(
std::remove_if(R.entries_.begin(), R.entries_.end(),
[&](const std::unique_ptr<RelocationEntry>& E) {
const uint32_t addr = E->address();
if (tls_cbk_start <= addr && addr < tls_cbk_end) {
return true;
}
if (tls_hdr_start <= addr && addr < tls_hdr_end) {
return true;
}
return false;
}),
R.entries_.end()
);
}
// Reset the DataDirectory RVA/size
tls_dir->RVA(0);
tls_dir->size(0);
// delete the TLS class
tls_.reset(nullptr);
}
result<uint64_t> Binary::offset_to_virtual_address(uint64_t offset,
uint64_t slide) const {
const uint64_t imagebase = slide > 0 ? slide : optional_header().imagebase();
return imagebase + offset_to_rva(offset);
}
uint64_t Binary::offset_to_rva(uint64_t offset) const {
if (const Section* section = section_from_offset(offset)) {
const uint64_t base_rva = section->virtual_address() - section->offset();
return base_rva + offset;
}
return offset;
}
uint64_t Binary::rva_to_offset(uint64_t RVA) const {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[RVA](const std::unique_ptr<Section>& section) {
const auto vsize_adj =
std::max<uint64_t>(section->virtual_size(),
section->sizeof_raw_data());
return section->virtual_address() <= RVA &&
RVA < (section->virtual_address() + vsize_adj);
});
if (it_section == sections_.end()) {
// If not found within a section,
// we assume that rva == offset
return RVA;
}
const std::unique_ptr<Section>& section = *it_section;
// rva - virtual_address + pointer_to_raw_data
uint32_t section_alignment = optional_header().section_alignment();
uint32_t file_alignment = optional_header().file_alignment();
if (section_alignment < 0x1000) {
section_alignment = file_alignment;
}
uint64_t section_va = section->virtual_address();
uint64_t section_offset = section->pointerto_raw_data();
section_va = align(section_va, section_alignment);
section_offset = align(section_offset, file_alignment);
return ((RVA - section_va) + section_offset);
}
const Section* Binary::section_from_offset(uint64_t offset) const {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[offset](const std::unique_ptr<Section>& section) {
return section->pointerto_raw_data() <= offset &&
offset < (section->pointerto_raw_data() +
section->sizeof_raw_data());
});
if (it_section == sections_.end()) {
return nullptr;
}
return it_section->get();
}
const Section* Binary::section_from_rva(uint64_t virtual_address) const {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[virtual_address](const std::unique_ptr<Section>& section) {
return section->virtual_address() <= virtual_address &&
virtual_address < (section->virtual_address() +
section->virtual_size());
});
if (it_section == sections_.end()) {
return nullptr;
}
return it_section->get();
}
const DataDirectory* Binary::data_directory(DataDirectory::TYPES index) const {
if (static_cast<size_t>(index) < data_directories_.size() &&
data_directories_[static_cast<size_t>(index)] != nullptr)
{
return data_directories_[static_cast<size_t>(index)].get();
}
return nullptr;
}
bool Binary::is_reproducible_build() const {
const auto it = std::find_if(debug_.begin(), debug_.end(),
[](const std::unique_ptr<Debug>& dbg) {
return Repro::classof(dbg.get());
});
return it != debug_.end();
}
Export& Binary::set_export(const Export& export_table) {
export_ = std::make_unique<Export>(export_table);
return *export_;
}
LIEF::Binary::symbols_t Binary::get_abstract_symbols() {
LIEF::Binary::symbols_t lief_symbols;
for (COFF::Symbol& s : symbols()) {
lief_symbols.push_back(&s);
}
if (Export* exp = get_export()) {
for (ExportEntry& entry : exp->entries()) {
lief_symbols.push_back(&entry);
}
}
for (std::unique_ptr<Import>& imp : imports_) {
for (ImportEntry& entry : imp->entries()) {
lief_symbols.push_back(&entry);
}
}
for (std::unique_ptr<DelayImport>& imp : delay_imports_) {
for (DelayImportEntry& entry : imp->entries()) {
lief_symbols.push_back(&entry);
}
}
return lief_symbols;
}
LIEF::Binary::sections_t Binary::get_abstract_sections() {
LIEF::Binary::sections_t secs;
secs.reserve(sections_.size());
std::transform(sections_.begin(), sections_.end(), std::back_inserter(secs),
[](const std::unique_ptr<Section>& s) { return s.get(); });
return secs;
}
const Section* Binary::get_section(const std::string& name) const {
const auto section_it =
std::find_if(sections_.begin(), sections_.end(),
[&name](const std::unique_ptr<Section>& section) {
return section->name() == name;
});
if (section_it == sections_.end()) {
return nullptr;
}
return section_it->get();
}
const Section* Binary::import_section() const {
if (!has_imports()) {
return nullptr;
}
if (const DataDirectory* import_directory = import_dir()) {
return import_directory->section();
}
return nullptr;
}
uint64_t Binary::virtual_size() const {
uint64_t size = 0;
size += dos_header().addressof_new_exeheader();
size += sizeof(details::pe_header);
size += (type_ == PE_TYPE::PE32) ? sizeof(details::pe32_optional_header) :
sizeof(details::pe64_optional_header);
for (const std::unique_ptr<Section>& section : sections_) {
size = std::max(size, section->virtual_address() + section->virtual_size());
}
size = LIEF::align(size, optional_header().section_alignment());
return size;
}
uint32_t Binary::sizeof_headers() const {
uint32_t size = 0;
size += dos_header().addressof_new_exeheader();
size += sizeof(details::pe_header);
size += (type_ == PE_TYPE::PE32) ? sizeof(details::pe32_optional_header) :
sizeof(details::pe64_optional_header);
size += sizeof(details::pe_data_directory) * data_directories_.size();
size += sizeof(details::pe_section) * sections_.size();
size =
static_cast<uint32_t>(LIEF::align(size, optional_header().file_alignment()));
return size;
}
void Binary::remove_section(const std::string& name, bool clear) {
Section* sec = get_section(name);
if (sec == nullptr) {
LIEF_ERR("Section '{}' not found", name);
return;
}
return remove(*sec, clear);
}
void Binary::remove(const Section& section, bool clear) {
const auto it_section =
std::find_if(sections_.begin(), sections_.end(),
[§ion](const std::unique_ptr<Section>& s) {
return *s == section;
});
if (it_section == sections_.end()) {
LIEF_ERR("Section '{}' not found", section.name());
return;
}
std::unique_ptr<Section>& to_remove = *it_section;
const size_t section_index = std::distance(sections_.begin(), it_section);
if (section_index < (sections_.size() - 1) && section_index > 0) {
std::unique_ptr<Section>& previous = sections_[section_index - 1];
const size_t raw_size_gap = (to_remove->offset() + to_remove->size()) -
(previous->offset() + previous->size());
previous->size(previous->size() + raw_size_gap);
const size_t vsize_size_gap =
(to_remove->virtual_address() + to_remove->virtual_size()) -
(previous->virtual_address() + previous->virtual_size());
previous->virtual_size(previous->virtual_size() + vsize_size_gap);
}
if (clear) {
to_remove->clear(0);
}
sections_.erase(it_section);
header().numberof_sections(header().numberof_sections() - 1);
optional_header().sizeof_headers(sizeof_headers());
optional_header().sizeof_image(static_cast<uint32_t>(virtual_size()));
}
result<uint64_t> Binary::make_space_for_new_section() {
const uint32_t shift_value =
align(sizeof(details::pe_section), optional_header().file_alignment());
const uint64_t section_table_offset =
dos_header().addressof_new_exeheader() + sizeof(details::pe_header) +
header().sizeof_optional_header() +
sizeof(details::pe_data_directory) * data_directories_.size() +
sizeof(details::pe_section) * sections_.size();
shift(section_table_offset, shift_value);
available_sections_space_++;
return available_sections_space_;
}
void Binary::shift(uint64_t /*from*/, uint64_t by) {
for (std::unique_ptr<Section>& section : sections_) {
section->pointerto_raw_data(section->pointerto_raw_data() + by);
}
}
uint64_t Binary::last_section_offset() const {
uint64_t offset = std::accumulate(
sections_.begin(), sections_.end(), static_cast<uint64_t>(sizeof_headers()),
[](uint64_t offset, const std::unique_ptr<Section>& s) {
return std::max<uint64_t>(s->pointerto_raw_data() + s->sizeof_raw_data(),
offset);
}
);
return offset;
}
Section* Binary::add_section(const Section& section) {
if (available_sections_space_ < 0) {
make_space_for_new_section();
return add_section(section);
}
auto new_section = std::make_unique<Section>(section);
std::vector<uint8_t> content = as_vector(new_section->content());
const auto section_size = static_cast<uint32_t>(content.size());
const auto section_size_aligned =
static_cast<uint32_t>(align(section_size,
optional_header().file_alignment()));
const uint32_t virtual_size = section_size;
content.insert(content.end(), section_size_aligned - section_size, 0);
new_section->content(content);
// Compute new section offset
uint64_t new_section_offset =
align(last_section_offset(), optional_header().file_alignment());
LIEF_DEBUG("New section offset: {:#012x}", new_section_offset);
// Compute new section Virtual address
const auto section_align =
static_cast<uint64_t>(optional_header().section_alignment());
const uint64_t new_section_va =
align(std::accumulate(sections_.begin(), sections_.end(), section_align,
[](uint64_t va, const std::unique_ptr<Section>& s) {
return std::max<uint64_t>(
s->virtual_address() + s->virtual_size(), va
);
}),
section_align);
LIEF_DEBUG("New section VA: {:#012x}", new_section_va);
if (new_section->pointerto_raw_data() == 0) {
new_section->pointerto_raw_data(new_section_offset);
}
if (new_section->sizeof_raw_data() == 0) {
new_section->sizeof_raw_data(section_size_aligned);
}
if (new_section->virtual_address() == 0) {
new_section->virtual_address(new_section_va);
}
if (new_section->virtual_size() == 0) {
new_section->virtual_size(virtual_size);
}
if (sections_.size() >= std::numeric_limits<uint16_t>::max()) {
LIEF_INFO("Binary reached maximum number of sections");
return nullptr;
}
available_sections_space_--;
// Update headers
header().numberof_sections(static_cast<uint16_t>(sections_.size()));
optional_header().sizeof_image(this->virtual_size());
optional_header().sizeof_headers(sizeof_headers());
sections_.push_back(std::move(new_section));
return sections_.back().get();
}
Relocation& Binary::add_relocation(const Relocation& relocation) {
auto newone = std::make_unique<Relocation>(relocation);
for (RelocationEntry& entry : newone->entries()) {
entry.parent(*newone);
}
relocations_.push_back(std::move(newone));
return *relocations_.back();
}
void Binary::remove_all_relocations() {
relocations_.clear();
}
LIEF::Binary::relocations_t Binary::get_abstract_relocations() {
LIEF::Binary::relocations_t abstract_relocs;
for (Relocation& relocation : relocations()) {
for (RelocationEntry& entry : relocation.entries()) {
abstract_relocs.push_back(&entry);
}
}
return abstract_relocs;
}
bool Binary::remove_import(const std::string& name) {
auto it = std::find_if(imports_.begin(), imports_.end(),
[&name](const std::unique_ptr<Import>& imp) {
return imp->name() == name;
});
if (it == imports_.end()) {
return false;
}
imports_.erase(it);
return true;
}
const Import* Binary::get_import(const std::string& import_name) const {
const auto it_import =
std::find_if(imports_.begin(), imports_.end(),
[&import_name](const std::unique_ptr<Import>& import) {
return import->name() == import_name;
});
if (it_import == imports_.end()) {
return nullptr;
}
return &**it_import;
}
ResourceNode* Binary::set_resources(const ResourceNode& resource) {
return set_resources(resource.clone());
}
ResourceNode* Binary::set_resources(std::unique_ptr<ResourceNode> root) {
resources_ = std::move(root);
return resources_.get();
}
uint32_t Binary::compute_checksum() const {
const size_t sizeof_ptr =
type_ == PE_TYPE::PE32 ? sizeof(uint32_t) : sizeof(uint64_t);
ChecksumStream cs(optional_header_.checksum());
cs // Hash dos header
.write(dos_header_.magic())
.write(dos_header_.used_bytes_in_last_page())
.write(dos_header_.file_size_in_pages())
.write(dos_header_.numberof_relocation())
.write(dos_header_.header_size_in_paragraphs())
.write(dos_header_.minimum_extra_paragraphs())
.write(dos_header_.maximum_extra_paragraphs())
.write(dos_header_.initial_relative_ss())
.write(dos_header_.initial_sp())
.write(dos_header_.checksum())
.write(dos_header_.initial_ip())
.write(dos_header_.initial_relative_cs())
.write(dos_header_.addressof_relocation_table())
.write(dos_header_.overlay_number())
.write(dos_header_.reserved())
.write(dos_header_.oem_id())
.write(dos_header_.oem_info())
.write(dos_header_.reserved2())
.write(dos_header_.addressof_new_exeheader())
.write(dos_stub_);
cs // Hash PE Header
.write(header_.signature())
.write(static_cast<uint16_t>(header_.machine()))
.write(header_.numberof_sections())
.write(header_.time_date_stamp())
.write(header_.pointerto_symbol_table())
.write(header_.numberof_symbols())
.write(header_.sizeof_optional_header())
.write(static_cast<uint16_t>(header_.characteristics()));
cs // Hash OptionalHeader
.write(static_cast<uint16_t>(optional_header_.magic()))
.write(optional_header_.major_linker_version())
.write(optional_header_.minor_linker_version())
.write(optional_header_.sizeof_code())
.write(optional_header_.sizeof_initialized_data())
.write(optional_header_.sizeof_uninitialized_data())
.write(optional_header_.addressof_entrypoint())
.write(optional_header_.baseof_code());
if (type_ == PE_TYPE::PE32) {
cs.write(optional_header_.baseof_data());
}
cs // Continuation of optional header
.write_sized_int(optional_header_.imagebase(), sizeof_ptr)
.write(optional_header_.section_alignment())
.write(optional_header_.file_alignment())
.write(optional_header_.major_operating_system_version())
.write(optional_header_.minor_operating_system_version())
.write(optional_header_.major_image_version())
.write(optional_header_.minor_image_version())
.write(optional_header_.major_subsystem_version())
.write(optional_header_.minor_subsystem_version())
.write(optional_header_.win32_version_value())
.write(optional_header_.sizeof_image())
.write(optional_header_.sizeof_headers())
.write(optional_header_.checksum())
.write(static_cast<uint16_t>(optional_header_.subsystem()))
.write(static_cast<uint16_t>(optional_header_.dll_characteristics()))
.write_sized_int(optional_header_.sizeof_stack_reserve(), sizeof_ptr)
.write_sized_int(optional_header_.sizeof_stack_commit(), sizeof_ptr)
.write_sized_int(optional_header_.sizeof_heap_reserve(), sizeof_ptr)
.write_sized_int(optional_header_.sizeof_heap_commit(), sizeof_ptr)
.write(optional_header_.loader_flags())
.write(optional_header_.numberof_rva_and_size());
for (const std::unique_ptr<DataDirectory>& dir : data_directories_) {
cs.write(dir->RVA()).write(dir->size());
}
// Section headers
for (const std::unique_ptr<Section>& sec : sections_) {
std::array<char, 8> name = {0};
const std::string& sec_name = sec->fullname();
uint32_t name_length = std::min<uint32_t>(sec_name.size() + 1, sizeof(name));
std::copy(sec_name.c_str(), sec_name.c_str() + name_length, name.begin());
cs.write(name)
.write(sec->virtual_size())
.write<uint32_t>(sec->virtual_address())
.write(sec->sizeof_raw_data())
.write(sec->pointerto_raw_data())
.write(sec->pointerto_relocation())
.write(sec->pointerto_line_numbers())
.write(sec->numberof_relocations())
.write(sec->numberof_line_numbers())
.write(static_cast<uint32_t>(sec->characteristics()));
}
cs.write(section_offset_padding_);
std::vector<Section*> sections;
sections.reserve(sections_.size());
std::transform(sections_.begin(), sections_.end(), std::back_inserter(sections),
[](const std::unique_ptr<Section>& s) { return s.get(); });
// Sort by file offset
std::sort(sections.begin(), sections.end(),
[](const Section* lhs, const Section* rhs) {
return lhs->pointerto_raw_data() < rhs->pointerto_raw_data();
});
uint64_t position = 0;
for (const Section* sec : sections) {
if (sec->sizeof_raw_data() == 0) {
continue;
}
span<const uint8_t> pad = sec->padding();
span<const uint8_t> content = sec->content();
if (/* overlapping */ sec->offset() < position) {
// Trunc the beginning of the overlap
if (position <= sec->offset() + content.size()) {
const uint64_t start_p = position - sec->offset();
const uint64_t size = content.size() - start_p;
cs.write(content.data() + start_p, size).write(pad);
} else {
LIEF_WARN("Overlap detected in padding area");
}
} else {
cs.write(content.data(), content.size()).write(pad);
}
position = sec->offset() + content.size() + pad.size();
}
if (!overlay_.empty()) {
cs.write(overlay());
}
return cs.finalize();
}
std::vector<uint8_t> Binary::authentihash(ALGORITHMS algo) const {
CONST_MAP_ALT HMAP = {
std::pair(ALGORITHMS::MD5, hashstream::HASH::MD5),
std::pair(ALGORITHMS::SHA_1, hashstream::HASH::SHA1),
std::pair(ALGORITHMS::SHA_256, hashstream::HASH::SHA256),
std::pair(ALGORITHMS::SHA_384, hashstream::HASH::SHA384),
std::pair(ALGORITHMS::SHA_512, hashstream::HASH::SHA512),
};
auto it_hash = HMAP.find(algo);
if (it_hash == HMAP.end()) {
LIEF_WARN("Unsupported hash algorithm: {}", to_string(algo));
return {};
}
const size_t sizeof_ptr =
type_ == PE_TYPE::PE32 ? sizeof(uint32_t) : sizeof(uint64_t);
const hashstream::HASH hash_type = it_hash->second;
hashstream ios(hash_type);
// vector_iostream ios;
ios // Hash dos header
.write(dos_header_.magic())
.write(dos_header_.used_bytes_in_last_page())
.write(dos_header_.file_size_in_pages())
.write(dos_header_.numberof_relocation())
.write(dos_header_.header_size_in_paragraphs())
.write(dos_header_.minimum_extra_paragraphs())
.write(dos_header_.maximum_extra_paragraphs())
.write(dos_header_.initial_relative_ss())
.write(dos_header_.initial_sp())
.write(dos_header_.checksum())
.write(dos_header_.initial_ip())
.write(dos_header_.initial_relative_cs())
.write(dos_header_.addressof_relocation_table())
.write(dos_header_.overlay_number())
.write(dos_header_.reserved())
.write(dos_header_.oem_id())
.write(dos_header_.oem_info())
.write(dos_header_.reserved2())
.write(dos_header_.addressof_new_exeheader())
.write(dos_stub_);
ios // Hash PE Header
.write(header_.signature())
.write(static_cast<uint16_t>(header_.machine()))
.write(header_.numberof_sections())
.write(header_.time_date_stamp())
.write(header_.pointerto_symbol_table())
.write(header_.numberof_symbols())
.write(header_.sizeof_optional_header())
.write(static_cast<uint16_t>(header_.characteristics()));
ios // Hash OptionalHeader
.write(static_cast<uint16_t>(optional_header_.magic()))
.write(optional_header_.major_linker_version())
.write(optional_header_.minor_linker_version())
.write(optional_header_.sizeof_code())
.write(optional_header_.sizeof_initialized_data())
.write(optional_header_.sizeof_uninitialized_data())
.write(optional_header_.addressof_entrypoint())
.write(optional_header_.baseof_code());
if (type_ == PE_TYPE::PE32) {
ios.write(optional_header_.baseof_data());
}
ios // Continuation of optional header
.write_sized_int(optional_header_.imagebase(), sizeof_ptr)
.write(optional_header_.section_alignment())
.write(optional_header_.file_alignment())
.write(optional_header_.major_operating_system_version())
.write(optional_header_.minor_operating_system_version())
.write(optional_header_.major_image_version())
.write(optional_header_.minor_image_version())
.write(optional_header_.major_subsystem_version())
.write(optional_header_.minor_subsystem_version())
.write(optional_header_.win32_version_value())
.write(optional_header_.sizeof_image())
.write(optional_header_.sizeof_headers())
// optional_header_.checksum() is not part of the hash
.write(static_cast<uint16_t>(optional_header_.subsystem()))
.write(static_cast<uint16_t>(optional_header_.dll_characteristics()))
.write_sized_int(optional_header_.sizeof_stack_reserve(), sizeof_ptr)
.write_sized_int(optional_header_.sizeof_stack_commit(), sizeof_ptr)
.write_sized_int(optional_header_.sizeof_heap_reserve(), sizeof_ptr)
.write_sized_int(optional_header_.sizeof_heap_commit(), sizeof_ptr)
.write(optional_header_.loader_flags())
.write(optional_header_.numberof_rva_and_size());
for (const std::unique_ptr<DataDirectory>& dir : data_directories_) {
if (dir->type() == DataDirectory::TYPES::CERTIFICATE_TABLE) {
continue;
}
ios.write(dir->RVA()).write(dir->size());
}
for (const std::unique_ptr<Section>& sec : sections_) {
std::array<char, 8> name = {0};
const std::string& sec_name = sec->fullname();
uint32_t name_length = std::min<uint32_t>(sec_name.size() + 1, sizeof(name));
std::copy(sec_name.c_str(), sec_name.c_str() + name_length, name.begin());
ios.write(name)
.write(sec->virtual_size())
.write<uint32_t>(sec->virtual_address())
.write(sec->sizeof_raw_data())
.write(sec->pointerto_raw_data())
.write(sec->pointerto_relocation())
.write(sec->pointerto_line_numbers())
.write(sec->numberof_relocations())
.write(sec->numberof_line_numbers())
.write(static_cast<uint32_t>(sec->characteristics()));
}
// LIEF_DEBUG("Section padding at {:#x}", ios.tellp());
ios.write(section_offset_padding_);
std::vector<Section*> sections;
sections.reserve(sections_.size());
std::transform(sections_.begin(), sections_.end(), std::back_inserter(sections),
[](const std::unique_ptr<Section>& s) { return s.get(); });
// Sort by file offset
std::sort(sections.begin(), sections.end(),
[](const Section* lhs, const Section* rhs) {
return lhs->pointerto_raw_data() < rhs->pointerto_raw_data();
});
uint64_t position = 0;
for (const Section* sec : sections) {
if (sec->sizeof_raw_data() == 0) {
continue;
}
span<const uint8_t> pad = sec->padding();
span<const uint8_t> content = sec->content();
LIEF_DEBUG("Authentihash: Append section {:<8}: [{:#06x}, {:#06x}] + "
"[{:#06x}] = [{:#06x}, {:#06x}]",
sec->name(), sec->offset(), sec->offset() + content.size(),
pad.size(), sec->offset(),
sec->offset() + content.size() + pad.size());
if (/* overlapping */ sec->offset() < position) {
// Trunc the beginning of the overlap
if (position <= sec->offset() + content.size()) {
const uint64_t start_p = position - sec->offset();
const uint64_t size = content.size() - start_p;
ios.write(content.data() + start_p, size).write(pad);
} else {
LIEF_WARN("Overlap detected in padding area");
}
} else {
ios.write(content.data(), content.size()).write(pad);
}
position = sec->offset() + content.size() + pad.size();
}
if (!overlay_.empty()) {
const DataDirectory* cert_dir = this->cert_dir();
if (cert_dir == nullptr) {
LIEF_ERR("CERTIFICATE_TABLE data directory not found");
return {};
}
LIEF_DEBUG("Add overlay and omit {:#010x} - {:#010x}", cert_dir->RVA(),
cert_dir->RVA() + cert_dir->size());
if (cert_dir->RVA() > 0 && cert_dir->size() > 0 &&
cert_dir->RVA() >= overlay_offset_)
{
const uint64_t start_cert_offset = cert_dir->RVA() - overlay_offset_;
const uint64_t end_cert_offset = start_cert_offset + cert_dir->size();
if (end_cert_offset <= overlay_.size()) {
LIEF_DEBUG("Add [{:#x}, {:#x}]", overlay_offset_,
overlay_offset_ + start_cert_offset);
LIEF_DEBUG("Add [{:#x}, {:#x}]", overlay_offset_ + end_cert_offset,
overlay_offset_ + end_cert_offset + overlay_.size() -
end_cert_offset);
ios.write(overlay_.data(), start_cert_offset)
.write(overlay_.data() + end_cert_offset,
overlay_.size() - end_cert_offset);
} else {
ios.write(overlay());
}
} else {
ios.write(overlay());
}
}
// When something gets wrong with the hash:
// std::vector<uint8_t> out = ios.raw();
// std::ofstream output_file{"/tmp/hash.blob", std::ios::out | std::ios::binary |
// std::ios::trunc}; if (output_file) {
// std::copy(
// out.begin(),
// out.end(),
// std::ostreambuf_iterator<char>(output_file));
// }
// std::vector<uint8_t> hash = hashstream(hash_type).write(out).raw();
std::vector<uint8_t> hash = ios.raw();
LIEF_DEBUG("{}", hex_dump(hash));
return hash;
}
Signature::VERIFICATION_FLAGS
Binary::verify_signature(Signature::VERIFICATION_CHECKS checks) const {
if (!has_signatures()) {
return Signature::VERIFICATION_FLAGS::NO_SIGNATURE;
}
Signature::VERIFICATION_FLAGS flags = Signature::VERIFICATION_FLAGS::OK;
for (size_t i = 0; i < signatures_.size(); ++i) {
const Signature& sig = signatures_[i];
flags |= verify_signature(sig, checks);
if (flags != Signature::VERIFICATION_FLAGS::OK) {
LIEF_INFO("Verification failed for signature #{:d} (0b{:b})", i,
static_cast<uintptr_t>(flags));
break;
}
}
return flags;
}
Signature::VERIFICATION_FLAGS
Binary::verify_signature(const Signature& sig,
Signature::VERIFICATION_CHECKS checks) const {
Signature::VERIFICATION_FLAGS flags = Signature::VERIFICATION_FLAGS::OK;
if (!is_true(checks & Signature::VERIFICATION_CHECKS::HASH_ONLY)) {
const Signature::VERIFICATION_FLAGS value = sig.check(checks);
if (value != Signature::VERIFICATION_FLAGS::OK) {
LIEF_INFO("Bad signature (0b{:b})", static_cast<uintptr_t>(value));
flags |= value;
}
}
const ContentInfo::Content& content = sig.content_info().value();
if (!SpcIndirectData::classof(&content)) {
LIEF_INFO("Expected SpcIndirectData content");
flags |= Signature::VERIFICATION_FLAGS::CORRUPTED_CONTENT_INFO;
return flags;
}
const auto& spc_indirect_data = static_cast<const SpcIndirectData&>(content);
// Check that the authentihash matches ContentInfo's digest
const std::vector<uint8_t>& authhash = authentihash(sig.digest_algorithm());
const span<const uint8_t> chash = spc_indirect_data.digest();
if (authhash != std::vector<uint8_t>(chash.begin(), chash.end())) {
LIEF_INFO("Authentihash and content info digest mismatch:\n {}\n {}",
hex_dump(authhash), hex_dump(chash));
flags |= Signature::VERIFICATION_FLAGS::BAD_DIGEST;
}
if (flags != Signature::VERIFICATION_FLAGS::OK) {
flags |= Signature::VERIFICATION_FLAGS::BAD_SIGNATURE;
}
return flags;
}
LIEF::Binary::functions_t Binary::get_abstract_exported_functions() const {
LIEF::Binary::functions_t result;
if (const Export* exp = get_export()) {
for (const ExportEntry& entry : exp->entries()) {
const std::string& name = entry.name();
if (!name.empty()) {
result.emplace_back(name, entry.address(), Function::FLAGS::EXPORTED);
}
}
}
return result;
}
LIEF::Binary::functions_t Binary::get_abstract_imported_functions() const {
LIEF::Binary::functions_t result;
for (const Import& import : imports()) {
Import resolved = import;
if (auto resolution = resolve_ordinals(import)) {
resolved = std::move(*resolution);
}
for (const ImportEntry& entry : resolved.entries()) {
const std::string& name = entry.name();
if (!name.empty()) {
result.emplace_back(name, entry.iat_address(), Function::FLAGS::IMPORTED);
}
}
}
for (const DelayImport& import : delay_imports()) {
for (const DelayImportEntry& entry : import.entries()) {
if (entry.is_ordinal()) {
continue;
}
const std::string& name = entry.name();
if (!name.empty()) {
result.emplace_back(name, entry.value(), Function::FLAGS::IMPORTED);
}
}
}
return result;
}
std::vector<std::string> Binary::get_abstract_imported_libraries() const {
std::vector<std::string> result;
for (const Import& import : imports()) {
result.push_back(import.name());
}
for (const DelayImport& import : delay_imports()) {
result.push_back(import.name());
}
return result;
}
void Binary::fill_address(uint64_t address, size_t size, uint8_t value,
VA_TYPES addr_type) {
uint64_t rva = address;
if (addr_type == VA_TYPES::VA || addr_type == VA_TYPES::AUTO) {
const int64_t delta = address - optional_header().imagebase();
if (delta > 0 || addr_type == LIEF::Binary::VA_TYPES::VA) {
rva -= optional_header().imagebase();
}
}
Section* section_topatch = section_from_rva(rva);
if (section_topatch == nullptr) {
LIEF_ERR("Section not found for RVA {:#x}", rva);
return;
}
const uint64_t offset = rva - section_topatch->virtual_address();
span<uint8_t> content = section_topatch->writable_content();
if (offset + size > content.size()) {
LIEF_ERR("Cannot write {} bytes at {:#x} (limit: {:#x})", size, offset,
content.size());
return;
}
std::fill_n(content.begin() + offset, size, value);
}
void Binary::patch_address(uint64_t address,
const std::vector<uint8_t>& patch_value,
VA_TYPES addr_type) {
uint64_t rva = address;
if (addr_type == VA_TYPES::VA || addr_type == VA_TYPES::AUTO) {
const int64_t delta = address - optional_header().imagebase();
if (delta > 0 || addr_type == LIEF::Binary::VA_TYPES::VA) {
rva -= optional_header().imagebase();
}
}
// Find the section associated with the virtual address
Section* section_topatch = section_from_rva(rva);
if (section_topatch == nullptr) {
LIEF_ERR("Section not found for RVA {:#x}", rva);
return;
}
const uint64_t offset = rva - section_topatch->virtual_address();
span<uint8_t> content = section_topatch->writable_content();
if (offset + patch_value.size() > content.size()) {
LIEF_ERR("Patch value ({} bytes @{:#x}) out of section bounds (limit: {:#x})",
patch_value.size(), offset, content.size());
return;
}
std::copy(patch_value.begin(), patch_value.end(), content.data() + offset);
}
void Binary::patch_address(uint64_t address, uint64_t patch_value, size_t size,
LIEF::Binary::VA_TYPES addr_type) {
if (size > sizeof(patch_value)) {
LIEF_ERR("Invalid size ({:#x})", size);
return;
}
uint64_t rva = address;
if (addr_type == LIEF::Binary::VA_TYPES::VA ||
addr_type == LIEF::Binary::VA_TYPES::AUTO)
{
const int64_t delta = address - optional_header().imagebase();
if (delta > 0 || addr_type == LIEF::Binary::VA_TYPES::VA) {
rva -= optional_header().imagebase();
}
}
Section* section_topatch = section_from_rva(rva);
if (section_topatch == nullptr) {
LIEF_ERR("Section not found for RVA {:#x}", rva);
return;
}
const uint64_t offset = rva - section_topatch->virtual_address();
span<uint8_t> content = section_topatch->writable_content();
if (offset > content.size() || (offset + size) > content.size()) {
LIEF_ERR("Patch value ({} bytes @{:#x}) out of section bounds (limit: {:#x})",
size, offset, content.size());
}
switch (size) {
case sizeof(uint8_t):
{
auto X = static_cast<uint8_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint8_t));
break;
}
case sizeof(uint16_t):
{
auto X = static_cast<uint16_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint16_t));
break;
}
case sizeof(uint32_t):
{
auto X = static_cast<uint32_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint32_t));
break;
}
case sizeof(uint64_t):
{
auto X = static_cast<uint64_t>(patch_value);
memcpy(content.data() + offset, &X, sizeof(uint64_t));
break;
}
default:
{
LIEF_ERR("Provided size ({}) does not match integer size", size);
return;
}
}
}
span<const uint8_t> Binary::get_content_from_virtual_address(
uint64_t virtual_address, uint64_t size, LIEF::Binary::VA_TYPES addr_type
) const {
uint64_t rva = virtual_address;
if (addr_type == LIEF::Binary::VA_TYPES::VA ||
addr_type == LIEF::Binary::VA_TYPES::AUTO)
{
const int64_t delta = virtual_address - optional_header().imagebase();
if (delta > 0 || addr_type == LIEF::Binary::VA_TYPES::VA) {
rva -= optional_header().imagebase();
}
}
const Section* section = section_from_rva(rva);
if (section == nullptr) {
LIEF_ERR("Section not found for RVA {:#x}", rva);
return {};
}
span<const uint8_t> content = section->content();
const uint64_t offset = rva - section->virtual_address();
uint64_t checked_size = size;
if ((offset + checked_size) > content.size()) {
uint64_t delta_off = offset + checked_size - content.size();
if (checked_size < delta_off) {
LIEF_ERR("Section end overflow prevents data access");
return {};
}
checked_size = checked_size - delta_off;
}
return {content.data() + offset, static_cast<size_t>(checked_size)};
}
void Binary::rich_header(const RichHeader& rich_header) {
rich_header_ = std::make_unique<RichHeader>(rich_header);
}
// Resource manager
// ===============
result<ResourcesManager> Binary::resources_manager() const {
if (resources_ == nullptr) {
return make_error_code(lief_errors::not_found);
}
return *resources_;
}
LIEF::Binary::functions_t Binary::ctor_functions() const {
LIEF::Binary::functions_t functions;
if (const TLS* tls_obj = tls()) {
const std::vector<uint64_t>& clbs = tls_obj->callbacks();
for (size_t i = 0; i < clbs.size(); ++i) {
functions.emplace_back("tls_" + std::to_string(i), clbs[i],
Function::FLAGS::CONSTRUCTOR);
}
}
return functions;
}
LIEF::Binary::functions_t Binary::functions() const {
static const auto func_cmd = [](const Function& lhs, const Function& rhs) {
return lhs.address() < rhs.address();
};
std::set<Function, decltype(func_cmd)> functions_set(func_cmd);
LIEF::Binary::functions_t exception_functions = this->exception_functions();
LIEF::Binary::functions_t exported = get_abstract_exported_functions();
LIEF::Binary::functions_t ctors = ctor_functions();
std::move(exception_functions.begin(), exception_functions.end(),
std::inserter(functions_set, functions_set.end()));
std::move(exported.begin(), exported.end(),
std::inserter(functions_set, functions_set.end()));
std::move(ctors.begin(), ctors.end(),
std::inserter(functions_set, functions_set.end()));
return {functions_set.begin(), functions_set.end()};
}
LIEF::Binary::functions_t Binary::exception_functions() const {
functions_t functions;
functions.reserve(exceptions_.size());
for (const std::unique_ptr<ExceptionInfo>& info : exceptions_) {
if (const auto* x64 = info->as<RuntimeFunctionX64>()) {
Function F(x64->rva_start());
F.size(x64->size());
functions.push_back(std::move(F));
}
}
return functions;
}
const DelayImport* Binary::get_delay_import(const std::string& import_name) const {
const auto it_import =
std::find_if(delay_imports_.begin(), delay_imports_.end(),
[&import_name](const std::unique_ptr<DelayImport>& import) {
return import->name() == import_name;
});
if (it_import == delay_imports_.end()) {
return nullptr;
}
return &**it_import;
}
const CodeViewPDB* Binary::codeview_pdb() const {
if (debug_.empty()) {
return nullptr;
}
const auto it = std::find_if(debug_.begin(), debug_.end(),
[](const std::unique_ptr<Debug>& debug) {
return CodeViewPDB::classof(debug.get());
});
if (it == debug_.end()) {
return nullptr;
}
return static_cast<const CodeViewPDB*>(it->get());
}
Debug* Binary::add_debug_info(const Debug& entry) {
debug_.push_back(entry.clone());
return debug_.back().get();
}
bool Binary::remove_debug(const Debug& entry) {
auto it = std::find_if(debug_.begin(), debug_.end(),
[&entry](const std::unique_ptr<Debug>& dbg) {
return dbg.get() == &entry;
});
if (it == debug_.end()) {
return false;
}
Debug& target = **it;
if (!target.payload().empty()) {
span<uint8_t> payload = target.payload();
std::memset(payload.data(), 0, payload.size());
}
debug_.erase(it);
return true;
}
bool Binary::clear_debug() {
DataDirectory* dbg_dir = this->debug_dir();
if (dbg_dir == nullptr) {
return false;
}
for (std::unique_ptr<Debug>& dbg : debug_) {
span<uint8_t> payload = dbg->payload();
if (payload.empty()) {
continue;
}
std::memset(payload.data(), 0, payload.size());
}
debug_.clear();
fill_address(dbg_dir->RVA(), dbg_dir->size(), /*value=*/0,
Binary::VA_TYPES::RVA);
dbg_dir->RVA(0);
dbg_dir->size(0);
return true;
}
ExceptionInfo* Binary::find_exception_at(uint32_t rva) {
auto it = std::find_if(exceptions_.begin(), exceptions_.end(),
[rva](const std::unique_ptr<ExceptionInfo>& info) {
return info->rva_start() == rva;
});
if (it == exceptions_.end()) {
return nullptr;
}
return it->get();
}
result<uint64_t> Binary::get_function_address(const std::string& name) const {
if (const Export* exp = get_export()) {
if (const ExportEntry* entry = exp->find_entry(name)) {
uint32_t rva = entry->address();
if (rva > 0) {
return rva;
}
}
}
const std::string alt_name = '_' + name;
for (const COFF::Symbol& sym : symbols()) {
if (sym.complex_type() != COFF::Symbol::COMPLEX_TYPE::TY_FUNCTION) {
continue;
}
if (sym.value() == 0) {
continue;
}
if (sym.name() == name || sym.name() == alt_name) {
return sym.value();
}
}
return LIEF::Binary::get_function_address(name);
}
bool Binary::is_arm64ec() const {
return has_hybrid_metadata_ptr(*this) &&
header().machine() == Header::MACHINE_TYPES::AMD64;
}
bool Binary::is_arm64x() const {
return has_hybrid_metadata_ptr(*this) &&
header().machine() == Header::MACHINE_TYPES::ARM64;
}
void Binary::accept(Visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& Binary::print(std::ostream& os) const {
os << "DOS Header {\n" << indent(LIEF::to_string(dos_header()), 2) << "}\n";
if (auto stub = dos_stub(); !stub.empty()) {
os << "DOS Sub {\n" << indent(LIEF::dump(stub), 2) << "}\n";
}
if (const RichHeader* rich = rich_header()) {
os << "Rich Header {\n" << indent(LIEF::to_string(*rich), 2) << "}\n";
}
os << "Header {\n" << indent(LIEF::to_string(header()), 2) << "}\n";
os << "Optional Header {\n"
<< indent(LIEF::to_string(optional_header()), 2) << "}\n";
os << "Data Directories:\n";
for (const DataDirectory& dir : data_directories()) {
os << " " << dir << '\n';
}
{
const auto secs = sections();
for (size_t i = 0; i < secs.size(); ++i) {
os << fmt::format("Section #{:02} {{\n", i)
<< indent(LIEF::to_string(secs[i]), 2) << "}\n";
}
}
if (const TLS* tls_obj = tls()) {
os << "TLS: {\n" << indent(LIEF::to_string(*tls_obj), 2) << "}\n";
}
if (const LoadConfiguration* lconf = load_configuration()) {
os << "Load Configuration: {\n" << indent(LIEF::to_string(*lconf), 2) << "}\n";
}
if (auto entries = debug(); !entries.empty()) {
os << fmt::format("Debug Entries (#{}):\n", entries.size());
for (size_t i = 0; i < entries.size(); ++i) {
os << fmt::format(" Entry[{:02}] {{\n", i)
<< indent(LIEF::to_string(entries[i]), 4) << " }\n";
}
os << "}\n";
}
if (auto imps = imports(); !imps.empty()) {
os << fmt::format("Imports (#{}):\n", imps.size());
for (const Import& imp : imps) {
os << indent(LIEF::to_string(imp), 2);
}
}
if (auto imps = delay_imports(); !imps.empty()) {
os << fmt::format("Delay Load Imports (#{}):\n", imps.size());
for (const DelayImport& imp : imps) {
os << indent(LIEF::to_string(imp), 2);
}
}
if (auto relocs = relocations(); !relocs.empty()) {
os << fmt::format("Base Relocations (#{}):\n", relocs.size());
for (const Relocation& R : relocs) {
os << indent(LIEF::to_string(R), 2);
}
}
if (const Export* exp = get_export()) {
os << *exp << '\n';
}
if (auto sigs = signatures(); !sigs.empty()) {
os << fmt::format("Signatures (#{})\n", sigs.size());
for (const Signature& S : sigs) {
os << indent(LIEF::to_string(S), 2);
}
}
if (const ResourceNode* root = resources()) {
os << "Resources:\n" << indent(LIEF::to_string(*root), 2);
}
if (auto syms = symbols(); !syms.empty()) {
os << fmt::format("COFF Symbols (#{})\n", syms.size());
for (size_t i = 0; i < syms.size(); ++i) {
os << fmt::format("Symbol[{:02d}] {{\n", i)
<< indent(LIEF::to_string(syms[i]), 2) << "}\n";
}
}
if (auto unwind = this->exceptions(); !unwind.empty()) {
os << fmt::format("Unwind Info (#{})\n", unwind.size());
for (const ExceptionInfo& info : unwind) {
os << indent(LIEF::to_string(info), 2);
}
}
if (const Binary* nested = nested_pe_binary()) {
os << "Nested PE Binary {\n" << indent(LIEF::to_string(*nested), 2) << "}";
}
return os;
}
} // namesapce PE