Skip to content

Commit b2d3694

Browse files
committed
Enable ELF interpreter modification (without size restriction)
See: 'examples/python/changer_elf_interpreter.py' for usage Resolve: #86
1 parent f3e7c8d commit b2d3694

File tree

12 files changed

+255
-46
lines changed

12 files changed

+255
-46
lines changed

api/python/ELF/objects/pyBinary.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ using no_const_getter = T (Binary::*)(void);
2626
template<class T, class P>
2727
using no_const_func = T (Binary::*)(P);
2828

29+
template<class T>
30+
using getter_t = T (Binary::*)(void) const;
31+
32+
template<class T>
33+
using setter_t = void (Binary::*)(T);
34+
2935
void init_ELF_Binary_class(py::module& m) {
3036

3137
// Binary object
@@ -166,9 +172,10 @@ void init_ELF_Binary_class(py::module& m) {
166172
&Binary::has_interpreter,
167173
"``True`` if the binary uses a loader")
168174

169-
.def_property_readonly("interpreter",
170-
&Binary::interpreter,
171-
"Return ELF interprer (loader) if any. (e.g. ``/lib64/ld-linux-x86-64.so.2``)")
175+
.def_property("interpreter",
176+
static_cast<getter_t<const std::string&>>(&Binary::interpreter),
177+
static_cast<setter_t<const std::string&>>(&Binary::interpreter),
178+
"ELF interprer (loader) if any. (e.g. ``/lib64/ld-linux-x86-64.so.2``)")
172179

173180
.def("section_from_offset",
174181
static_cast<no_const_func<Section&, uint64_t>>(&Binary::section_from_offset),

api/python/ELF/objects/pyDynamicEntryArray.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ void init_ELF_DynamicEntryArray_class(py::module& m) {
4242
.def_property("array",
4343
static_cast<std::vector<uint64_t>& (DynamicEntryArray::*) (void)>(&DynamicEntryArray::array),
4444
static_cast<setter_t<const std::vector<uint64_t>&>>(&DynamicEntryArray::array),
45-
"Return the array")
45+
"Return the array",
46+
py::return_value_policy::reference)
4647

4748
.def("__eq__", &DynamicEntryArray::operator==)
4849
.def("__ne__", &DynamicEntryArray::operator!=)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python
2+
import lief
3+
import argparse
4+
import os
5+
import stat
6+
import sys
7+
8+
def change_interpreter(target, interpreter, output=None):
9+
if not os.path.isfile(target) or not lief.is_elf(target):
10+
print("Wrong target! ({})".format(target))
11+
return 1
12+
13+
14+
if not os.path.isfile(interpreter) or not lief.is_elf(interpreter):
15+
print("Wrong interpreter! ({})".format(interpreter))
16+
return 1
17+
18+
binary = lief.parse(target)
19+
if not binary.has_interpreter:
20+
print("The given target doesn't have interpreter!")
21+
return 1
22+
23+
binary.interpreter = interpreter
24+
25+
output_path = output
26+
if output_path is None:
27+
output_path = os.path.basename(target)
28+
output_path += "_updated"
29+
30+
if os.path.isfile(output_path):
31+
os.remove(output_path)
32+
33+
binary.write(output_path)
34+
35+
# Set as executable
36+
st = os.stat(output_path)
37+
os.chmod(output_path, st.st_mode | stat.S_IEXEC)
38+
return 0
39+
40+
41+
def main():
42+
parser = argparse.ArgumentParser(description='Change the ELF interpreter of the given binary')
43+
44+
45+
parser.add_argument("-o", "--output",
46+
help = 'Path to the binary rewritten',
47+
action = 'store',
48+
default = None)
49+
50+
parser.add_argument("target",
51+
metavar="<elf>",
52+
help='Target ELF file')
53+
54+
parser.add_argument("interpreter",
55+
metavar="<interpreter>",
56+
help='Path to the new interpreter')
57+
58+
59+
args = parser.parse_args()
60+
61+
status = change_interpreter(args.target, args.interpreter, args.output)
62+
sys.exit(status)
63+
64+
65+
if __name__ == "__main__":
66+
main()
67+
68+

include/LIEF/ELF/Binary.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,10 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
200200
bool has_interpreter(void) const;
201201

202202
//! @brief Return ELF interprer if any. (e.g. `/lib64/ld-linux-x86-64.so.2`)
203-
std::string interpreter(void) const;
203+
const std::string& interpreter(void) const;
204+
205+
//! @brief Change the interpreter
206+
void interpreter(const std::string& interpreter);
204207

205208
//! @brief Return both static and dynamic symbols
206209
it_symbols symbols(void);
@@ -486,6 +489,8 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
486489

487490
//! object used to manage segments/sections
488491
DataHandler::Handler* datahandler_;
492+
493+
std::string interpreter_;
489494
};
490495

491496
}

