Skip to content
Permalink
Browse files
Enable ELF interpreter modification (without size restriction)
See: 'examples/python/changer_elf_interpreter.py' for usage

Resolve: #86
  • Loading branch information
romainthomas committed Sep 7, 2017
1 parent f3e7c8d commit b2d3694
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 46 deletions.
@@ -26,6 +26,12 @@ using no_const_getter = T (Binary::*)(void);
template<class T, class P>
using no_const_func = T (Binary::*)(P);

template<class T>
using getter_t = T (Binary::*)(void) const;

template<class T>
using setter_t = void (Binary::*)(T);

void init_ELF_Binary_class(py::module& m) {

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

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

.def("section_from_offset",
static_cast<no_const_func<Section&, uint64_t>>(&Binary::section_from_offset),
@@ -42,7 +42,8 @@ void init_ELF_DynamicEntryArray_class(py::module& m) {
.def_property("array",
static_cast<std::vector<uint64_t>& (DynamicEntryArray::*) (void)>(&DynamicEntryArray::array),
static_cast<setter_t<const std::vector<uint64_t>&>>(&DynamicEntryArray::array),
"Return the array")
"Return the array",
py::return_value_policy::reference)

.def("__eq__", &DynamicEntryArray::operator==)
.def("__ne__", &DynamicEntryArray::operator!=)
@@ -0,0 +1,68 @@
#!/usr/bin/env python
import lief
import argparse
import os
import stat
import sys

def change_interpreter(target, interpreter, output=None):
if not os.path.isfile(target) or not lief.is_elf(target):
print("Wrong target! ({})".format(target))
return 1


if not os.path.isfile(interpreter) or not lief.is_elf(interpreter):
print("Wrong interpreter! ({})".format(interpreter))
return 1

binary = lief.parse(target)
if not binary.has_interpreter:
print("The given target doesn't have interpreter!")
return 1

binary.interpreter = interpreter

output_path = output
if output_path is None:
output_path = os.path.basename(target)
output_path += "_updated"

if os.path.isfile(output_path):
os.remove(output_path)

binary.write(output_path)

# Set as executable
st = os.stat(output_path)
os.chmod(output_path, st.st_mode | stat.S_IEXEC)
return 0


def main():
parser = argparse.ArgumentParser(description='Change the ELF interpreter of the given binary')


parser.add_argument("-o", "--output",
help = 'Path to the binary rewritten',
action = 'store',
default = None)

parser.add_argument("target",
metavar="<elf>",
help='Target ELF file')

parser.add_argument("interpreter",
metavar="<interpreter>",
help='Path to the new interpreter')


args = parser.parse_args()

status = change_interpreter(args.target, args.interpreter, args.output)
sys.exit(status)


if __name__ == "__main__":
main()


@@ -200,7 +200,10 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
bool has_interpreter(void) const;

//! @brief Return ELF interprer if any. (e.g. `/lib64/ld-linux-x86-64.so.2`)
std::string interpreter(void) const;
const std::string& interpreter(void) const;

//! @brief Change the interpreter
void interpreter(const std::string& interpreter);

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

//! object used to manage segments/sections
DataHandler::Handler* datahandler_;

std::string interpreter_;
};

}
@@ -97,6 +97,7 @@ class DLL_PUBLIC Builder {

void build_symbol_version(void);

template<typename ELF_T>
void build_interpreter(void);

bool empties_gnuhash_;
@@ -1155,26 +1155,21 @@ bool Binary::has_interpreter(void) const {
return segment != nullptr and segment->type() == SEGMENT_TYPES::PT_INTERP;
});

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

