Fix memory leaks in the MachO Python API and create FatBinary

API Changes:

LIEF::MachO::Parser won't return a 'std::vector' of MachO::Binary*
but a pointer to MachO::FatBinary object
It's a kind of wrapper on std::vector<MachO::Binary*>
This commit is contained in:
Romain Thomas 2017-09-29 09:57:00 +02:00
parent 554fa153af
commit 3602643f5d
32 changed files with 430 additions and 170 deletions

View File

@ -242,7 +242,8 @@ set(LIBFUZZER_SRC_FILES)
if (LIEF_FUZZING)
message(STATUS "Fuzzing Enabled")
set(LIBFUZZER_URL "http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer")
set(LIBFUZZER_VERSION 314494)
set(LIBFUZZER_URL "http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer/") #\?p=${LIBFUZZER_VERSION}")
ExternalProject_Add(lief_libfuzzer
SVN_REPOSITORY ${LIBFUZZER_URL}
CONFIGURE_COMMAND ""

View File

@ -23,18 +23,18 @@
using namespace LIEF::MachO;
Macho_Binary_t** macho_parse(const char *file) {
std::vector<Binary*> macho_binaries{Parser::parse(file)};
FatBinary* macho_binaries = Parser::parse(file);
Macho_Binary_t** c_macho_binaries = static_cast<Macho_Binary_t**>(
malloc((macho_binaries.size() + 1) * sizeof(Macho_Binary_t**)));
malloc((macho_binaries->size() + 1) * sizeof(Macho_Binary_t**)));
for (size_t i = 0; i < macho_binaries.size(); ++i) {
Binary* binary = macho_binaries [i];
for (size_t i = 0; i < macho_binaries->size(); ++i) {
Binary& binary = (*macho_binaries)[i];
c_macho_binaries[i] = static_cast<Macho_Binary_t*>(malloc(sizeof(Macho_Binary_t)));
init_c_binary(c_macho_binaries[i], binary);
init_c_binary(c_macho_binaries[i], &binary);
}
c_macho_binaries[macho_binaries.size()] = nullptr;
c_macho_binaries[macho_binaries->size()] = nullptr;
return c_macho_binaries;
}

View File

@ -2,6 +2,7 @@ set(LIEF_PYTHON_MACHO_SRC
"${CMAKE_CURRENT_LIST_DIR}/pyMachO.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyDylibCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyBinary.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyFatBinary.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyLoadCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pySegmentCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyHeader.cpp"

View File

@ -0,0 +1,60 @@
/* Copyright 2017 R. Thomas
* Copyright 2017 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 <algorithm>
#include "LIEF/MachO/FatBinary.hpp"
#include "pyMachO.hpp"
void init_MachO_FatBinary_class(py::module& m) {
py::class_<FatBinary>(m, "FatBinary")
.def_property_readonly("size",
&FatBinary::size,
"Number of " RST_CLASS_REF(lief.MachO.Binary) " registred")
.def("at",
static_cast<Binary& (FatBinary::*)(size_t)>(&FatBinary::at),
"Return the " RST_CLASS_REF(lief.MachO.Binary) " at the given index",
"index"_a)
.def("__len__",
&FatBinary::size)
.def("__getitem__",
static_cast<Binary& (FatBinary::*)(size_t)>(&FatBinary::operator[]),
"",
py::return_value_policy::reference)
.def("__iter__",
static_cast<it_binaries (FatBinary::*)(void)>(&FatBinary::begin),
py::return_value_policy::reference_internal)
.def("__str__",
[] (const FatBinary& fat_binary)
{
std::ostringstream stream;
stream << fat_binary;
std::string str = stream.str();
return str;
});
}

View File

@ -24,14 +24,14 @@ void init_MachO_Parser_class(py::module& m) {
// Parser (Parser)
m.def("parse",
static_cast<std::vector<Binary*> (*) (const std::string&)>(&LIEF::MachO::Parser::parse),
static_cast<FatBinary* (*) (const std::string&)>(&LIEF::MachO::Parser::parse),
"Parse the given binary and return a **list** of " RST_CLASS_REF(lief.MachO.Binary) " objects",
"filename"_a,
py::return_value_policy::take_ownership);
m.def("parse",
static_cast<std::vector<Binary*> (*) (const std::vector<uint8_t>&, const std::string&)>(&LIEF::MachO::Parser::parse),
static_cast<FatBinary* (*) (const std::vector<uint8_t>&, const std::string&)>(&LIEF::MachO::Parser::parse),
"Parse the given binary (from raw) and return a **list** of " RST_CLASS_REF(lief.MachO.Binary) " objects",
py::arg("raw"), py::arg("name") = "",
py::return_value_policy::take_ownership);

View File

@ -23,10 +23,9 @@
void init_MachO_module(py::module& m) {
py::module LIEF_MachO_module = m.def_submodule("MachO", "Python API for MachO");
py::bind_vector<std::vector<Binary*>>(m, "macho_list");
// Objects
init_MachO_Parser_class(LIEF_MachO_module);
init_MachO_FatBinary_class(LIEF_MachO_module);
init_MachO_Binary_class(LIEF_MachO_module);
init_MachO_Header_class(LIEF_MachO_module);
init_MachO_LoadCommand_class(LIEF_MachO_module);

View File

@ -27,6 +27,7 @@ using namespace LIEF::MachO;
PYBIND11_MAKE_OPAQUE(std::vector<Binary*>)
void init_MachO_Parser_class(py::module&);
void init_MachO_FatBinary_class(py::module&);
void init_MachO_Binary_class(py::module&);
void init_MachO_Header_class(py::module&);
void init_MachO_LoadCommand_class(py::module&);

View File

@ -63,6 +63,7 @@ void init_LIEF_iterators(py::module& m) {
// MachO
// =====
#if defined(LIEF_MACHO_MODULE)
init_ref_iterator<LIEF::MachO::it_binaries>(m);
init_ref_iterator<LIEF::MachO::it_relocations>(m);
init_ref_iterator<LIEF::MachO::it_commands>(m);
init_ref_iterator<LIEF::MachO::it_symbols>(m);

View File

@ -13,6 +13,14 @@ Parsers
----------
FatBinary
*********
.. doxygenclass:: LIEF::MachO::FatBinary
:project: lief
----------
Binary
******

View File

@ -10,6 +10,17 @@ Parser
----------
FatBinary
*********
.. autoclass:: lief.MachO.FatBinary
:members:
:inherited-members:
:undoc-members:
----------
.. _python-macho-binary-api-ref:
Binary

View File

@ -38,13 +38,9 @@ int main(int argc, char **argv) {
return -1;
}
std::vector<MachO::Binary*> binaries = MachO::Parser::parse(argv[1]);
MachO::Binary* binary = binaries.back();
binary->write(argv[2]);
for (MachO::Binary *b : binaries) {
delete b;
}
std::unique_ptr<LIEF::MachO::FatBinary> binaries{MachO::Parser::parse(argv[1])};
MachO::Binary& binary = binaries->back();
binary.write(argv[2]);
return 0;
}

View File

@ -30,10 +30,10 @@ int main(int argc, char **argv) {
return -1;
}
std::vector<MachO::Binary*> binaries = MachO::Parser::parse(argv[1]);
MachO::Binary* binary = binaries.back();
auto segments = binary->segments();
auto itSegment = std::find_if(
std::unique_ptr<LIEF::MachO::FatBinary> binaries{MachO::Parser::parse(argv[1])};
MachO::Binary& binary = binaries->back();
auto&& segments = binary.segments();
auto&& itSegment = std::find_if(
std::begin(segments),
std::end(segments),
[] (const MachO::SegmentCommand& segment) {
@ -64,7 +64,7 @@ int main(int argc, char **argv) {
segment_header.segname[segment_name.size()] = 0;
segment_header.vmaddr = 0x200050000;
segment_header.vmsize = 0x2000;
segment_header.fileoff = binary->original_size();
segment_header.fileoff = binary.original_size();
segment_header.filesize = payload.size();
segment_header.maxprot = 7;
segment_header.initprot = 3;
@ -82,11 +82,8 @@ int main(int argc, char **argv) {
reinterpret_cast<uint8_t*>(&segment_header) + sizeof(segment_header)
});
//binary->insert_command(std::move(segment));
binary->write(argv[2]);
binary.write(argv[2]);
for (MachO::Binary *b : binaries) {
delete b;
}
return 0;
}

View File

@ -20,23 +20,23 @@
using namespace LIEF::MachO;
void print_binary(const Binary* binary) {
std::cout << binary->header() << std::endl;
void print_binary(const Binary& binary) {
std::cout << binary.header() << std::endl;
std::cout << "== Library ==" << std::endl;
for (const DylibCommand& library : binary->libraries()) {
for (const DylibCommand& library : binary.libraries()) {
std::cout << library << std::endl;
}
std::cout << std::endl;
std::cout << "== Sections ==" << std::endl;
for (const Section& section : binary->sections()) {
for (const Section& section : binary.sections()) {
std::cout << section << std::endl;
}
//std::cout << "== Segments ==" << std::endl;
//for (SegmentCommand& segment : binary->segments()) {
//for (SegmentCommand& segment : binary.segments()) {
// std::cout << segment << std::endl;
// if (segment.sections().size() > 0) {
// //std::cout << std::hex;
@ -61,31 +61,31 @@ void print_binary(const Binary* binary) {
//}
//std::cout << std::endl;
//auto commands = binary->commands();
for (const LoadCommand& cmd : binary->commands()) {
//auto commands = binary.commands();
for (const LoadCommand& cmd : binary.commands()) {
std::cout << cmd << std::endl;
std::cout << "======================" << std::endl;
}
std::cout << "== Symbols ==" << std::endl;
for (const Symbol& symbol : binary->symbols()) {
for (const Symbol& symbol : binary.symbols()) {
std::cout << symbol << std::endl;
}
std::cout << "== Exported symbols ==" << std::endl;
for (const Symbol& symbol : binary->exported_symbols()) {
for (const Symbol& symbol : binary.exported_symbols()) {
std::cout << symbol << std::endl;
}
std::cout << "== Imported symbols ==" << std::endl;
for (const Symbol& symbol : binary->imported_symbols()) {
for (const Symbol& symbol : binary.imported_symbols()) {
std::cout << symbol << std::endl;
}
std::cout << "== Relocations ==" << std::endl;
for (const Relocation& relocation : binary->relocations()) {
for (const Relocation& relocation : binary.relocations()) {
std::cout << relocation << std::endl;
}
@ -99,16 +99,12 @@ int main(int argc, char **argv) {
std::cerr << "Usage: " << argv[0] << " <MachO binary>" << std::endl;
return -1;
}
std::vector<Binary*> binaries = Parser::parse(argv[1]);
for (const Binary* binary : binaries) {
std::unique_ptr<FatBinary> binaries{Parser::parse(argv[1])};
for (const Binary& binary : *binaries) {
print_binary(binary);
std::cout << std::endl;
}
for (Binary* binary : binaries) {
delete binary;
}
return 0;
}

View File

@ -1,3 +1,4 @@
include(ProcessorCount)
set(LIEF_FUZZER_SRC
elf_fuzzer.cpp
pe_fuzzer.cpp
@ -33,7 +34,8 @@ ExternalProject_Add(lief_fuzzer_corpus
INSTALL_COMMAND ""
GIT_REPOSITORY ${SAMPLES_GIT_URL}
GIT_TAG ${SAMPLES_TAG}
UPDATE_COMMAND ${GIT_EXECUTABLE} pull
#UPDATE_COMMAND ${GIT_EXECUTABLE} pull
UPDATE_COMMAND ""
)
ExternalProject_Get_Property(lief_fuzzer_corpus source_dir)
@ -41,7 +43,9 @@ set(LIEF_CORUPUS_DIRECTORY "${source_dir}" CACHE INTERNAL "Path to LIEF samples"
message(STATUS "Samples directory: ${LIEF_CORUPUS_DIRECTORY}")
ProcessorCount(N)
set(LIB_FUZZER_ARGS -detect_leaks=1 -rss_limit_mb=0 -print_final_stats=1 -jobs=${N})
set(ASAN_OPTIONS allocator_may_return_null=1)
# ELF
# ===
set(ELF_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}/elf-output)
@ -50,7 +54,7 @@ add_custom_target(build-elf-fuzz-output
COMMAND ${CMAKE_COMMAND} -E make_directory ${ELF_WORKING_DIR})
add_custom_target("fuzz-elf"
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/elf_fuzzer ${LIEF_CORUPUS_DIRECTORY}/ELF -detect_leaks=1 -print_final_stats=1
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=${ASAN_OPTIONS} ${CMAKE_CURRENT_BINARY_DIR}/elf_fuzzer ${LIEF_CORUPUS_DIRECTORY}/ELF ${LIB_FUZZER_ARGS}
DEPENDS elf_fuzzer LIB_LIEF_STATIC build-elf-fuzz-output lief_fuzzer_corpus
WORKING_DIRECTORY ${ELF_WORKING_DIR}
COMMENT "Run ELF fuzzer")
@ -64,7 +68,7 @@ add_custom_target(build-pe-fuzz-output
COMMAND ${CMAKE_COMMAND} -E make_directory ${PE_WORKING_DIR})
add_custom_target("fuzz-pe"
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/pe_fuzzer ${LIEF_CORUPUS_DIRECTORY}/PE -detect_leaks=1 -print_final_stats=1
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS="${ASAN_OPTIONS}" ${CMAKE_CURRENT_BINARY_DIR}/pe_fuzzer ${LIEF_CORUPUS_DIRECTORY}/PE ${LIB_FUZZER_ARGS}
DEPENDS pe_fuzzer LIB_LIEF_STATIC build-pe-fuzz-output lief_fuzzer_corpus
WORKING_DIRECTORY ${PE_WORKING_DIR}
COMMENT "Run PE fuzzer")
@ -74,10 +78,10 @@ add_custom_target("fuzz-pe"
set(MACHO_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}/macho-output)
add_custom_target(build-macho-fuzz-output
COMMAND ${CMAKE_COMMAND} -E make_directory ${MACHO_WORKING_DIR})
COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=${ASAN_OPTIONS} ${CMAKE_COMMAND} -E make_directory ${MACHO_WORKING_DIR})
add_custom_target("fuzz-macho"
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/macho_fuzzer ${LIEF_CORUPUS_DIRECTORY}/MachO -detect_leaks=1 -print_final_stats=1
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/macho_fuzzer ${LIEF_CORUPUS_DIRECTORY}/MachO ${LIB_FUZZER_ARGS}
DEPENDS macho_fuzzer LIB_LIEF_STATIC build-macho-fuzz-output lief_fuzzer_corpus
WORKING_DIRECTORY ${MACHO_WORKING_DIR}
COMMENT "Run MachO fuzzer")

View File

@ -4,14 +4,11 @@
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::vector<uint8_t> raw = {data, data + size};
std::vector<LIEF::MachO::Binary*> binaries;
std::unique_ptr<LIEF::MachO::FatBinary> binaries;
try {
binaries = LIEF::MachO::Parser::parse(raw);
binaries = std::unique_ptr<LIEF::MachO::FatBinary>(LIEF::MachO::Parser::parse(raw));
} catch (const LIEF::exception& e) {
std::cout << e.what() << std::endl;
}
for (LIEF::MachO::Binary* b: binaries) {
delete b;
}
return 0;
}

View File

@ -0,0 +1,73 @@
/* Copyright 2017 R. Thomas
* Copyright 2017 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.
*/
#ifndef LIEF_MACHO_FAT_BINARY_H_
#define LIEF_MACHO_FAT_BINARY_H_
#include <vector>
#include "LIEF/types.hpp"
#include "LIEF/visibility.h"
#include "LIEF/MachO/Binary.hpp"
namespace LIEF {
class Parser;
namespace MachO {
class Parser;
class Builder;
class DLL_PUBLIC FatBinary {
friend class LIEF::Parser;
friend class Parser;
friend class Builder;
public:
FatBinary(const FatBinary&) = delete;
virtual ~FatBinary(void);
//! @brief Number of @link MachO::Binary binary @endlink registred
size_t size(void) const;
it_binaries begin(void);
it_const_binaries begin(void) const;
it_binaries end(void);
it_const_binaries end(void) const;
Binary& at(size_t index);
const Binary& at(size_t index) const;
Binary& back(void);
const Binary& back(void) const;
Binary& operator[](size_t index);
const Binary& operator[](size_t index) const;
DLL_PUBLIC friend std::ostream& operator<<(std::ostream& os, const FatBinary& fatbinary);
private:
FatBinary(void);
FatBinary(const std::vector<Binary*>& binaries);
binaries_t binaries_;
};
} // namespace MachO
} // namespace LIEF
#endif

View File

@ -26,6 +26,7 @@
#include "LIEF/MachO/Structures.hpp"
#include "LIEF/MachO/Binary.hpp"
#include "LIEF/MachO/FatBinary.hpp"
namespace LIEF {
@ -37,8 +38,8 @@ class DLL_PUBLIC Parser : public LIEF::Parser {
~Parser(void);
static std::vector<Binary*> parse(const std::string& filename);
static std::vector<Binary*> parse(const std::vector<uint8_t>& data, const std::string& name = "");
static FatBinary* parse(const std::string& filename);
static FatBinary* parse(const std::vector<uint8_t>& data, const std::string& name = "");
private:
Parser(const std::string& file);

View File

@ -38,6 +38,11 @@ struct KeyCmp {
};
using binaries_t = std::vector<Binary*>;
using it_binaries = ref_iterator<binaries_t&>;
using it_const_binaries = const_ref_iterator<const binaries_t&>;
using buffer_t = std::vector<uint8_t>; ///< Container used to store raw data
using commands_t = std::vector<LoadCommand*>;

View File

@ -21,6 +21,7 @@
#include "LIEF/exception.hpp"
#include "LIEF/visibility.h"
#include "LIEF/utils.hpp"
#include "LIEF/Abstract/Parser.hpp"
@ -41,7 +42,13 @@ namespace PE {
class DLL_PUBLIC Parser : public LIEF::Parser {
//! @brief Minimum size for a DLL's name
constexpr static unsigned MIN_DLL_NAME_SIZE = 4;
static constexpr unsigned MIN_DLL_NAME_SIZE = 4;
//! @brief Maximum size of the data read
static constexpr size_t MAX_DATA_SIZE = 3_GB;
static constexpr size_t MAX_TLS_CALLBACKS = 3000;
public:
static Binary* parse(const std::string& filename);

View File

@ -51,14 +51,13 @@ Binary* Parser::parse(const std::string& filename) {
#if defined(LIEF_MACHO_MODULE)
if (MachO::is_macho(filename)) {
// For fat binary we take the last one...
std::vector<MachO::Binary*> binaries = MachO::Parser::parse(filename);
MachO::Binary* binary_return = binaries.back();
binaries.pop_back();
MachO::FatBinary* binaries = MachO::Parser::parse(filename);
MachO::Binary& binary_return = (*binaries)[0];
// delete others
for (MachO::Binary* binary : binaries) {
delete binary;
for (size_t i = 1; i < binaries->size(); ++i) {
delete binaries->binaries_[i];
}
return binary_return;
return &binary_return;
}
#endif
@ -84,14 +83,13 @@ Binary* Parser::parse(const std::vector<uint8_t>& raw, const std::string& name)
#if defined(LIEF_MACHO_MODULE)
if (MachO::is_macho(raw)) {
// For fat binary we take the last one...
std::vector<MachO::Binary*> binaries = MachO::Parser::parse(raw, name);
MachO::Binary* binary_return = binaries.back();
binaries.pop_back();
MachO::FatBinary* binaries = MachO::Parser::parse(raw, name);
MachO::Binary& binary_return = (*binaries)[0];
// delete others
for (MachO::Binary* binary : binaries) {
delete binary;
for (size_t i = 1; i < binaries->size(); ++i) {
delete binaries->binaries_[i];
}
return binary_return;
return &binary_return;
}
#endif

View File

@ -101,11 +101,13 @@ std::string VectorStream::get_string(uint64_t offset, uint64_t size) const {
throw LIEF::read_out_of_bound(offset);
}
uint64_t max_size = this->size() - (offset + size);
size_t max_size = static_cast<size_t>(this->size() - (offset + size));
if (size > 0) {
max_size = std::min<uint64_t>(max_size, size);
max_size = std::min<size_t>(max_size, size);
}
std::string tmp{this->read_string(offset, max_size), max_size};
const char* str = this->read_string(offset);
const char* it_null = std::find(str, str + max_size, '\0');
std::string tmp{str, it_null};
return tmp.c_str();
}

View File

@ -299,7 +299,7 @@ void Parser::parse_notes(uint64_t offset, uint64_t size) {
break;
}
std::string name = {this->stream_->read_string(current_offset, namesz), namesz - 1};
std::string name = this->stream_->get_string(current_offset, namesz);
VLOG(VDEBUG) << "Name: " << name << std::endl;
current_offset += namesz;
current_offset = align(current_offset, sizeof(uint32_t));

View File

@ -837,8 +837,8 @@ void Parser::parse_sections(void) {
const Section* string_section = this->binary_->sections_[section_string_index];
for (Section* section : this->binary_->sections_) {
try {
section->name({this->stream_->read_string(
string_section->file_offset() + section->name_idx())});
section->name(this->stream_->get_string(
string_section->file_offset() + section->name_idx()));
} catch (const LIEF::read_out_of_bound&) {
LOG(WARNING) << "Section's name is corrupted";
}
@ -884,7 +884,7 @@ void Parser::parse_segments(void) {
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, segment->physical_size());
this->binary_->interpreter_ = this->stream_->get_string(offset_to_content, segment->physical_size());
}
} catch (const LIEF::read_out_of_bound&) {
@ -957,8 +957,8 @@ void Parser::parse_static_symbols(uint64_t offset, uint32_t nbSymbols, const Sec
for (uint32_t i = 0; i < nbSymbols; ++i) {
std::unique_ptr<Symbol> symbol{new Symbol{&symbol_headers[i]}};
try {
std::string symbol_name = {this->stream_->read_string(
string_section->file_offset() + symbol_headers[i].st_name)};
std::string symbol_name = this->stream_->get_string(
string_section->file_offset() + symbol_headers[i].st_name);
symbol->name(symbol_name);
} catch (const LIEF::read_out_of_bound& e) {
LOG(WARNING) << e.what();
@ -994,7 +994,7 @@ void Parser::parse_dynamic_symbols(uint64_t offset) {
if (symbol_headers->st_name > 0) {
try {
std::string name{
this->stream_->read_string(string_offset + symbol_headers->st_name)};
this->stream_->get_string(string_offset + symbol_headers->st_name)};
symbol->name(name);
} catch (const exception& e) {
VLOG(VDEBUG) << e.what();
@ -1046,8 +1046,7 @@ void Parser::parse_dynamic_entries(uint64_t offset, uint64_t size) {
if (dynamic_string_offset == 0) {
LOG(WARNING) << "Unable to find the .dynstr section";
} else {
std::string library_name = {
this->stream_->read_string(dynamic_string_offset + dynamic_entry->value())};
std::string library_name = this->stream_->get_string(dynamic_string_offset + dynamic_entry->value());
dynamic_entry->name(library_name);
}
break;
@ -1061,8 +1060,7 @@ void Parser::parse_dynamic_entries(uint64_t offset, uint64_t size) {
if (dynamic_string_offset == 0) {
LOG(WARNING) << "Unable to find the .dynstr section";
} else {
std::string sharename = {
this->stream_->read_string(dynamic_string_offset + dynamic_entry->value())};
std::string sharename = this->stream_->get_string(dynamic_string_offset + dynamic_entry->value());
dynamic_entry->name(sharename);
}
break;
@ -1075,8 +1073,7 @@ void Parser::parse_dynamic_entries(uint64_t offset, uint64_t size) {
if (dynamic_string_offset == 0) {
LOG(WARNING) << "Unable to find the .dynstr section";
} else {
std::string name = {
this->stream_->read_string(dynamic_string_offset + dynamic_entry->value())};
std::string name = this->stream_->get_string(dynamic_string_offset + dynamic_entry->value());
dynamic_entry->name(name);
}
break;
@ -1090,8 +1087,7 @@ void Parser::parse_dynamic_entries(uint64_t offset, uint64_t size) {
if (dynamic_string_offset == 0) {
LOG(WARNING) << "Unable to find the .dynstr section";
} else {
std::string name = {
this->stream_->read_string(dynamic_string_offset + dynamic_entry->value())};
std::string name = this->stream_->get_string(dynamic_string_offset + dynamic_entry->value());
dynamic_entry->name(name);
}
break;
@ -1378,8 +1374,8 @@ void Parser::parse_symbol_version_requirement(uint64_t offset, uint32_t nb_entri
std::unique_ptr<SymbolVersionRequirement> symbol_version_requirement{new SymbolVersionRequirement{header}};
if (string_offset != 0) {
symbol_version_requirement->name({
this->stream_->read_string(string_offset + header->vn_file)});
symbol_version_requirement->name(
this->stream_->get_string(string_offset + header->vn_file));
}
const uint32_t nb_symbol_aux = header->vn_cnt;
@ -1401,7 +1397,7 @@ void Parser::parse_symbol_version_requirement(uint64_t offset, uint32_t nb_entri
std::unique_ptr<SymbolVersionAuxRequirement> svar{new SymbolVersionAuxRequirement{aux_header}};
if (string_offset != 0) {
svar->name({this->stream_->read_string(string_offset + aux_header->vna_name)});
svar->name(this->stream_->get_string(string_offset + aux_header->vna_name));
}
symbol_version_requirement->symbol_version_aux_requirement_.push_back(svar.release());
@ -1463,7 +1459,7 @@ void Parser::parse_symbol_version_definition(uint64_t offset, uint32_t nb_entrie
offset + next_symbol_offset + svd_header->vd_aux + next_aux_offset,
sizeof(Elf_Verdaux)));
if (string_offset != 0) {
std::string name = {this->stream_->read_string(string_offset + svda_header->vda_name)};
std::string name = this->stream_->get_string(string_offset + svda_header->vda_name);
symbol_version_definition->symbol_version_aux_.push_back(new SymbolVersionAux{name});
}

View File

@ -161,7 +161,7 @@ void BinaryParser::parse_export_trie(uint64_t start, uint64_t current_offset, ui
const uint8_t nb_children = this->stream_->read_integer<uint8_t>(children_offset);
children_offset += sizeof(uint8_t);
for (size_t i = 0; i < nb_children; ++i) {
std::string suffix = this->stream_->read_string(children_offset);
std::string suffix = this->stream_->get_string(children_offset);
std::string name = prefix + suffix;
children_offset += suffix.size() + 1;

View File

@ -152,7 +152,7 @@ void BinaryParser::parse_load_commands(void) {
load_command = std::unique_ptr<DylibCommand>{new DylibCommand{cmd}};
const uint32_t str_name_offset = cmd->dylib.name;
std::string name = {this->stream_->read_string(loadcommands_offset + str_name_offset)};
std::string name = this->stream_->get_string(loadcommands_offset + str_name_offset);
dynamic_cast<DylibCommand*>(load_command.get())->name(name);
break;
@ -182,9 +182,9 @@ void BinaryParser::parse_load_commands(void) {
this->stream_->read(loadcommands_offset, sizeof(dylinker_command)));
const uint32_t linker_name_offset = cmd->name;
std::string name = {this->stream_->read_string(
std::string name = this->stream_->get_string(
loadcommands_offset +
linker_name_offset)};
linker_name_offset);
load_command = std::unique_ptr<DylinkerCommand>{new DylinkerCommand{cmd}};
dynamic_cast<DylinkerCommand*>(load_command.get())->name(name);
@ -204,9 +204,9 @@ void BinaryParser::parse_load_commands(void) {
this->stream_->read(loadcommands_offset, sizeof(prebound_dylib_command)));
std::string name = {this->stream_->read_string(
std::string name = this->stream_->get_string(
loadcommands_offset +
cmd->name)};
cmd->name);
//uint32_t sizeof_linked_modules = (cmd->nmodules / 8) + (cmd->nmodules % 8);
@ -308,7 +308,7 @@ void BinaryParser::parse_load_commands(void) {
uint32_t idx = nlist[j].n_strx;
if (idx > 0) {
symbol->name(
this->stream_->read_string(cmd->stroff + idx));
this->stream_->get_string(cmd->stroff + idx));
}
this->binary_->symbols_.push_back(symbol.release());
}
@ -879,7 +879,7 @@ void BinaryParser::parse_dyldinfo_generic_bind() {
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
{
symbol_name = this->stream_->read_string(current_offset);
symbol_name = this->stream_->get_string(current_offset);
current_offset += symbol_name.size() + 1;
if ((imm & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0) {
@ -1069,7 +1069,7 @@ void BinaryParser::parse_dyldinfo_weak_bind() {
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
{
symbol_name = this->stream_->read_string(current_offset);
symbol_name = this->stream_->get_string(current_offset);
current_offset += symbol_name.size() + 1;
if ((imm & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) != 0) {
@ -1289,7 +1289,7 @@ void BinaryParser::parse_dyldinfo_lazy_bind() {
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
{
symbol_name = this->stream_->read_string(current_offset);
symbol_name = this->stream_->get_string(current_offset);
current_offset += symbol_name.size() + 1;
if ((imm & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0) {
@ -1366,26 +1366,17 @@ void BinaryParser::do_bind(BINDING_CLASS cls,
// Address to bind
uint64_t address = segment.virtual_address() + segment_offset;
// Check if a relocation already exists:
Relocation* reloc = nullptr;
std::unique_ptr<Relocation> new_relocation{nullptr};
bool reloc_exists = false;
auto&& it_reloc = std::find_if(
std::begin(segment.relocations_),
std::end(segment.relocations_),
[address] (const Relocation* r) {
return r->address() == address;
});
if (it_reloc != std::end(segment.relocations_)) {
reloc = *it_reloc;
reloc_exists = true;
std::unique_ptr<RelocationDyld> new_relocation{new RelocationDyld{address, type}};
auto&& result = segment.relocations_.emplace(new_relocation.get());
Relocation* reloc = *result.first;
if (result.second) {
new_relocation.release();
} else {
new_relocation = std::unique_ptr<Relocation>{new RelocationDyld{address, type}};
reloc = new_relocation.get();
delete new_relocation.release();
}
reloc->architecture_ = this->binary_->header().cpu_type();
switch (static_cast<BIND_TYPES>(type)) {
@ -1445,9 +1436,7 @@ void BinaryParser::do_bind(BINDING_CLASS cls,
LOG(ERROR) << "New symbol found: " << symbol_name;
}
if (not reloc_exists) {
segment.relocations_.emplace(new_relocation.release());
}
this->binary_->dyld_info().binding_info_.push_back(binding_info.release());
VLOG(VDEBUG) << to_string(cls) << segment.name() << " - " << symbol_name;
}
@ -1467,15 +1456,13 @@ void BinaryParser::do_rebase(uint8_t type, uint8_t segment_idx, uint64_t segment
uint64_t address = segment.virtual_address() + segment_offset;
// Check if a relocation already exists:
Relocation* reloc = nullptr;
bool reloc_exists = false;
reloc = new RelocationDyld{address, type};
auto&& it_reloc = segment.relocations_.find(reloc);
if (it_reloc != std::end(segment.relocations_)) {
delete reloc;
reloc = *it_reloc;
reloc_exists = true;
std::unique_ptr<RelocationDyld> new_relocation{new RelocationDyld{address, type}};
auto&& result = segment.relocations_.emplace(new_relocation.get());
Relocation* reloc = *result.first;
if (result.second) {
new_relocation.release();
} else {
delete new_relocation.release();
}
reloc->architecture_ = this->binary_->header().cpu_type();
@ -1500,9 +1487,6 @@ void BinaryParser::do_rebase(uint8_t type, uint8_t segment_idx, uint64_t segment
LOG(ERROR) << "Unsuported relocation type: 0x" << std::hex << type;
}
}
if (not reloc_exists) {
segment.relocations_.insert(it_reloc, reloc);
}
}

View File

@ -18,6 +18,7 @@ set(LIEF_MACHO_SRC
"${CMAKE_CURRENT_LIST_DIR}/Binary.cpp"
"${CMAKE_CURRENT_LIST_DIR}/UUIDCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Symbol.cpp"
"${CMAKE_CURRENT_LIST_DIR}/FatBinary.cpp"
"${CMAKE_CURRENT_LIST_DIR}/EnumToString.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Header.cpp"
"${CMAKE_CURRENT_LIST_DIR}/DynamicSymbolCommand.cpp"
@ -47,6 +48,7 @@ set(LIEF_MACHO_SRC
)
set(LIEF_MACHO_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/FatBinary.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/Binary.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/BinaryParser.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/MachO/Builder.hpp"

101
src/MachO/FatBinary.cpp Normal file
View File

@ -0,0 +1,101 @@
/* Copyright 2017 R. Thomas
* Copyright 2017 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 <numeric>
#include <iomanip>
#include "LIEF/MachO/FatBinary.hpp"
namespace LIEF {
namespace MachO {
FatBinary::FatBinary(void) :
binaries_{}
{}
FatBinary::FatBinary(const std::vector<Binary*>& binaries) :
binaries_{binaries}
{}
size_t FatBinary::size(void) const {
return this->binaries_.size();
}
it_binaries FatBinary::begin(void) {
return this->binaries_;
}
it_const_binaries FatBinary::begin(void) const {
return this->binaries_;
}
it_binaries FatBinary::end(void) {
return it_binaries{this->binaries_}.end();
}
it_const_binaries FatBinary::end(void) const {
return it_const_binaries{this->binaries_}.end();
}
Binary& FatBinary::at(size_t index) {
return const_cast<Binary&>(static_cast<const FatBinary*>(this)->at(index));
}
const Binary& FatBinary::at(size_t index) const {
if (index >= this->size()) {
throw std::out_of_range("Bad index");
}
return *this->binaries_[index];
}
Binary& FatBinary::back(void) {
return const_cast<Binary&>(static_cast<const FatBinary*>(this)->back());
}
const Binary& FatBinary::back(void) const {
return *this->binaries_.back();
}
Binary& FatBinary::operator[](size_t index) {
return const_cast<Binary&>(static_cast<const FatBinary*>(this)->operator[](index));
}
const Binary& FatBinary::operator[](size_t index) const {
return this->at(index);
}
std::ostream& operator<<(std::ostream& os, const FatBinary& fatbinary) {
for (const Binary& binary : fatbinary) {
os << binary;
os << std::endl << std::endl;
}
return os;
}
FatBinary::~FatBinary(void) {
for (Binary* b : this->binaries_) {
delete b;
}
}
}
}

View File

@ -51,13 +51,13 @@ Parser::Parser(const std::string& file) :
}
std::vector<Binary*> Parser::parse(const std::string& filename) {
FatBinary* Parser::parse(const std::string& filename) {
if (not is_macho(filename)) {
throw bad_file("'" + filename + "' is not a MachO binary");
}
Parser parser{filename};
return parser.binaries_;
return new FatBinary{parser.binaries_};
}
// From Vector
@ -73,13 +73,13 @@ Parser::Parser(const std::vector<uint8_t>& data, const std::string& name) :
}
std::vector<Binary*> Parser::parse(const std::vector<uint8_t>& data, const std::string& name) {
FatBinary* Parser::parse(const std::vector<uint8_t>& data, const std::string& name) {
if (not is_macho(data)) {
throw bad_file("'" + name + "' is not a MachO binary");
}
Parser parser{data, name};
return parser.binaries_;
return new FatBinary{parser.binaries_};
}

View File

@ -210,14 +210,18 @@ void Parser::build_sections(void) {
try {
const uint8_t* ptr_to_rawdata = reinterpret_cast<const uint8_t*>(this->stream_->read(
offset,
size_to_read));
if (size_to_read > Parser::MAX_DATA_SIZE) {
LOG(WARNING) << "Section '" << section->name() << "' data is too large!";
} else {
const uint8_t* ptr_to_rawdata = reinterpret_cast<const uint8_t*>(this->stream_->read(
offset,
size_to_read));
section->content_ = {
ptr_to_rawdata,
ptr_to_rawdata + size_to_read
};
section->content_ = {
ptr_to_rawdata,
ptr_to_rawdata + size_to_read
};
}
} catch (const std::bad_alloc& e) {
LOG(WARNING) << "Section " << section->name() << " corrupted: " << e.what();
} catch (const read_out_of_bound& e) {
@ -464,7 +468,7 @@ void Parser::build_symbols(void) {
this->binary_->header().numberof_symbols() * STRUCT_SIZES::Symbol16Size +
offset;
try {
symbol.name_ = this->stream_->read_string(offset_name);
symbol.name_ = this->stream_->get_string(offset_name);
} catch (const LIEF::read_out_of_bound&) { // Corrupted
LOG(WARNING) << "Symbol name is corrupted";
}
@ -577,7 +581,7 @@ void Parser::build_exports(void) {
uint32_t name_offset = this->binary_->rva_to_offset(export_directory_table->NameRVA);
try {
export_object.name_ = this->stream_->read_string(name_offset);
export_object.name_ = this->stream_->get_string(name_offset);
} catch (const LIEF::read_out_of_bound& e) {
LOG(WARNING) << e.what();
}
@ -617,7 +621,7 @@ void Parser::build_exports(void) {
ExportEntry entry;
try {
entry.name_ = this->stream_->read_string(name_offset);
entry.name_ = this->stream_->get_string(name_offset);
} catch (const LIEF::read_out_of_bound& e) {
LOG(WARNING) << e.what();
}

View File

@ -271,7 +271,7 @@ void Parser::build_import_table(void) {
}
// Offset to the Import (Library) name
const uint64_t offsetName = this->binary_->rva_to_offset(import.name_RVA_);
import.name_ = this->stream_->read_string(offsetName);
import.name_ = this->stream_->get_string(offsetName);
// We assume that a DLL name should be at least 4 length size and "printable
@ -329,7 +329,7 @@ void Parser::build_import_table(void) {
if(not entry.is_ordinal()) {
entry.name_ = this->stream_->read_string(
entry.name_ = this->stream_->get_string(
this->binary_->rva_to_offset(entry.hint_name_rva()) + sizeof(uint16_t));
entry.hint_ = *reinterpret_cast<const uint16_t*>(
@ -368,19 +368,26 @@ void Parser::build_tls(void) {
const uint64_t imagebase = this->binary_->optional_header().imagebase();
try {
const uint64_t startDataRVA = tls_header->RawDataStartVA - imagebase;
const uint64_t stopDataRVA = tls_header->RawDataEndVA - imagebase;
const uint__ offsetStartTemplate = this->binary_->rva_to_offset(startDataRVA);
const uint__ offsetEndTemplate = this->binary_->rva_to_offset(stopDataRVA);
const uint64_t start_data_rva = tls_header->RawDataStartVA - imagebase;
const uint64_t stop_data_rva = tls_header->RawDataEndVA - imagebase;
const uint8_t* template_ptr = reinterpret_cast<const uint8_t*>(
this->stream_->read(offsetStartTemplate, offsetEndTemplate - offsetStartTemplate));
std::vector<uint8_t> templateData = {
template_ptr,
template_ptr + offsetEndTemplate - offsetStartTemplate
};
tls.data_template(templateData);
const uint__ start_template_offset = this->binary_->rva_to_offset(start_data_rva);
const uint__ end_template_offset = this->binary_->rva_to_offset(stop_data_rva);
const size_t size_to_read = end_template_offset - start_template_offset;
try {
if (size_to_read > Parser::MAX_DATA_SIZE) {
LOG(WARNING) << "TLS's template is too large!";
} else {
const uint8_t* template_ptr = reinterpret_cast<const uint8_t*>(
this->stream_->read(start_template_offset, size_to_read));
std::vector<uint8_t> template_data = {
template_ptr,
template_ptr + size_to_read
};
tls.data_template(std::move(template_data));
}
} catch (const read_out_of_bound&) {
throw corrupted("TLS corrupted (data template)");
@ -388,13 +395,15 @@ void Parser::build_tls(void) {
throw corrupted("TLS corrupted (data template)");
}
const uint64_t offsetToCallbacks = this->binary_->rva_to_offset(tls.addressof_callbacks() - imagebase);
const uint__ *rvaCallbacksAddress = reinterpret_cast<const uint__*>(
this->stream_->read(offsetToCallbacks, sizeof(uint__)));
uint64_t callbacks_offset = this->binary_->rva_to_offset(tls.addressof_callbacks() - imagebase);
uint__ callback_rva = this->stream_->read_integer<uint__>(callbacks_offset);
callbacks_offset += sizeof(uint__);
while (*rvaCallbacksAddress != 0) {
tls.callbacks_.push_back(static_cast<uint64_t>(*rvaCallbacksAddress));
rvaCallbacksAddress++;
size_t count = 0;
while (callback_rva > 0 and count < Parser::MAX_TLS_CALLBACKS) {
tls.callbacks_.push_back(static_cast<uint64_t>(callback_rva));
callback_rva = this->stream_->read_integer<uint__>(callbacks_offset);
callbacks_offset += sizeof(uint__);
}
tls.directory_ = &(this->binary_->data_directory(DATA_DIRECTORY::TLS_TABLE));

View File

@ -1,3 +1,4 @@
include(ProcessorCount)
find_package(Git REQUIRED)
enable_testing()
@ -329,6 +330,11 @@ add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/macho")
# /!\ Bad Hack /!\
add_dependencies(LIB_LIEF_STATIC lief_samples)
add_dependencies(LIB_LIEF_SHARED lief_samples)
ProcessorCount(N)
if(N EQUAL 0)
set(N 1)
endif()
add_custom_target(check-lief COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
add_custom_target(check-lief COMMAND ${CMAKE_CTEST_COMMAND} --parallel 1 --output-on-failure
DEPENDS lief_samples)

View File

@ -1,4 +1,4 @@
@echo off
@echo on
set PYTHONPATH=%PYTHONPATH%;@PROJECT_BINARY_DIR@/api/python