mirror of
https://github.com/lief-project/LIEF
synced 2026-06-08 15:30:44 +00:00
Improve the logic of sections insertion in ELF binaries
This commit is contained in:
@@ -562,6 +562,13 @@ class Binary(lief.Binary):
|
||||
|
||||
SEGMENT_GAP = 4
|
||||
|
||||
class SEC_INSERT_POS(enum.Enum):
|
||||
AUTO = 0
|
||||
|
||||
POST_SECTION = 2
|
||||
|
||||
POST_SEGMENT = 1
|
||||
|
||||
@property
|
||||
def type(self) -> Header.CLASS: ...
|
||||
|
||||
@@ -581,7 +588,7 @@ class Binary(lief.Binary):
|
||||
def add(self, arg: DynamicEntry, /) -> DynamicEntry: ...
|
||||
|
||||
@overload
|
||||
def add(self, section: Section, loaded: bool = True) -> Section: ...
|
||||
def add(self, section: Section, loaded: bool = True, pos: Binary.SEC_INSERT_POS = Binary.SEC_INSERT_POS.AUTO) -> Section: ...
|
||||
|
||||
@overload
|
||||
def add(self, segment: Segment, base: int = 0) -> Segment: ...
|
||||
@@ -830,6 +837,12 @@ class Binary(lief.Binary):
|
||||
@property
|
||||
def is_targeting_android(self) -> bool: ...
|
||||
|
||||
@overload
|
||||
def get_section_idx(self, arg: Section, /) -> Union[int, lief.lief_errors]: ...
|
||||
|
||||
@overload
|
||||
def get_section_idx(self, arg: str, /) -> Union[int, lief.lief_errors]: ...
|
||||
|
||||
overlay: memoryview
|
||||
|
||||
def relocate_phdr_table(self, type: Binary.PHDR_RELOC = Binary.PHDR_RELOC.AUTO) -> int: ...
|
||||
|
||||
@@ -104,6 +104,30 @@ void create<Binary>(nb::module_& m) {
|
||||
enforcement.
|
||||
)delim"_doc);
|
||||
|
||||
nb::enum_<Binary::SEC_INSERT_POS>(bin, "SEC_INSERT_POS", R"delim(
|
||||
This enum defines where the content of a newly added section should be
|
||||
inserted.
|
||||
)delim"_doc)
|
||||
.value("AUTO", Binary::SEC_INSERT_POS::AUTO,
|
||||
"Defer the choice to LIEF"_doc
|
||||
)
|
||||
.value("POST_SECTION", Binary::SEC_INSERT_POS::POST_SECTION,
|
||||
R"doc(
|
||||
Insert the section after the last valid offset in the **segments** table.
|
||||
|
||||
With this choice, the section is inserted after the loaded content but
|
||||
before any debug information.
|
||||
)doc"_doc
|
||||
)
|
||||
.value("POST_SEGMENT", Binary::SEC_INSERT_POS::POST_SEGMENT,
|
||||
R"doc(
|
||||
Insert the section after the last valid offset in the **section** table.
|
||||
|
||||
With this choice, the section is inserted at the very end of the binary.
|
||||
)doc"_doc
|
||||
)
|
||||
;
|
||||
|
||||
bin
|
||||
.def_prop_ro("type",
|
||||
&Binary::type,
|
||||
@@ -476,14 +500,14 @@ void create<Binary>(nb::module_& m) {
|
||||
"virtual_address"_a)
|
||||
|
||||
.def("add",
|
||||
nb::overload_cast<const Section&, bool>(&Binary::add),
|
||||
nb::overload_cast<const Section&, bool, Binary::SEC_INSERT_POS>(&Binary::add),
|
||||
R"delim(
|
||||
Add the given :class:`~lief.ELF.Section` to the binary.
|
||||
|
||||
If the section does not aim at being loaded in memory,
|
||||
the ``loaded`` parameter has to be set to ``False`` (default: ``True``)
|
||||
)delim"_doc,
|
||||
"section"_a, "loaded"_a = true,
|
||||
"section"_a, "loaded"_a = true, "pos"_a = Binary::SEC_INSERT_POS::AUTO,
|
||||
nb::rv_policy::reference_internal)
|
||||
|
||||
.def("add",
|
||||
@@ -734,6 +758,22 @@ void create<Binary>(nb::module_& m) {
|
||||
R"doc(True if the current is targeting Android)doc"_doc
|
||||
)
|
||||
|
||||
.def("get_section_idx", [] (const Binary& self, const Section& sec) {
|
||||
return error_or(
|
||||
nb::overload_cast<const Section&>(&Binary::get_section_idx, nb::const_),
|
||||
self, sec);
|
||||
},
|
||||
R"doc(Find the index of the section given in the first parameter)doc"_doc
|
||||
)
|
||||
|
||||
.def("get_section_idx", [] (const Binary& self, const std::string& name) {
|
||||
return error_or(
|
||||
nb::overload_cast<const std::string&>(&Binary::get_section_idx, nb::const_),
|
||||
self, name);
|
||||
},
|
||||
R"doc(Find the index of the section with the name given in the first parameter)doc"_doc
|
||||
)
|
||||
|
||||
.def_prop_rw("overlay",
|
||||
nb::overload_cast<>(&Binary::overlay, nb::const_),
|
||||
[] (Binary& self, nb::bytes& bytes) {
|
||||
|
||||
@@ -579,6 +579,12 @@
|
||||
:py:meth:`lief.ELF.Binary.write`
|
||||
:cpp:func:`LIEF::ELF::Binary::write`
|
||||
|
||||
|
||||
.. |lief-elf-binary-add| lief-api:: lief.ELF.Binary.add()
|
||||
|
||||
:py:func:`lief.ELF.Binary.add`
|
||||
:cpp:func:`LIEF::ELF::Binary::add`
|
||||
|
||||
.. |lief-elf-aarch64pauth| lief-api:: lief.ELF.AArch64PAuth
|
||||
|
||||
:py:class:`lief.ELF.AArch64PAuth`
|
||||
@@ -591,6 +597,17 @@
|
||||
:py:func:`lief.ELF.Relocation.resolve`
|
||||
:cpp:func:`LIEF::ELF::Relocation::resolve`
|
||||
|
||||
.. |lief-elf-segment| lief-api:: lief.ELF.Segment
|
||||
|
||||
:rust:struct:`lief::elf::Segment`
|
||||
:py:class:`lief.ELF.Segment`
|
||||
:cpp:class:`LIEF::ELF::Segment`
|
||||
|
||||
.. |lief-elf-section| lief-api:: lief.ELF.Section
|
||||
|
||||
:rust:struct:`lief::elf::Section`
|
||||
:py:class:`lief.ELF.Section`
|
||||
:cpp:class:`LIEF::ELF::Section`
|
||||
|
||||
.. Mach-O ======================================================================
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
:ELF:
|
||||
|
||||
* LIEF inserted sections are not compatible with a ``strip`` after the
|
||||
modification of the binary (see: :ref:`Adding a section/segment <format-elf-section-segment>`)
|
||||
* Relax the condition over the ``DT_SYMENT`` entry (:issue:`1177`)
|
||||
* Fix issue when parsing the dynamic table with an invalid offset (bug found
|
||||
by :github_user:`lebr0nli`)
|
||||
|
||||
@@ -130,6 +130,125 @@ file.
|
||||
|
||||
:ref:`binary-abstraction`
|
||||
|
||||
.. _format-elf-section-segment:
|
||||
|
||||
Adding a Section/Segment
|
||||
************************
|
||||
|
||||
The ELF format uses two tables to represent the different slices of the binary:
|
||||
|
||||
1. The sections table
|
||||
2. The segments table
|
||||
|
||||
While the sections table offers a detailed view of the binary,
|
||||
it is primarily needed by the **compiler** and the **linker**. In particular,
|
||||
this table is not required for **loading** and **executing** an ELF file.
|
||||
The Android loader enforces the existence of a sections table and requires
|
||||
certain specific sections but from a loading perspective, this table is not used.
|
||||
|
||||
If you intend to modify an ELF file to load additional content into memory
|
||||
(such as code or data), it is recommended to add a |lief-elf-segment| instead of
|
||||
a section:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: :fa:`brands fa-python` Python
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
elf: lief.ELF.Binary = ...
|
||||
|
||||
segment = lief.ELF.Segment()
|
||||
segment.type = lief.ELF.Segment.TYPES.LOAD
|
||||
segment.content = list(b'Hello World')
|
||||
|
||||
new_segment: lief.ELF.Segment = elf.add(segment)
|
||||
|
||||
elf.write("new.elf")
|
||||
|
||||
.. tab:: :fa:`regular fa-file-code` C++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
std::unique_ptr<LIEF::ELF::Binary> elf;
|
||||
|
||||
LIEF::ELF::Segment segment;
|
||||
segment.type(LIEF::ELF::Segment::TYPES::LOAD);
|
||||
segment.content({1, 2, 3});
|
||||
|
||||
LIEF::ELF::Segment* new_segment = elf.add(segment);
|
||||
elf.write("new.elf");
|
||||
|
||||
You can also achieve this modification by creating a |lief-elf-section| that will
|
||||
**implicitly** create an associated ``PT_LOAD`` segment:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: :fa:`brands fa-python` Python
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
elf: lief.ELF.Binary = ...
|
||||
|
||||
section = lief.ELF.Section(".lief_demo")
|
||||
section.content = list(b'Hello World')
|
||||
|
||||
new_section: lief.ELF.Section = elf.add(section, loaded=True)
|
||||
|
||||
elf.write("new.elf")
|
||||
|
||||
.. tab:: :fa:`regular fa-file-code` C++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
std::unique_ptr<LIEF::ELF::Binary> elf;
|
||||
|
||||
LIEF::ELF::Section section(".lief_demo");
|
||||
section.content({1, 2, 3});
|
||||
|
||||
LIEF::ELF::Section* new_section = elf.add(section, /*loaded=*/true);
|
||||
elf.write("new.elf");
|
||||
|
||||
As mentioned above, the segments table matters from a loading perspective over
|
||||
the sections table. Therefore, it makes more sense to explicitly add a new
|
||||
segment rather than adding a section that implicitly adds a segment.
|
||||
|
||||
On the other hand, for debugging purposes or specific
|
||||
tools, one might want to add a **non-loaded** section. In this case, the data
|
||||
of the section is inserted at the end of the binary right after all the data wrapped
|
||||
by the segments:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: :fa:`brands fa-python` Python
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
elf: lief.ELF.Binary = ...
|
||||
|
||||
section = lief.ELF.Section(".metadata")
|
||||
section.content = list(b'version: 1.2.3')
|
||||
|
||||
# /!\ Note that loaded is set to False here
|
||||
# ------------------------------------------
|
||||
new_section: lief.ELF.Section = elf.add(section, loaded=False)
|
||||
|
||||
elf.write("new.elf")
|
||||
|
||||
.. tab:: :fa:`regular fa-file-code` C++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
std::unique_ptr<LIEF::ELF::Binary> elf;
|
||||
|
||||
LIEF::ELF::Section section(".metadata");
|
||||
section.content({1, 2, 3});
|
||||
|
||||
LIEF::ELF::Section* new_section = elf.add(section, /*loaded=*/false);
|
||||
elf.write("new.elf");
|
||||
|
||||
See: |lief-elf-binary-add| for the details about the API
|
||||
|
||||
Advance Parsing/Writing
|
||||
***********************
|
||||
|
||||
|
||||
@@ -223,6 +223,25 @@ class LIEF_API Binary : public LIEF::Binary {
|
||||
SEGMENT_GAP,
|
||||
};
|
||||
|
||||
/// This enum defines where the content of a newly added section should be
|
||||
/// inserted.
|
||||
enum class SEC_INSERT_POS {
|
||||
/// Defer the choice to LIEF
|
||||
AUTO = 0,
|
||||
|
||||
/// Insert the section after the last valid offset in the **segments**
|
||||
/// table.
|
||||
///
|
||||
/// With this choice, the section is inserted after the loaded content but
|
||||
/// before any debug information.
|
||||
POST_SEGMENT,
|
||||
|
||||
/// Insert the section after the last valid offset in the **section** table.
|
||||
///
|
||||
/// With this choice, the section is inserted at the very end of the binary.
|
||||
POST_SECTION,
|
||||
};
|
||||
|
||||
public:
|
||||
Binary& operator=(const Binary& ) = delete;
|
||||
Binary(const Binary& copy) = delete;
|
||||
@@ -595,14 +614,16 @@ class LIEF_API Binary : public LIEF::Binary {
|
||||
|
||||
/// Add a new section in the binary
|
||||
///
|
||||
/// @param[in] section The section object to insert
|
||||
/// @param[in] loaded Boolean value to indicate that section's data must be loaded
|
||||
/// by a PT_LOAD segment
|
||||
/// @param[in] section The section object to insert
|
||||
/// @param[in] loaded Boolean value to indicate that section's data must be loaded
|
||||
/// by a PT_LOAD segment
|
||||
/// @param[in] pos Position where to insert the data in the sections table
|
||||
///
|
||||
/// @return The section added. The `size` and the `virtual address` might change.
|
||||
///
|
||||
/// This function requires a well-formed ELF binary
|
||||
Section* add(const Section& section, bool loaded = true);
|
||||
Section* add(const Section& section, bool loaded = true,
|
||||
SEC_INSERT_POS pos = SEC_INSERT_POS::AUTO);
|
||||
|
||||
Section* extend(const Section& section, uint64_t size);
|
||||
|
||||
@@ -940,6 +961,18 @@ class LIEF_API Binary : public LIEF::Binary {
|
||||
return std::distance(sections_.begin(), it);
|
||||
}
|
||||
|
||||
uint8_t ptr_size() const {
|
||||
switch (type()) {
|
||||
case Header::CLASS::ELF32:
|
||||
return sizeof(uint32_t);
|
||||
case Header::CLASS::ELF64:
|
||||
return sizeof(uint64_t);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool classof(const LIEF::Binary* bin) {
|
||||
return bin->format() == Binary::FORMATS::ELF ||
|
||||
bin->format() == Binary::FORMATS::OAT;
|
||||
@@ -1077,12 +1110,13 @@ class LIEF_API Binary : public LIEF::Binary {
|
||||
LIEF_LOCAL Segment* extend_segment(const Segment& segment, uint64_t size);
|
||||
|
||||
template<bool LOADED>
|
||||
LIEF_LOCAL Section* add_section(const Section& section);
|
||||
LIEF_LOCAL Section* add_section(const Section& section, SEC_INSERT_POS pos);
|
||||
|
||||
std::vector<Symbol*> symtab_dyn_symbols() const;
|
||||
|
||||
LIEF_LOCAL std::string shstrtab_name() const;
|
||||
LIEF_LOCAL Section* add_frame_section(const Section& sec);
|
||||
LIEF_LOCAL Section* add_section(std::unique_ptr<Section> sec);
|
||||
|
||||
LIEF_LOCAL LIEF::Binary::functions_t tor_functions(DynamicEntry::TAG tag) const;
|
||||
|
||||
|
||||
+31
-3
@@ -972,14 +972,14 @@ result<uint64_t> Binary::get_function_address(const std::string& func_name, bool
|
||||
return (*it_symbol)->value();
|
||||
}
|
||||
|
||||
Section* Binary::add(const Section& section, bool loaded) {
|
||||
Section* Binary::add(const Section& section, bool loaded, SEC_INSERT_POS pos) {
|
||||
if (section.is_frame()) {
|
||||
return add_frame_section(section);
|
||||
}
|
||||
if (loaded) {
|
||||
return add_section<true>(section);
|
||||
return add_section<true>(section, pos);
|
||||
}
|
||||
return add_section<false>(section);
|
||||
return add_section<false>(section, pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -3132,6 +3132,34 @@ bool Binary::is_targeting_android() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Section* Binary::add_section(std::unique_ptr<Section> sec) {
|
||||
Section* sec_ptr = sec.get();
|
||||
|
||||
const auto it_new_sec_place = std::find_if(
|
||||
sections_.begin(), sections_.end(), [sec_ptr] (const std::unique_ptr<Section>& S) {
|
||||
return S->file_offset() > sec_ptr->file_offset();
|
||||
});
|
||||
|
||||
if (it_new_sec_place == sections_.end()) {
|
||||
sections_.push_back(std::move(sec));
|
||||
} else {
|
||||
size_t idx = std::distance(sections_.begin(), it_new_sec_place);
|
||||
for (size_t i = 0; i < sections_.size(); ++i) {
|
||||
const uint32_t link = sections_[i]->link();
|
||||
if (link >= idx) {
|
||||
sections_[i]->link(link + 1);
|
||||
}
|
||||
}
|
||||
if (header_.section_name_table_idx() >= idx) {
|
||||
header_.section_name_table_idx(header_.section_name_table_idx() + 1);
|
||||
}
|
||||
sections_.insert(it_new_sec_place, std::move(sec));
|
||||
}
|
||||
|
||||
return sec_ptr;
|
||||
}
|
||||
|
||||
std::ostream& Binary::print(std::ostream& os) const {
|
||||
|
||||
os << "Header" << '\n';
|
||||
|
||||
+91
-40
@@ -32,6 +32,36 @@
|
||||
namespace LIEF {
|
||||
namespace ELF {
|
||||
|
||||
inline void init_alignment(Segment& segment, uintptr_t pagesize, uintptr_t ptrsz) {
|
||||
if (segment.alignment() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (segment.type()) {
|
||||
case Segment::TYPE::LOAD:
|
||||
segment.alignment(pagesize);
|
||||
break;
|
||||
case Segment::TYPE::PHDR:
|
||||
case Segment::TYPE::DYNAMIC:
|
||||
case Segment::TYPE::TLS:
|
||||
segment.alignment(ptrsz);
|
||||
break;
|
||||
case Segment::TYPE::NOTE:
|
||||
case Segment::TYPE::GNU_EH_FRAME:
|
||||
segment.alignment(sizeof(uint32_t));
|
||||
break;
|
||||
case Segment::TYPE::GNU_RELRO:
|
||||
segment.alignment(1);
|
||||
break;
|
||||
case Segment::TYPE::GNU_STACK:
|
||||
segment.alignment(0x10);
|
||||
break;
|
||||
default:
|
||||
segment.alignment(ptrsz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ===============
|
||||
// ARM Relocations
|
||||
// ===============
|
||||
@@ -394,9 +424,9 @@ Segment* Binary::add_segment<Header::FILE_TYPE::EXEC>(const Segment& segment, ui
|
||||
new_segment->physical_size(segmentsize);
|
||||
new_segment->virtual_size(segmentsize);
|
||||
|
||||
if (new_segment->alignment() == 0) {
|
||||
new_segment->alignment(psize);
|
||||
}
|
||||
|
||||
init_alignment(*new_segment, psize, this->ptr_size());
|
||||
|
||||
new_segment->datahandler_ = datahandler_.get();
|
||||
|
||||
DataHandler::Node new_node{new_segment->file_offset(), new_segment->physical_size(),
|
||||
@@ -432,12 +462,11 @@ Segment* Binary::add_segment<Header::FILE_TYPE::EXEC>(const Segment& segment, ui
|
||||
// =======================
|
||||
template<>
|
||||
Segment* Binary::add_segment<Header::FILE_TYPE::DYN>(const Segment& segment, uint64_t base) {
|
||||
const auto psize = static_cast<uint64_t>(get_pagesize(*this));
|
||||
|
||||
const auto psize = (uint64_t)get_pagesize(*this);
|
||||
const auto ptr_size = this->ptr_size();
|
||||
/*const uint64_t new_phdr_offset = */ relocate_phdr_table_auto();
|
||||
|
||||
span<const uint8_t> content_ref = segment.content();
|
||||
std::vector<uint8_t> content{content_ref.data(), std::end(content_ref)};
|
||||
std::vector<uint8_t> content = as_vector(segment.content());
|
||||
|
||||
auto new_segment = std::make_unique<Segment>(segment);
|
||||
new_segment->datahandler_ = datahandler_.get();
|
||||
@@ -446,29 +475,32 @@ Segment* Binary::add_segment<Header::FILE_TYPE::DYN>(const Segment& segment, uin
|
||||
DataHandler::Node::SEGMENT};
|
||||
datahandler_->add(new_node);
|
||||
|
||||
const uint64_t last_offset_sections = last_offset_section();
|
||||
init_alignment(*new_segment, psize, ptr_size);
|
||||
|
||||
const uint64_t last_offset_segments = last_offset_segment();
|
||||
const uint64_t last_offset = std::max<uint64_t>(last_offset_sections, last_offset_segments);
|
||||
const uint64_t last_offset_aligned = align(last_offset, psize);
|
||||
const uint64_t last_offset = last_offset_segments;
|
||||
const uint64_t last_offset_aligned = align(last_offset, 0x10);
|
||||
if (base == 0) {
|
||||
base = align(next_virtual_address(), psize);
|
||||
}
|
||||
|
||||
uint64_t segmentsize = align(content.size(), 0x10);
|
||||
|
||||
const uint64_t delta = last_offset_aligned + segmentsize - last_offset;
|
||||
|
||||
shift_sections(last_offset, delta);
|
||||
|
||||
new_segment->file_offset(last_offset_aligned);
|
||||
new_segment->virtual_address(new_segment->file_offset() + base);
|
||||
new_segment->physical_address(new_segment->virtual_address());
|
||||
|
||||
uint64_t segmentsize = align(content.size(), 0x10);
|
||||
//uint64_t segmentsize = content.size();
|
||||
new_segment->handler_size_ = content.size();
|
||||
new_segment->physical_size(segmentsize);
|
||||
new_segment->virtual_size(segmentsize);
|
||||
|
||||
if (new_segment->alignment() == 0) {
|
||||
new_segment->alignment(psize);
|
||||
}
|
||||
|
||||
// Patch SHDR
|
||||
Header& header = this->header();
|
||||
const uint64_t new_section_hdr_offset = new_segment->file_offset() + new_segment->physical_size();
|
||||
header.section_headers_offset(new_section_hdr_offset);
|
||||
header.section_headers_offset(header.section_headers_offset() + delta);
|
||||
|
||||
auto alloc = datahandler_->make_hole(last_offset_aligned, new_segment->physical_size());
|
||||
|
||||
@@ -486,7 +518,9 @@ Segment* Binary::add_segment<Header::FILE_TYPE::DYN>(const Segment& segment, uin
|
||||
[&new_segment] (const std::unique_ptr<Segment>& s) {
|
||||
return s->type() == new_segment->type();
|
||||
});
|
||||
|
||||
Segment* seg_ptr = new_segment.get();
|
||||
|
||||
if (it_new_segment_place == segments_.rend()) {
|
||||
segments_.push_back(std::move(new_segment));
|
||||
} else {
|
||||
@@ -563,14 +597,12 @@ Segment* Binary::extend_segment<Segment::TYPE::LOAD>(const Segment& segment, uin
|
||||
return segment_to_extend.get();
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
Section* Binary::add_section<true>(const Section& section) {
|
||||
LIEF_DEBUG("Adding section '{}' as LOADED", section.name());
|
||||
// Create a Segment:
|
||||
inline Segment seg_for_section(const Section& section) {
|
||||
Segment new_segment;
|
||||
|
||||
// Create the segment associated with the section
|
||||
span<const uint8_t> content_ref = section.content();
|
||||
new_segment.content({std::begin(content_ref), std::end(content_ref)});
|
||||
new_segment.content(as_vector(content_ref));
|
||||
new_segment.type(Segment::TYPE::LOAD);
|
||||
|
||||
new_segment.virtual_address(section.virtual_address());
|
||||
@@ -589,8 +621,20 @@ Section* Binary::add_section<true>(const Section& section) {
|
||||
if (section.has(Section::FLAGS::EXECINSTR)) {
|
||||
new_segment.add(Segment::FLAGS::X);
|
||||
}
|
||||
return new_segment;
|
||||
}
|
||||
|
||||
Segment* segment_added = add(new_segment);
|
||||
template<>
|
||||
Section* Binary::add_section</*loaded=*/true>(const Section& section,
|
||||
SEC_INSERT_POS pos)
|
||||
{
|
||||
LIEF_DEBUG("Adding section '{}' as LOADED", section.name());
|
||||
if (pos != SEC_INSERT_POS::AUTO && pos != SEC_INSERT_POS::POST_SEGMENT) {
|
||||
LIEF_ERR("Unsupported position for inserting loaded section");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Segment* segment_added = add(seg_for_section(section));
|
||||
if (segment_added == nullptr) {
|
||||
LIEF_ERR("Can't add a LOAD segment of the section");
|
||||
return nullptr;
|
||||
@@ -615,16 +659,14 @@ Section* Binary::add_section<true>(const Section& section) {
|
||||
segment_added->sections_.push_back(new_section.get());
|
||||
|
||||
header().numberof_sections(header().numberof_sections() + 1);
|
||||
|
||||
Section* sec_ptr = new_section.get();
|
||||
sections_.push_back(std::move(new_section));
|
||||
return sec_ptr;
|
||||
return add_section(std::move(new_section));
|
||||
}
|
||||
|
||||
// Add a non-loaded section
|
||||
template<>
|
||||
Section* Binary::add_section<false>(const Section& section) {
|
||||
|
||||
Section* Binary::add_section</*loaded=*/false>(const Section& section,
|
||||
SEC_INSERT_POS pos)
|
||||
{
|
||||
auto new_section = std::make_unique<Section>(section);
|
||||
new_section->datahandler_ = datahandler_.get();
|
||||
|
||||
@@ -634,7 +676,20 @@ Section* Binary::add_section<false>(const Section& section) {
|
||||
|
||||
const uint64_t last_offset_sections = last_offset_section();
|
||||
const uint64_t last_offset_segments = last_offset_segment();
|
||||
const uint64_t last_offset = std::max<uint64_t>(last_offset_sections, last_offset_segments);
|
||||
|
||||
uint64_t last_offset = 0;
|
||||
switch (pos) {
|
||||
case SEC_INSERT_POS::AUTO:
|
||||
case SEC_INSERT_POS::POST_SEGMENT:
|
||||
last_offset = last_offset_segments;
|
||||
break;
|
||||
case SEC_INSERT_POS::POST_SECTION:
|
||||
last_offset = std::max(last_offset_segments, last_offset_sections);
|
||||
break;
|
||||
}
|
||||
|
||||
const uint64_t delta = section.size();
|
||||
shift_sections(last_offset, delta);
|
||||
|
||||
auto alloc = datahandler_->make_hole(last_offset, section.size());
|
||||
if (!alloc) {
|
||||
@@ -646,17 +701,13 @@ Section* Binary::add_section<false>(const Section& section) {
|
||||
new_section->size(section.size());
|
||||
|
||||
// Copy original content in the data handler
|
||||
span<const uint8_t> content_ref = section.content();
|
||||
new_section->content({std::begin(content_ref), std::end(content_ref)});
|
||||
|
||||
header().numberof_sections(header().numberof_sections() + 1);
|
||||
new_section->content(as_vector(section.content()));
|
||||
|
||||
Header& header = this->header();
|
||||
const uint64_t new_section_hdr_offset = new_section->offset() + new_section->size();
|
||||
header.numberof_sections(header.numberof_sections() + 1);
|
||||
const uint64_t new_section_hdr_offset = header.section_headers_offset() + delta;
|
||||
header.section_headers_offset(new_section_hdr_offset);
|
||||
Section* sec_ptr = new_section.get();
|
||||
sections_.push_back(std::move(new_section));
|
||||
return sec_ptr;
|
||||
return add_section(std::move(new_section));
|
||||
}
|
||||
|
||||
template<class ELF_T>
|
||||
|
||||
@@ -962,7 +962,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
|
||||
binary_->remove(*string_names_section, /* clear */ true);
|
||||
Section sec_str_section(sec_name, Section::TYPE::STRTAB);
|
||||
sec_str_section.content(std::vector<uint8_t>(raw_shstrtab_.size()));
|
||||
binary_->add(sec_str_section, /* loaded */ false);
|
||||
binary_->add(sec_str_section, /*loaded=*/false,
|
||||
/*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
|
||||
|
||||
// Default behavior: push_back => index = binary_->sections_.size() - 1
|
||||
hdr.section_name_table_idx(binary_->sections_.size() - 1);
|
||||
@@ -1567,7 +1568,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
|
||||
Section strtab{".strtab", Section::TYPE::STRTAB};
|
||||
strtab.content(raw_strtab_);
|
||||
strtab.alignment(1);
|
||||
Section* new_strtab = binary_->add(strtab, /* loaded */ false);
|
||||
Section* new_strtab = binary_->add(
|
||||
strtab, /*loaded=*/false, /*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
|
||||
|
||||
strtab_idx = binary_->sections().size() - 1;
|
||||
|
||||
@@ -1624,7 +1626,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
|
||||
symtab.entry_size(sizeof_sym);
|
||||
symtab.alignment(8);
|
||||
symtab.link(strtab_idx);
|
||||
Section* new_symtab = binary_->add(symtab, /* loaded */ false);
|
||||
Section* new_symtab = binary_->add(
|
||||
symtab, /*loaded=*/false, /*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
|
||||
if (new_symtab == nullptr) {
|
||||
LIEF_ERR("Can't add a new .symbtab section");
|
||||
return make_error_code(lief_errors::build_error);
|
||||
@@ -1664,7 +1667,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
|
||||
Section section{sec_name, Section::TYPE::NOTE};
|
||||
section += Section::FLAGS::ALLOC;
|
||||
|
||||
Section* section_added = binary_->add(section, /*loaded */ false);
|
||||
Section* section_added = binary_->add(
|
||||
section, /*loaded=*/false, /*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
|
||||
if (section_added == nullptr) {
|
||||
LIEF_ERR("Can't add SHT_NOTE section");
|
||||
return make_error_code(lief_errors::build_error);
|
||||
|
||||
@@ -7,10 +7,11 @@ import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import pytest
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from subprocess import Popen
|
||||
from utils import is_linux, glibc_version, get_sample
|
||||
from utils import is_linux, glibc_version, get_sample, has_private_samples
|
||||
|
||||
SAMPLE_DIR = pathlib.Path(os.getenv("LIEF_SAMPLES_DIR", ""))
|
||||
|
||||
@@ -259,7 +260,6 @@ def test_issue_970(tmp_path: Path):
|
||||
assert svd_0.auxiliary_symbols[0].name == "libcudart.so.12"
|
||||
assert svd_1.auxiliary_symbols[0].name == "libcudart.so.12"
|
||||
|
||||
|
||||
def test_issue_1121(tmp_path: Path):
|
||||
elf = lief.ELF.parse(get_sample("ELF/issue_1121.elf"))
|
||||
elf.get_symbol("main").name = "main_test"
|
||||
@@ -269,3 +269,94 @@ def test_issue_1121(tmp_path: Path):
|
||||
|
||||
new = lief.ELF.parse(out)
|
||||
assert new.has_symbol("main_test")
|
||||
|
||||
|
||||
@pytest.mark.skipif(not has_private_samples(), reason="needs private samples")
|
||||
def test_smart_insert_1(tmp_path: Path):
|
||||
"""
|
||||
The purpose of this test is to make sure that when we have a binary with debug
|
||||
info (or not) and we insert a new section/segment, the section is inserted
|
||||
prior the debug info so that stripping the binary works
|
||||
"""
|
||||
input_path = Path(get_sample("private/ELF/libclang-cpp.so.20.1"))
|
||||
elf = lief.ELF.parse(input_path)
|
||||
|
||||
section = lief.ELF.Section(".lief_test")
|
||||
section.content = list(b"This is a test")
|
||||
elf.add(section)
|
||||
|
||||
output = tmp_path / input_path.name
|
||||
elf.write(output.as_posix())
|
||||
|
||||
new_elf = lief.ELF.parse(output)
|
||||
|
||||
sec = new_elf.get_section(".lief_test")
|
||||
assert new_elf.get_section_idx(sec) == 27
|
||||
|
||||
if is_linux():
|
||||
llvm_strip = shutil.which("llvm-strip")
|
||||
assert llvm_strip is not None
|
||||
print(f"Using llvm-strip: {llvm_strip}")
|
||||
popen_args = {
|
||||
"universal_newlines": True,
|
||||
"stdout": subprocess.PIPE,
|
||||
"stderr": subprocess.STDOUT,
|
||||
}
|
||||
|
||||
args = [
|
||||
llvm_strip,
|
||||
output.as_posix()
|
||||
]
|
||||
with Popen(args, **popen_args) as proc: # type: ignore[call-overload]
|
||||
stdout, _ = proc.communicate(10)
|
||||
print("stdout:", stdout)
|
||||
assert proc.returncode == 0
|
||||
assert len(stdout) == 0
|
||||
|
||||
elf_strip = lief.ELF.parse(output)
|
||||
lief_test_section: lief.ELF.Section = elf_strip.get_section(".lief_test")
|
||||
assert lief_test_section is not None
|
||||
print(bytes(lief_test_section.content))
|
||||
assert bytes(lief_test_section.content) == b'This is a test\x00\x00'
|
||||
|
||||
@pytest.mark.skipif(not has_private_samples(), reason="needs private samples")
|
||||
def test_smart_insert_2(tmp_path: Path):
|
||||
input_path = Path(get_sample("private/ELF/libhwui.so"))
|
||||
elf = lief.ELF.parse(input_path)
|
||||
|
||||
section = lief.ELF.Section(".lief_section_to_strip")
|
||||
section.content = list(b"The content of this section needs to be removed")
|
||||
elf.add(section, loaded=False)
|
||||
|
||||
output = tmp_path / input_path.name
|
||||
elf.write(output.as_posix())
|
||||
|
||||
new_elf = lief.ELF.parse(output)
|
||||
|
||||
sec = new_elf.get_section(".lief_section_to_strip")
|
||||
assert new_elf.get_section_idx(sec) == 25
|
||||
|
||||
if is_linux():
|
||||
llvm_strip = shutil.which("llvm-strip")
|
||||
assert llvm_strip is not None
|
||||
print(f"Using llvm-strip: {llvm_strip}")
|
||||
popen_args = {
|
||||
"universal_newlines": True,
|
||||
"stdout": subprocess.PIPE,
|
||||
"stderr": subprocess.STDOUT,
|
||||
}
|
||||
|
||||
args = [
|
||||
llvm_strip,
|
||||
output.as_posix()
|
||||
]
|
||||
|
||||
with Popen(args, **popen_args) as proc: # type: ignore[call-overload]
|
||||
stdout, _ = proc.communicate(10)
|
||||
print("stdout:", stdout)
|
||||
assert proc.returncode == 0
|
||||
assert len(stdout) == 0
|
||||
|
||||
elf_strip = lief.ELF.parse(output)
|
||||
lief_test_section: lief.ELF.Section = elf_strip.get_section(".lief_section_to_strip")
|
||||
assert lief_test_section is None
|
||||
|
||||
Reference in New Issue
Block a user