include/LIEF/ELF/Builder.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class DLL_PUBLIC Builder {
9797

9898
void build_symbol_version(void);
9999

100+
template<typename ELF_T>
100101
void build_interpreter(void);
101102

102103
bool empties_gnuhash_;

src/ELF/Binary.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,26 +1155,21 @@ bool Binary::has_interpreter(void) const {
11551155
return segment != nullptr and segment->type() == SEGMENT_TYPES::PT_INTERP;
11561156
});
11571157

1158-
return it_segment_interp != std::end(this->segments_);
1158+
return it_segment_interp != std::end(this->segments_) and not this->interpreter_.empty();
11591159
}
11601160

1161-
std::string Binary::interpreter(void) const {
1162-
auto&& it_segment_interp = std::find_if(
1163-
std::begin(this->segments_),
1164-
std::end(this->segments_),
1165-
[] (const Segment* segment)
1166-
{
1167-
return segment != nullptr and segment->type() == SEGMENT_TYPES::PT_INTERP;
1168-
});
1169-
1170-
if (it_segment_interp == std::end(this->segments_)) {
1171-
throw not_found("PT_INTERP not found");
1161+
const std::string& Binary::interpreter(void) const {
1162+
if (not this->has_interpreter()) {
1163+
throw not_found("Interpreter not found!");
11721164
}
1165+
return this->interpreter_;
1166+
}
11731167

1174-
const std::vector<uint8_t>& content = (*it_segment_interp)->content();
1175-
return reinterpret_cast<const char*>(content.data());
1168+
void Binary::interpreter(const std::string& interpreter) {
1169+
this->interpreter_ = interpreter;
11761170
}
11771171

1172+
11781173
void Binary::write(const std::string& filename) {
11791174
Builder builder{this};
11801175
builder.build();

src/ELF/Builder.cpp

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -152,27 +152,6 @@ void Builder::build_symbol_version(void) {
152152

153153
}
154154

155-
void Builder::build_interpreter(void) {
156-
VLOG(VDEBUG) << "[+] Building Interpreter" << std::endl;
157-
const std::string& inter_str = this->binary_->interpreter();
158-
159-
// Look for the PT_INTERP segment
160-
auto&& it_pt_interp = std::find_if(
161-
std::begin(this->binary_->segments_),
162-
std::end(this->binary_->segments_),
163-
[] (const Segment* s) {
164-
return s->type() == SEGMENT_TYPES::PT_INTERP;
165-
});
166-
167-
if (it_pt_interp == std::end(this->binary_->segments_)) {
168-
throw not_found("Unable to find the INTERP segment");
169-
}
170-
171-
Segment* interp_segment = *it_pt_interp;
172-
interp_segment->content({std::begin(inter_str), std::end(inter_str)});
173-
174-
175-
}
176155

177156

178157

src/ELF/Builder.tcc

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void Builder::build(void) {
9898
// Build Interpreter
9999
if (this->binary_->has_interpreter()) {
100100
try {
101-
this->build_interpreter();
101+
this->build_interpreter<ELF_T>();
102102
} catch (const LIEF::exception& e) {
103103
LOG(ERROR) << e.what();
104104
}
@@ -1598,7 +1598,7 @@ void Builder::relocate_dynamic_array(DynamicEntryArray& entry_array, DynamicEntr
15981598

15991599
// /!\ 'entry' is updated by call 'add (segment)' /!
16001600
uint64_t original_init_va = entry_array.value();
1601-
LOG(DEBUG) << "Original Array address: " << std::hex << original_init_va << std::endl;
1601+
VLOG(VDEBUG) << "Original Array address: " << std::hex << original_init_va << std::endl;
16021602
if (this->binary_->header().file_type() == E_TYPE::ET_DYN) {
16031603
for (Relocation& r : this->binary_->dynamic_relocations()) {
16041604

@@ -1639,7 +1639,7 @@ void Builder::relocate_dynamic_array(DynamicEntryArray& entry_array, DynamicEntr
16391639
}
16401640

16411641
// We need to create a new RELATIVE relocation
1642-
LOG(DEBUG) << "Can't find relocation for '0x" << std::hex << array[i] << "' (0x" << address_relocation << ")" << std::endl;
1642+
VLOG(VDEBUG) << "Can't find relocation for '0x" << std::hex << array[i] << "' (0x" << address_relocation << ")" << std::endl;
16431643
const bool is_rela = this->binary_->relocations_.back()->is_rela();
16441644

16451645
switch (arch) {
@@ -1677,13 +1677,74 @@ void Builder::relocate_dynamic_array(DynamicEntryArray& entry_array, DynamicEntr
16771677
relocation->purpose(RELOCATION_PURPOSES::RELOC_PURPOSE_DYNAMIC);
16781678
relocation->architecture_ = arch;
16791679
this->binary_->relocations_.push_back(relocation);
1680-
LOG(DEBUG) << "Relocation added: " << *relocation << std::endl;
1680+
VLOG(VDEBUG) << "Relocation added: " << *relocation << std::endl;
16811681
}
16821682
}
16831683
}
16841684

16851685
entry_array.value(new_segment.virtual_address());
16861686

16871687
}
1688+
1689+
template<typename ELF_T>
1690+
void Builder::build_interpreter(void) {
1691+
VLOG(VDEBUG) << "[+] Building Interpreter" << std::endl;
1692+
const std::string& inter_str = this->binary_->interpreter();
1693+
1694+
// Look for the PT_INTERP segment
1695+
auto&& it_pt_interp = std::find_if(
1696+
std::begin(this->binary_->segments_),
1697+
std::end(this->binary_->segments_),
1698+
[] (const Segment* s) {
1699+
return s->type() == SEGMENT_TYPES::PT_INTERP;
1700+
});
1701+
1702+
// Look for the ".interp" section
1703+
auto&& it_section_interp = std::find_if(
1704+
std::begin(this->binary_->sections_),
1705+
std::end(this->binary_->sections_),
1706+
[] (const Section* s) {
1707+
return s->name() == ".interp";
1708+
});
1709+
1710+
1711+
if (it_pt_interp == std::end(this->binary_->segments_)) {
1712+
throw not_found("Unable to find the INTERP segment");
1713+
}
1714+
1715+
Segment* interp_segment = *it_pt_interp;
1716+
if (inter_str.size() > interp_segment->physical_size()) {
1717+
LOG(INFO) << "The 'interpreter' segment needs to be relocated";
1718+
1719+
// Create a LOAD segment for the new Interpreter:
1720+
Segment load_interpreter_segment;
1721+
load_interpreter_segment.type(SEGMENT_TYPES::PT_LOAD);
1722+
load_interpreter_segment.flags(ELF_SEGMENT_FLAGS::PF_R);
1723+
load_interpreter_segment.content({std::begin(inter_str), std::end(inter_str)});
1724+
Segment& new_interpreter_load = this->binary_->add(load_interpreter_segment);
1725+
1726+
interp_segment->virtual_address(new_interpreter_load.virtual_address());
1727+
interp_segment->virtual_size(new_interpreter_load.virtual_size());
1728+
interp_segment->physical_address(new_interpreter_load.physical_address());
1729+
1730+
interp_segment->file_offset(new_interpreter_load.file_offset());
1731+
interp_segment->physical_size(new_interpreter_load.physical_size());
1732+
1733+
if (it_section_interp != std::end(this->binary_->sections_)) {
1734+
Section* interp = *it_section_interp;
1735+
interp->virtual_address(new_interpreter_load.virtual_address());
1736+
interp->size(new_interpreter_load.physical_size());
1737+
interp->offset(new_interpreter_load.file_offset());
1738+
interp->content(new_interpreter_load.content());
1739+
interp->original_size_ = new_interpreter_load.physical_size();
1740+
}
1741+
return this->build<ELF_T>();
1742+
}
1743+
const char* inter_cstr = inter_str.c_str();
1744+
interp_segment->content({inter_cstr, inter_cstr + inter_str.size() + 1});
16881745
}
1689-
}
1746+
1747+
1748+
1749+
} // namespace ELF
1750+
} // namespace LIEF

src/ELF/Parser.tcc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,9 @@ void Parser::parse_segments(void) {
797797
const uint8_t* content = static_cast<const uint8_t*>(
798798
this->stream_->read(offset_to_content, size));
799799
segment->content({content, content + size});
800+
if (segment->type() == SEGMENT_TYPES::PT_INTERP) {
801+
this->binary_->interpreter_ = this->stream_->read_string(offset_to_content);
802+
}
800803

801804
} catch (const LIEF::read_out_of_bound&) {
802805
LOG(WARNING) << "Segment's file offset and/or segment's size is corrupted";

src/ELF/Section.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ void Section::content(const std::vector<uint8_t>& content) {
297297
std::vector<uint8_t>& binary_content = this->datahandler_->content();
298298
this->datahandler_->reserve(node.offset(), content.size());
299299

300-
if (this->original_size() < content.size()) {
300+
if (node.size() < content.size()) {
301301
LOG(WARNING) << "You insert data in section '"
302302
<< this->name() << "' It may lead to overaly! (" << std::hex << node.size() << " < " << content.size() << ")" << std::endl;
303303
}

tests/elf/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ if (PYTHON_TESTS_ENABLED)
205205
${PYTHON_EXECUTABLE}
206206
"${CMAKE_CURRENT_SOURCE_DIR}/hash_tests.py")
207207

208+
209+
ADD_PYTHON_TEST(ELF_PYTHON_change_interpreter
210+
${PYTHON_EXECUTABLE}
211+
"${CMAKE_CURRENT_SOURCE_DIR}/change_interpreter.py")
212+
208213
# Examples
209214
# --------
210215
ADD_PYTHON_TEST(EXAMPLE_PYTHON_elf_reader_ls

0 commit comments

Comments
 (0)