Skip to content

Commit

Permalink
Add support for Mach-O overlay
Browse files Browse the repository at this point in the history
Resolve: #1028
  • Loading branch information
romainthomas committed May 4, 2024
1 parent 97d96da commit 7faf45f
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 30 deletions.
2 changes: 2 additions & 0 deletions api/python/lief/MachO.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ class Binary(lief.Binary):
@property
def off_ranges(self) -> lief.MachO.Binary.range_t: ...
@property
def overlay(self) -> memoryview: ...
@property
def page_size(self) -> int: ...
@property
def relocations(self) -> lief.MachO.Binary.it_relocations: ... # type: ignore
Expand Down
7 changes: 7 additions & 0 deletions api/python/src/MachO/objects/pyBinary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <sstream>
#include <nanobind/stl/vector.h>
#include <nanobind/stl/string.h>
#include "nanobind/extra/memoryview.hpp"

#include "LIEF/MachO/Binary.hpp"
#include "LIEF/MachO/BuildVersion.hpp"
Expand Down Expand Up @@ -663,6 +664,12 @@ void create<Binary>(nb::module_& m) {
.def("__contains__",
nb::overload_cast<LOAD_COMMAND_TYPES>(&Binary::has, nb::const_))

.def_prop_ro("overlay",
[] (const Binary& self) {
const span<const uint8_t> overlay = self.overlay();
return nb::memoryview::from_memory(overlay.data(), overlay.size());
})

LIEF_DEFAULT_STR(Binary);
}
}
Expand Down
5 changes: 5 additions & 0 deletions include/LIEF/MachO/Binary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,10 @@ class LIEF_API Binary : public LIEF::Binary {
return bin->format() == Binary::FORMATS::MACHO;
}

span<const uint8_t> overlay() const {
return overlay_;
}

private:
//! Default constructor
Binary();
Expand Down Expand Up @@ -756,6 +760,7 @@ class LIEF_API Binary : public LIEF::Binary {
uint64_t fileset_offset_ = 0;
uint64_t in_memory_base_addr_ = 0;
std::string fileset_name_;
std::vector<uint8_t> overlay_;
};

} // namespace MachO
Expand Down
2 changes: 2 additions & 0 deletions include/LIEF/MachO/BinaryParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ class LIEF_API BinaryParser : public LIEF::Parser {
template<class MACHO_T>
ok_error_t post_process(CodeSignatureDir& cmd);

ok_error_t parse_overlay();

// Exports
// -------
ok_error_t parse_dyldinfo_export();
Expand Down
1 change: 1 addition & 0 deletions include/LIEF/MachO/ParserConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct LIEF_API ParserConfig {
bool parse_dyld_exports = true; ///< Parse the Dyld export trie
bool parse_dyld_bindings = true; ///< Parse the Dyld binding opcodes
bool parse_dyld_rebases = true; ///< Parse the Dyld rebase opcodes
bool parse_overlay = true; ///< Whether the overlay data should be parsed

/// When parsing Mach-O from memory, this option
/// can be used to *undo* relocations and symbols bindings.
Expand Down
50 changes: 21 additions & 29 deletions src/MachO/Binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1951,43 +1951,35 @@ bool Binary::is_valid_addr(uint64_t address) const {


Binary::range_t Binary::va_ranges() const {
const auto it_min = std::min_element(
std::begin(segments_), std::end(segments_),
[] (const SegmentCommand* lhs, const SegmentCommand* rhs) {
if (lhs->virtual_address() == 0 || rhs->virtual_address() == 0) {
return true;
}
return lhs->virtual_address() < rhs->virtual_address();
});
uint64_t min = uint64_t(-1);
uint64_t max = 0;

const auto it_max = std::min_element(
std::begin(segments_), std::end(segments_),
[] (const SegmentCommand* lhs, const SegmentCommand* rhs) {
return (lhs->virtual_address() + lhs->virtual_size()) > (rhs->virtual_address() + rhs->virtual_size());
});
for (const SegmentCommand* segment : segments_) {
min = std::min<uint64_t>(min, segment->virtual_address());
max = std::max(max, segment->virtual_address() + segment->virtual_size());
}

if (min == uint64_t(-1)) {
return {0, 0};
}

return {(*it_min)->virtual_address(), (*it_max)->virtual_address() + (*it_max)->virtual_size()};
return {min, max};
}

Binary::range_t Binary::off_ranges() const {
uint64_t min = uint64_t(-1);
uint64_t max = 0;

const auto it_min = std::min_element(
std::begin(segments_), std::end(segments_),
[] (const SegmentCommand* lhs, const SegmentCommand* rhs) {
if (lhs->file_offset() == 0 || rhs->file_offset() == 0) {
return true;
}
return lhs->file_offset() < rhs->file_offset();
});

for (const SegmentCommand* segment : segments_) {
min = std::min<uint64_t>(min, segment->file_offset());
max = std::max(max, segment->file_offset() + segment->file_size());
}

const auto it_max = std::min_element(
std::begin(segments_), std::end(segments_),
[] (const SegmentCommand* lhs, const SegmentCommand* rhs) {
return (lhs->file_offset() + lhs->file_size()) > (rhs->file_offset() + rhs->file_size());
});
if (min == uint64_t(-1)) {
return {0, 0};
}

return {(*it_min)->file_offset(), (*it_max)->file_offset() + (*it_max)->file_size()};
return {min, max};
}


Expand Down
17 changes: 16 additions & 1 deletion src/MachO/BinaryParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,6 @@ ok_error_t BinaryParser::parse_dyld_exports() {
parse_export_trie(exports->export_info_, offset, end_offset, "",
&invalid_names);
return ok();

}

ok_error_t BinaryParser::parse_dyldinfo_export() {
Expand Down Expand Up @@ -390,5 +389,21 @@ ok_error_t BinaryParser::parse_dyldinfo_export() {
}


ok_error_t BinaryParser::parse_overlay() {
const uint64_t last_offset = binary_->off_ranges().end;
if (last_offset >= stream_->size()) {
return ok();
}

const uint64_t overlay_size = stream_->size() - last_offset;
LIEF_INFO("Overlay detected at 0x{:x} ({} bytes)", last_offset, overlay_size);
if (!stream_->peek_data(binary_->overlay_, last_offset, overlay_size)) {
LIEF_WARN("Can't read overlay data");
return make_error_code(lief_errors::read_error);
}
return ok();
}


} // namespace MachO
} // namespace LIEF
4 changes: 4 additions & 0 deletions src/MachO/BinaryParser.tcc
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ ok_error_t BinaryParser::parse() {
if (LinkerOptHint* opt = binary_->linker_opt_hint()) {
post_process<MACHO_T>(*opt);
}

if (config_.parse_overlay) {
parse_overlay();
}
return ok();
}

Expand Down
4 changes: 4 additions & 0 deletions tests/macho/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,7 @@ def test_twolevel_hints():
assert hashlib.sha256(tw_hints.data).hexdigest() == "e44cef3a83eb89954557a9ad2a36ebf4794ce0385da5a39381fdadc3e6037beb"
assert tw_hints.command_offset == 1552
print(lief.to_json(tw_hints))

def test_overlay():
sample = lief.MachO.parse(get_sample("MachO/overlay_data.bin")).at(0)
assert bytes(sample.overlay) == b'\x00overlay data'

0 comments on commit 7faf45f

Please sign in to comment.