std::string Binary::interpreter(void) const {
auto&& it_segment_interp = std::find_if(
std::begin(this->segments_),
std::end(this->segments_),
[] (const Segment* segment)
{
return segment != nullptr and segment->type() == SEGMENT_TYPES::PT_INTERP;
});

if (it_segment_interp == std::end(this->segments_)) {
throw not_found("PT_INTERP not found");
const std::string& Binary::interpreter(void) const {
if (not this->has_interpreter()) {
throw not_found("Interpreter not found!");
}
return this->interpreter_;
}

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


void Binary::write(const std::string& filename) {
Builder builder{this};
builder.build();
@@ -152,27 +152,6 @@ void Builder::build_symbol_version(void) {

}

void Builder::build_interpreter(void) {
VLOG(VDEBUG) << "[+] Building Interpreter" << std::endl;
const std::string& inter_str = this->binary_->interpreter();

// Look for the PT_INTERP segment
auto&& it_pt_interp = std::find_if(
std::begin(this->binary_->segments_),
std::end(this->binary_->segments_),
[] (const Segment* s) {
return s->type() == SEGMENT_TYPES::PT_INTERP;
});

if (it_pt_interp == std::end(this->binary_->segments_)) {
throw not_found("Unable to find the INTERP segment");
}

Segment* interp_segment = *it_pt_interp;
interp_segment->content({std::begin(inter_str), std::end(inter_str)});


}



@@ -98,7 +98,7 @@ void Builder::build(void) {
// Build Interpreter
if (this->binary_->has_interpreter()) {
try {
this->build_interpreter();
this->build_interpreter<ELF_T>();
} catch (const LIEF::exception& e) {
LOG(ERROR) << e.what();
}
@@ -1598,7 +1598,7 @@ void Builder::relocate_dynamic_array(DynamicEntryArray& entry_array, DynamicEntr

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

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

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

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

entry_array.value(new_segment.virtual_address());

}

template<typename ELF_T>
void Builder::build_interpreter(void) {
VLOG(VDEBUG) << "[+] Building Interpreter" << std::endl;
const std::string& inter_str = this->binary_->interpreter();

// Look for the PT_INTERP segment
auto&& it_pt_interp = std::find_if(
std::begin(this->binary_->segments_),
std::end(this->binary_->segments_),
[] (const Segment* s) {
return s->type() == SEGMENT_TYPES::PT_INTERP;
});

// Look for the ".interp" section
auto&& it_section_interp = std::find_if(
std::begin(this->binary_->sections_),
std::end(this->binary_->sections_),
[] (const Section* s) {
return s->name() == ".interp";
});


if (it_pt_interp == std::end(this->binary_->segments_)) {
throw not_found("Unable to find the INTERP segment");
}

Segment* interp_segment = *it_pt_interp;
if (inter_str.size() > interp_segment->physical_size()) {
LOG(INFO) << "The 'interpreter' segment needs to be relocated";

// Create a LOAD segment for the new Interpreter:
Segment load_interpreter_segment;
load_interpreter_segment.type(SEGMENT_TYPES::PT_LOAD);
load_interpreter_segment.flags(ELF_SEGMENT_FLAGS::PF_R);
load_interpreter_segment.content({std::begin(inter_str), std::end(inter_str)});
Segment& new_interpreter_load = this->binary_->add(load_interpreter_segment);

interp_segment->virtual_address(new_interpreter_load.virtual_address());
interp_segment->virtual_size(new_interpreter_load.virtual_size());
interp_segment->physical_address(new_interpreter_load.physical_address());

interp_segment->file_offset(new_interpreter_load.file_offset());
interp_segment->physical_size(new_interpreter_load.physical_size());

if (it_section_interp != std::end(this->binary_->sections_)) {
Section* interp = *it_section_interp;
interp->virtual_address(new_interpreter_load.virtual_address());
interp->size(new_interpreter_load.physical_size());
interp->offset(new_interpreter_load.file_offset());
interp->content(new_interpreter_load.content());
interp->original_size_ = new_interpreter_load.physical_size();
}
return this->build<ELF_T>();
}
const char* inter_cstr = inter_str.c_str();
interp_segment->content({inter_cstr, inter_cstr + inter_str.size() + 1});
}
}



} // namespace ELF
} // namespace LIEF
@@ -797,6 +797,9 @@ void Parser::parse_segments(void) {
const uint8_t* content = static_cast<const uint8_t*>(
this->stream_->read(offset_to_content, size));
segment->content({content, content + size});
if (segment->type() == SEGMENT_TYPES::PT_INTERP) {
this->binary_->interpreter_ = this->stream_->read_string(offset_to_content);
}

} catch (const LIEF::read_out_of_bound&) {
LOG(WARNING) << "Segment's file offset and/or segment's size is corrupted";
@@ -297,7 +297,7 @@ void Section::content(const std::vector<uint8_t>& content) {
std::vector<uint8_t>& binary_content = this->datahandler_->content();
this->datahandler_->reserve(node.offset(), content.size());

if (this->original_size() < content.size()) {
if (node.size() < content.size()) {
LOG(WARNING) << "You insert data in section '"
<< this->name() << "' It may lead to overaly! (" << std::hex << node.size() << " < " << content.size() << ")" << std::endl;
}
@@ -205,6 +205,11 @@ if (PYTHON_TESTS_ENABLED)
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/hash_tests.py")


ADD_PYTHON_TEST(ELF_PYTHON_change_interpreter
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/change_interpreter.py")

# Examples
# --------
ADD_PYTHON_TEST(EXAMPLE_PYTHON_elf_reader_ls

0 comments on commit b2d3694

Please sign in to comment.