From f999e4934574f25d67abfda080d73a69daf2deb4 Mon Sep 17 00:00:00 2001 From: Romain Thomas Date: Sat, 26 Jul 2025 11:26:14 +0200 Subject: [PATCH] Bootstrap new MachO segmented chained fixups --- api/python/lief/MachO/__init__.pyi | 52 +++++++++++- api/python/src/MachO/enums.cpp | 3 +- .../objects/pyChainedPointerAnalysis.cpp | 82 +++++++++++++++++++ include/LIEF/MachO/BinaryParser.hpp | 6 ++ include/LIEF/MachO/ChainedPointerAnalysis.hpp | 49 ++++++++++- include/LIEF/MachO/DyldChainedFormat.hpp | 1 + include/LIEF/MachO/RelocationFixup.hpp | 8 ++ src/MachO/BinaryParser.tcc | 48 +++++++++++ src/MachO/Builder.tcc | 14 ++++ src/MachO/ChainedFixup.cpp | 3 + src/MachO/ChainedFixup.hpp | 40 +++++++-- src/MachO/ChainedPointerAnalysis.cpp | 64 ++++++++++++++- src/MachO/DyldChainedFormat.cpp | 1 + src/MachO/RelocationFixup.cpp | 49 ++++++++++- 14 files changed, 405 insertions(+), 15 deletions(-) diff --git a/api/python/lief/MachO/__init__.pyi b/api/python/lief/MachO/__init__.pyi index 3e0d8a45..c724e742 100644 --- a/api/python/lief/MachO/__init__.pyi +++ b/api/python/lief/MachO/__init__.pyi @@ -244,6 +244,8 @@ class DYLD_CHAINED_PTR_FORMAT(enum.Enum): PTR_ARM64E_SHARED_CACHE = 13 + PTR_ARM64E_SEGMENTED = 14 + class ParserConfig: def __init__(self) -> None: ... @@ -517,6 +519,48 @@ class ChainedPointerAnalysis: def __str__(self) -> str: ... + class dyld_chained_ptr_arm64e_segmented_rebase_t: + @property + def target_seg_offset(self) -> int: ... + + @property + def target_seg_index(self) -> int: ... + + @property + def padding(self) -> int: ... + + @property + def next(self) -> int: ... + + @property + def auth(self) -> bool: ... + + def __str__(self) -> str: ... + + class dyld_chained_ptr_arm64e_auth_segmented_rebase_t: + @property + def target_seg_offset(self) -> int: ... + + @property + def target_seg_index(self) -> int: ... + + @property + def diversity(self) -> int: ... + + @property + def addr_div(self) -> int: ... + + @property + def key(self) -> int: ... + + @property + def next(self) -> int: ... + + @property + def auth(self) -> bool: ... + + def __str__(self) -> str: ... + @staticmethod def stride(fmt: DYLD_CHAINED_PTR_FORMAT) -> int: ... @@ -568,7 +612,13 @@ class ChainedPointerAnalysis: @property def dyld_chained_ptr_32_firmware_rebase(self) -> ChainedPointerAnalysis.dyld_chained_ptr_32_firmware_rebase_t: ... - def get_as(self, arg: DYLD_CHAINED_PTR_FORMAT, /) -> Union[lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_bind24_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_bind24_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_kernel_cache_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_cache_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_firmware_rebase_t, int, None]: ... + @property + def dyld_chained_ptr_arm64e_segmented_rebase(self) -> ChainedPointerAnalysis.dyld_chained_ptr_arm64e_segmented_rebase_t: ... + + @property + def dyld_chained_ptr_arm64e_auth_segmented_rebase(self) -> ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_segmented_rebase_t: ... + + def get_as(self, arg: DYLD_CHAINED_PTR_FORMAT, /) -> Union[lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_bind24_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_bind24_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_kernel_cache_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_bind_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_cache_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_firmware_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_segmented_rebase_t, lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_segmented_rebase_t, int, None]: ... class FatBinary: class it_binaries: diff --git a/api/python/src/MachO/enums.cpp b/api/python/src/MachO/enums.cpp index fdd92a2f..ca8af382 100644 --- a/api/python/src/MachO/enums.cpp +++ b/api/python/src/MachO/enums.cpp @@ -117,7 +117,8 @@ void init_enums(nb::module_& m) { .value(PY_ENUM(DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_FIRMWARE)) .value(PY_ENUM(DYLD_CHAINED_PTR_FORMAT::PTR_X86_64_KERNEL_CACHE)) .value(PY_ENUM(DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24)) - .value(PY_ENUM(DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SHARED_CACHE)); + .value(PY_ENUM(DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SHARED_CACHE)) + .value(PY_ENUM(DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED)); } } diff --git a/api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp b/api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp index 9bb3ddd2..d573d759 100644 --- a/api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp +++ b/api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp @@ -45,6 +45,8 @@ struct ChainedPointer : public nanobind::object { "lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_bind_t, " "lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_cache_rebase_t, " "lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_firmware_rebase_t, " + "lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_segmented_rebase_t, " + "lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_segmented_rebase_t, " "int, " "None" "]", @@ -471,6 +473,76 @@ void create(nb::module_& m) { ) LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_32_firmware_rebase_t); ; + nb::class_( + clazz, "dyld_chained_ptr_arm64e_segmented_rebase_t" + ) + .def_prop_ro("target_seg_offset", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t& ptr) { + return (uint32_t)ptr.target_seg_offset; + } + ) + .def_prop_ro("target_seg_index", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t& ptr) { + return (uint32_t)ptr.target_seg_index; + } + ) + .def_prop_ro("padding", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t& ptr) { + return (uint32_t)ptr.padding; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t& ptr) { + return (uint32_t)ptr.next; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t& ptr) { + return (bool)ptr.auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_arm64e_auth_segmented_rebase_t" + ) + .def_prop_ro("target_seg_offset", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& ptr) { + return (uint32_t)ptr.target_seg_offset; + } + ) + .def_prop_ro("target_seg_index", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& ptr) { + return (uint32_t)ptr.target_seg_index; + } + ) + .def_prop_ro("diversity", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& ptr) { + return (uint32_t)ptr.diversity; + } + ) + .def_prop_ro("addr_div", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& ptr) { + return (uint32_t)ptr.addr_div; + } + ) + .def_prop_ro("key", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& ptr) { + return (uint32_t)ptr.key; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& ptr) { + return (uint32_t)ptr.next; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& ptr) { + return (bool)ptr.auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t); + ; clazz .def_static("stride", &ChainedPointerAnalysis::stride, "fmt"_a) @@ -506,6 +578,10 @@ void create(nb::module_& m) { &ChainedPointerAnalysis::dyld_chained_ptr_32_cache_rebase) .def_prop_ro("dyld_chained_ptr_32_firmware_rebase", &ChainedPointerAnalysis::dyld_chained_ptr_32_firmware_rebase) + .def_prop_ro("dyld_chained_ptr_arm64e_segmented_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase) + .def_prop_ro("dyld_chained_ptr_arm64e_auth_segmented_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase) .def("get_as", [] (const ChainedPointerAnalysis& self, DYLD_CHAINED_PTR_FORMAT fmt) -> ChainedPointer { ChainedPointerAnalysis::union_pointer_t ptr = self.get_as(fmt); @@ -551,6 +627,12 @@ void create(nb::module_& m) { case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: return nb::cast(ptr.ptr_32_firmware_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE: + return nb::cast(ptr.ptr_arm64e_segmented_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE: + return nb::cast(ptr.ptr_arm64e_auth_segmented_rebase); } return nb::none(); } diff --git a/include/LIEF/MachO/BinaryParser.hpp b/include/LIEF/MachO/BinaryParser.hpp index 95060370..223f090e 100644 --- a/include/LIEF/MachO/BinaryParser.hpp +++ b/include/LIEF/MachO/BinaryParser.hpp @@ -65,6 +65,7 @@ struct dyld_chained_fixups_header; union dyld_chained_ptr_arm64e; union dyld_chained_ptr_generic64; union dyld_chained_ptr_generic32; +union dyld_chained_ptr_arm64e_segmented; } /// Class used to parse a **single** binary (i.e. non-FAT) @@ -224,6 +225,11 @@ class LIEF_API BinaryParser : public LIEF::Parser { const details::dyld_chained_starts_in_segment& seg_info, const details::dyld_chained_ptr_generic32 & fixup); + LIEF_LOCAL ok_error_t do_chained_fixup( + SegmentCommand& segment, uint64_t chain_address, uint32_t chain_offset, + const details::dyld_chained_starts_in_segment& seg_info, + const details::dyld_chained_ptr_arm64e_segmented& fixup); + template LIEF_LOCAL ok_error_t post_process(SymbolCommand& cmd); diff --git a/include/LIEF/MachO/ChainedPointerAnalysis.hpp b/include/LIEF/MachO/ChainedPointerAnalysis.hpp index 29713d27..ffb688af 100644 --- a/include/LIEF/MachO/ChainedPointerAnalysis.hpp +++ b/include/LIEF/MachO/ChainedPointerAnalysis.hpp @@ -209,6 +209,34 @@ class LIEF_API ChainedPointerAnalysis { std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_32_firmware_rebase_t& chain); }; + // DYLD_CHAINED_PTR_ARM64E_SEGMENTED + struct dyld_chained_ptr_arm64e_segmented_rebase_t + { + uint32_t target_seg_offset : 28, + target_seg_index : 4; + uint32_t padding : 19, + next : 12, + auth : 1; + + friend LIEF_API + std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_segmented_rebase_t& chain); + }; + + // DYLD_CHAINED_PTR_ARM64E_SEGMENTED + struct dyld_chained_ptr_arm64e_auth_segmented_rebase_t + { + uint32_t target_seg_offset : 28, + target_seg_index : 4; + uint32_t diversity : 16, + addr_div : 1, + key : 2, + next : 12, + auth : 1; + friend LIEF_API + std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_auth_segmented_rebase_t& chain); + }; + + enum class PTR_TYPE : uint64_t { UNKNOWN = 0, DYLD_CHAINED_PTR_ARM64E_REBASE, @@ -223,7 +251,9 @@ class LIEF_API ChainedPointerAnalysis { DYLD_CHAINED_PTR_32_REBASE, DYLD_CHAINED_PTR_32_BIND, DYLD_CHAINED_PTR_32_CACHE_REBASE, - DYLD_CHAINED_PTR_32_FIRMWARE_REBASE + DYLD_CHAINED_PTR_32_FIRMWARE_REBASE, + DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE, + DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE, }; static std::unique_ptr from_value(uint64_t value, @@ -251,6 +281,7 @@ class LIEF_API ChainedPointerAnalysis { case DYLD_CHAINED_PTR_FORMAT::PTR_32: case DYLD_CHAINED_PTR_FORMAT::PTR_32_CACHE: case DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED: return 4; case DYLD_CHAINED_PTR_FORMAT::PTR_X86_64_KERNEL_CACHE: @@ -273,6 +304,7 @@ class LIEF_API ChainedPointerAnalysis { case DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE: case DYLD_CHAINED_PTR_FORMAT::PTR_X86_64_KERNEL_CACHE: case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SHARED_CACHE: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED: return sizeof(uint64_t); case DYLD_CHAINED_PTR_FORMAT::PTR_32_FIRMWARE: @@ -382,6 +414,18 @@ class LIEF_API ChainedPointerAnalysis { return result; } + const dyld_chained_ptr_arm64e_segmented_rebase_t dyld_chained_ptr_arm64e_segmented_rebase() const { + dyld_chained_ptr_arm64e_segmented_rebase_t result; + std::memcpy(&result, &value_, sizeof(result)); + return result; + } + + const dyld_chained_ptr_arm64e_auth_segmented_rebase_t dyld_chained_ptr_arm64e_auth_segmented_rebase() const { + dyld_chained_ptr_arm64e_auth_segmented_rebase_t result; + std::memcpy(&result, &value_, sizeof(result)); + return result; + } + struct union_pointer_t { PTR_TYPE type = PTR_TYPE::UNKNOWN; union { @@ -398,6 +442,9 @@ class LIEF_API ChainedPointerAnalysis { dyld_chained_ptr_32_bind_t ptr_32_bind; dyld_chained_ptr_32_cache_rebase_t ptr_32_cache_rebase; dyld_chained_ptr_32_firmware_rebase_t ptr_32_firmware_rebase; + + dyld_chained_ptr_arm64e_segmented_rebase_t ptr_arm64e_segmented_rebase; + dyld_chained_ptr_arm64e_auth_segmented_rebase_t ptr_arm64e_auth_segmented_rebase; uint64_t raw; }; uint32_t next() const; diff --git a/include/LIEF/MachO/DyldChainedFormat.hpp b/include/LIEF/MachO/DyldChainedFormat.hpp index c0c4973b..603d28de 100644 --- a/include/LIEF/MachO/DyldChainedFormat.hpp +++ b/include/LIEF/MachO/DyldChainedFormat.hpp @@ -42,6 +42,7 @@ enum class DYLD_CHAINED_PTR_FORMAT { PTR_X86_64_KERNEL_CACHE = 11, // stride 1, x86_64 kernel caches PTR_ARM64E_USERLAND24 = 12, // stride 8, unauth target is vm offset, 24-bit bind PTR_ARM64E_SHARED_CACHE = 13, // stride 8, regular/auth targets both vm offsets. Only A keys supported + PTR_ARM64E_SEGMENTED = 14, // stride 4, rebase offsets use segIndex and segOffset }; LIEF_API const char* to_string(DYLD_CHAINED_FORMAT fmt); diff --git a/include/LIEF/MachO/RelocationFixup.hpp b/include/LIEF/MachO/RelocationFixup.hpp index 1eb6d215..6a7a07c0 100644 --- a/include/LIEF/MachO/RelocationFixup.hpp +++ b/include/LIEF/MachO/RelocationFixup.hpp @@ -31,6 +31,8 @@ struct dyld_chained_ptr_arm64e_rebase; struct dyld_chained_ptr_arm64e_auth_rebase; struct dyld_chained_ptr_64_rebase; struct dyld_chained_ptr_32_rebase; +struct dyld_chained_ptr_arm64e_segmented_rebase; +struct dyld_chained_ptr_arm64e_auth_segmented_rebase; } class BinaryParser; @@ -133,12 +135,16 @@ class LIEF_API RelocationFixup : public Relocation { ARM64E_AUTH_REBASE, PTR64_REBASE, PTR32_REBASE, + SEGMENTED, + AUTH_SEGMENTED, }; LIEF_LOCAL void set(const details::dyld_chained_ptr_arm64e_rebase& fixup); LIEF_LOCAL void set(const details::dyld_chained_ptr_arm64e_auth_rebase& fixup); LIEF_LOCAL void set(const details::dyld_chained_ptr_64_rebase& fixup); LIEF_LOCAL void set(const details::dyld_chained_ptr_32_rebase& fixup); + LIEF_LOCAL void set(const details::dyld_chained_ptr_arm64e_segmented_rebase& fixup); + LIEF_LOCAL void set(const details::dyld_chained_ptr_arm64e_auth_segmented_rebase& fixup); DYLD_CHAINED_PTR_FORMAT ptr_fmt_ = DYLD_CHAINED_PTR_FORMAT::PTR_32; uint64_t imagebase_ = 0; @@ -151,6 +157,8 @@ class LIEF_API RelocationFixup : public Relocation { details::dyld_chained_ptr_arm64e_auth_rebase* arm64_auth_rebase_; details::dyld_chained_ptr_64_rebase* p64_rebase_; details::dyld_chained_ptr_32_rebase* p32_rebase_; + details::dyld_chained_ptr_arm64e_segmented_rebase* segmented_rebase_; + details::dyld_chained_ptr_arm64e_auth_segmented_rebase* auth_segmented_rebase_; }; }; diff --git a/src/MachO/BinaryParser.tcc b/src/MachO/BinaryParser.tcc index 2b7ebb52..124702f7 100644 --- a/src/MachO/BinaryParser.tcc +++ b/src/MachO/BinaryParser.tcc @@ -2956,6 +2956,27 @@ result BinaryParser::next_chain(uint64_t& chain_address, uint64_t chai chain_address += delta; return chain.next * stride; } + + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED: + { + details::dyld_chained_ptr_arm64e_segmented_rebase chain; + + if (auto res = stream_->peek(chain_offset)) { + chain = *res; + } else { + LIEF_ERR("Can't read the dyld chain at 0x{:x}", chain_offset); + return make_error_code(res.error()); + } + + if (chain.next == 0) { + return CHAIN_END; + } + + int32_t delta = chain.next * stride - chain_offset; + chain_address += delta; + return chain.next * stride; + } + default: { LIEF_ERR("Unknown pointer format: 0x{:04x}", seg_info.pointer_format); @@ -3036,6 +3057,24 @@ ok_error_t BinaryParser::process_fixup(SegmentCommand& segment, "the attached binary", to_string(ptr_fmt)); return make_error_code(lief_errors::not_implemented); } + case LIEF::MachO::DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED: + { + details::dyld_chained_ptr_arm64e_segmented fixup; + if (auto res = stream_->peek(chain_offset)) { + fixup = *res; + } else { + LIEF_ERR("Can't read the dyld chain at 0x{:x}", chain_offset); + return make_error_code(res.error()); + } + + auto is_ok = do_chained_fixup(segment, chain_address, chain_offset, seg_info, fixup); + if (!is_ok) { + LIEF_WARN("Can't process the fixup {} - 0x{:x}", segment.name(), chain_offset); + return make_error_code(is_ok.error()); + } + return ok(); + } + default: { LIEF_ERR("Unknown pointer format: 0x{:04x}", seg_info.pointer_format); @@ -3366,6 +3405,15 @@ ok_error_t BinaryParser::do_chained_fixup(SegmentCommand& segment, return ok(); } +ok_error_t BinaryParser::do_chained_fixup( + SegmentCommand& /*segment*/, uint64_t /*chain_address*/, uint32_t /*chain_offset*/, + const details::dyld_chained_starts_in_segment& /*seg_info*/, + const details::dyld_chained_ptr_arm64e_segmented& /*fixup*/) +{ + LIEF_ERR("Segmented chained rebase is not supported"); + return make_error_code(lief_errors::not_supported); +} + template ok_error_t BinaryParser::post_process(SymbolCommand& cmd) { diff --git a/src/MachO/Builder.tcc b/src/MachO/Builder.tcc index 9ee3c581..00b911d6 100644 --- a/src/MachO/Builder.tcc +++ b/src/MachO/Builder.tcc @@ -1245,6 +1245,20 @@ ok_error_t Builder::update_fixups(DyldChainedFixups& command) { break; } + case RelocationFixup::REBASE_TYPES::SEGMENTED: + { + [[maybe_unused]] auto& raw_fixup = *reinterpret_cast(sdata.data() + rel_offset); + LIEF_ERR("dyld_chained_ptr_arm64e_segmented_rebase is not supported ({})", __LINE__); + break; + } + + case RelocationFixup::REBASE_TYPES::AUTH_SEGMENTED: + { + [[maybe_unused]] auto& raw_fixup = *reinterpret_cast(sdata.data() + rel_offset); + LIEF_ERR("dyld_chained_ptr_arm64e_auth_segmented_rebase is not supported ({})", __LINE__); + break; + } + case RelocationFixup::REBASE_TYPES::UNKNOWN: { break; diff --git a/src/MachO/ChainedFixup.cpp b/src/MachO/ChainedFixup.cpp index 6b5b42ad..ee57b260 100644 --- a/src/MachO/ChainedFixup.cpp +++ b/src/MachO/ChainedFixup.cpp @@ -111,6 +111,9 @@ bool chained_fixup::is_rebase(uint16_t ptr_format) const { { return true; } + case LIEF::MachO::DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED: + return true; + default: { LIEF_ERR("Unknown pointer format: 0x{:04x}", ptr_format); diff --git a/src/MachO/ChainedFixup.hpp b/src/MachO/ChainedFixup.hpp index 05836c06..c061f666 100644 --- a/src/MachO/ChainedFixup.hpp +++ b/src/MachO/ChainedFixup.hpp @@ -203,6 +203,28 @@ struct dyld_chained_ptr_32_firmware_rebase next : 6; // 4-byte stride }; +// DYLD_CHAINED_PTR_ARM64E_SEGMENTED +struct dyld_chained_ptr_arm64e_segmented_rebase +{ + uint32_t target_seg_offset : 28, // offset in segment + target_seg_index : 4; // index into segment address table + uint32_t padding : 19, + next : 12, // 4-byte stide + auth : 1; // == 0 +}; + +// DYLD_CHAINED_PTR_ARM64E_SEGMENTED +struct dyld_chained_ptr_arm64e_auth_segmented_rebase +{ + uint32_t target_seg_offset : 28, // offset in segment + target_seg_index : 4; // index into segment address table + uint32_t diversity : 16, + addr_div : 1, + key : 2, + next : 12, // 4-byte stide + auth : 1; // == 1 +}; + struct dyld_chained_import { uint32_t lib_ordinal : 8, weak_import : 1, @@ -239,17 +261,23 @@ uint64_t sign_extended_addend(const dyld_chained_ptr_arm64e_bind24& bind); uint64_t sign_extended_addend(const dyld_chained_ptr_64_bind& bind); union dyld_chained_ptr_arm64e { - dyld_chained_ptr_arm64e_auth_rebase auth_rebase; - dyld_chained_ptr_arm64e_auth_bind auth_bind; - dyld_chained_ptr_arm64e_rebase rebase; - dyld_chained_ptr_arm64e_bind bind; - dyld_chained_ptr_arm64e_bind24 bind24; - dyld_chained_ptr_arm64e_auth_bind24 auth_bind24; + dyld_chained_ptr_arm64e_auth_rebase auth_rebase; + dyld_chained_ptr_arm64e_auth_bind auth_bind; + dyld_chained_ptr_arm64e_rebase rebase; + dyld_chained_ptr_arm64e_bind bind; + dyld_chained_ptr_arm64e_bind24 bind24; + dyld_chained_ptr_arm64e_auth_bind24 auth_bind24; + uint64_t sign_extended_addend() const; uint64_t unpack_target() const; void pack_target(uint64_t value); }; +union dyld_chained_ptr_arm64e_segmented { + dyld_chained_ptr_arm64e_segmented_rebase seg_rebase; + dyld_chained_ptr_arm64e_auth_segmented_rebase auth_seg_rebase; +}; + union dyld_chained_ptr_generic64 { dyld_chained_ptr_64_rebase rebase; dyld_chained_ptr_64_bind bind; diff --git a/src/MachO/ChainedPointerAnalysis.cpp b/src/MachO/ChainedPointerAnalysis.cpp index c041cd8d..2f12e080 100644 --- a/src/MachO/ChainedPointerAnalysis.cpp +++ b/src/MachO/ChainedPointerAnalysis.cpp @@ -129,7 +129,7 @@ std::ostream& operator<<(std::ostream& os, std::ostream& operator<<(std::ostream& os, const ChainedPointerAnalysis::dyld_chained_ptr_32_cache_rebase_t& chain) { - os << fmt::format("target: 0x{:06x}, next: 0x{:x}}", + os << fmt::format("target: 0x{:06x}, next: 0x{:x}", chain.target, chain.next); return os; } @@ -137,11 +137,29 @@ std::ostream& operator<<(std::ostream& os, std::ostream& operator<<(std::ostream& os, const ChainedPointerAnalysis::dyld_chained_ptr_32_firmware_rebase_t& chain) { - os << fmt::format("target: 0x{:06x}, next: 0x{:x}}", + os << fmt::format("target: 0x{:06x}, next: 0x{:x}", chain.target, chain.next); return os; } +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t& chain) +{ + os << fmt::format("segment offset: 0x{:06x}, segment index: {}, next: 0x{:x}", + chain.target_seg_offset, chain.target_seg_index, chain.next); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t& chain) +{ + os << fmt::format("segment offset: 0x{:06x}, segment index: {}, next: 0x{:x}, " + "addr_div: {} key: {} diversity: {}, auth: {}", + chain.target_seg_offset, chain.target_seg_index, chain.next, + chain.addr_div, chain.key, chain.diversity, chain.auth); + return os; +} + template ChainedPointerAnalysis::union_pointer_t create_impl(const T& value) { ChainedPointerAnalysis::union_pointer_t out; @@ -239,6 +257,20 @@ ChainedPointerAnalysis::union_pointer_t create_impl(const T& value) { out.ptr_32_firmware_rebase = value; } + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE; + out.ptr_arm64e_segmented_rebase = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE; + out.ptr_arm64e_auth_segmented_rebase = value; + } + return out; } @@ -308,6 +340,14 @@ ChainedPointerAnalysis::union_pointer_t ChainedPointerAnalysis::get_as(DYLD_CHAI case DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE: return create_impl(dyld_chained_ptr_64_kernel_cache_rebase()); + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED: + { + if (dyld_chained_ptr_arm64e_segmented_rebase().auth) { + return create_impl(dyld_chained_ptr_arm64e_auth_segmented_rebase()); + } + return create_impl(dyld_chained_ptr_arm64e_segmented_rebase()); + } + default: return {}; } @@ -342,6 +382,10 @@ uint32_t ChainedPointerAnalysis::union_pointer_t::next() const { return ptr_32_cache_rebase.next; case PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: return ptr_32_firmware_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE: + return ptr_arm64e_segmented_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE: + return ptr_arm64e_auth_segmented_rebase.next; case PTR_TYPE::UNKNOWN: return 0; } @@ -369,6 +413,8 @@ result ChainedPointerAnalysis::union_pointer_t::ordinal() const { case PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE: case PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: case PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE: + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE: + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE: case PTR_TYPE::UNKNOWN: return make_error_code(lief_errors::not_found); } @@ -389,6 +435,10 @@ bool ChainedPointerAnalysis::union_pointer_t::is_auth() const { return arm64e_bind24.auth; case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: return arm64e_auth_bind24.auth; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE: + return ptr_arm64e_segmented_rebase.auth; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE: + return ptr_arm64e_auth_segmented_rebase.auth; case PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE: case PTR_TYPE::DYLD_CHAINED_PTR_64_BIND: case PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE: @@ -418,6 +468,10 @@ result ChainedPointerAnalysis::union_pointer_t::target() const { return ptr_32_cache_rebase.target; case PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: return ptr_32_firmware_rebase.target; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE: + return ptr_arm64e_segmented_rebase.target_seg_offset; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE: + return ptr_arm64e_auth_segmented_rebase.target_seg_offset; case PTR_TYPE::DYLD_CHAINED_PTR_32_BIND: case PTR_TYPE::DYLD_CHAINED_PTR_64_BIND: case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: @@ -473,6 +527,12 @@ std::ostream& operator<<(std::ostream& os, case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: os << ptr.ptr_32_firmware_rebase; break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE: + os << ptr.ptr_arm64e_segmented_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE: + os << ptr.ptr_arm64e_auth_segmented_rebase; + break; case ChainedPointerAnalysis::PTR_TYPE::UNKNOWN: break; } diff --git a/src/MachO/DyldChainedFormat.cpp b/src/MachO/DyldChainedFormat.cpp index 32d8a297..b27bbc36 100644 --- a/src/MachO/DyldChainedFormat.cpp +++ b/src/MachO/DyldChainedFormat.cpp @@ -51,6 +51,7 @@ const char* to_string(DYLD_CHAINED_PTR_FORMAT e) { ENTRY(PTR_X86_64_KERNEL_CACHE), ENTRY(PTR_ARM64E_USERLAND24), ENTRY(PTR_ARM64E_SHARED_CACHE), + ENTRY(PTR_ARM64E_SEGMENTED), }; #undef ENTRY diff --git a/src/MachO/RelocationFixup.cpp b/src/MachO/RelocationFixup.cpp index b21e55ec..8eb0f2be 100644 --- a/src/MachO/RelocationFixup.cpp +++ b/src/MachO/RelocationFixup.cpp @@ -38,10 +38,12 @@ inline uint64_t unpack_target(const details::dyld_chained_ptr_arm64e_rebase& reb RelocationFixup::~RelocationFixup() { switch (rtypes_) { - case REBASE_TYPES::ARM64E_REBASE: delete arm64_rebase_; break; - case REBASE_TYPES::ARM64E_AUTH_REBASE: delete arm64_auth_rebase_; break; - case REBASE_TYPES::PTR64_REBASE: delete p64_rebase_; break; - case REBASE_TYPES::PTR32_REBASE: delete p32_rebase_; break; + case REBASE_TYPES::ARM64E_REBASE: delete arm64_rebase_; break; + case REBASE_TYPES::ARM64E_AUTH_REBASE: delete arm64_auth_rebase_; break; + case REBASE_TYPES::PTR64_REBASE: delete p64_rebase_; break; + case REBASE_TYPES::PTR32_REBASE: delete p32_rebase_; break; + case REBASE_TYPES::SEGMENTED: delete segmented_rebase_; break; + case REBASE_TYPES::AUTH_SEGMENTED: delete auth_segmented_rebase_; break; case REBASE_TYPES::UNKNOWN: {} } } @@ -65,6 +67,8 @@ RelocationFixup& RelocationFixup::operator=(const RelocationFixup& other) { case REBASE_TYPES::ARM64E_AUTH_REBASE: arm64_auth_rebase_ = new details::dyld_chained_ptr_arm64e_auth_rebase{*other.arm64_auth_rebase_}; break; case REBASE_TYPES::PTR64_REBASE: p64_rebase_ = new details::dyld_chained_ptr_64_rebase{*other.p64_rebase_}; break; case REBASE_TYPES::PTR32_REBASE: p32_rebase_ = new details::dyld_chained_ptr_32_rebase{*other.p32_rebase_}; break; + case REBASE_TYPES::SEGMENTED: segmented_rebase_ = new details::dyld_chained_ptr_arm64e_segmented_rebase{*other.segmented_rebase_}; break; + case REBASE_TYPES::AUTH_SEGMENTED: auth_segmented_rebase_ = new details::dyld_chained_ptr_arm64e_auth_segmented_rebase{*other.auth_segmented_rebase_}; break; case REBASE_TYPES::UNKNOWN: {} } return *this; @@ -82,6 +86,8 @@ RelocationFixup::RelocationFixup(const RelocationFixup& other) : case REBASE_TYPES::ARM64E_AUTH_REBASE: arm64_auth_rebase_ = new details::dyld_chained_ptr_arm64e_auth_rebase{*other.arm64_auth_rebase_}; break; case REBASE_TYPES::PTR64_REBASE: p64_rebase_ = new details::dyld_chained_ptr_64_rebase{*other.p64_rebase_}; break; case REBASE_TYPES::PTR32_REBASE: p32_rebase_ = new details::dyld_chained_ptr_32_rebase{*other.p32_rebase_}; break; + case REBASE_TYPES::SEGMENTED: segmented_rebase_ = new details::dyld_chained_ptr_arm64e_segmented_rebase{*other.segmented_rebase_}; break; + case REBASE_TYPES::AUTH_SEGMENTED: auth_segmented_rebase_ = new details::dyld_chained_ptr_arm64e_auth_segmented_rebase{*other.auth_segmented_rebase_}; break; case REBASE_TYPES::UNKNOWN: {} } } @@ -129,6 +135,14 @@ uint64_t RelocationFixup::target() const { return imagebase_ + p32_rebase_->target; } + case REBASE_TYPES::SEGMENTED: + case REBASE_TYPES::AUTH_SEGMENTED: + { + LIEF_ERR("Segmented rebase is not supported"); + return -1llu; + } + + case REBASE_TYPES::UNKNOWN: { LIEF_ERR("Can't get target: unknown rebase type"); @@ -176,6 +190,13 @@ void RelocationFixup::target(uint64_t target) { return; } + case REBASE_TYPES::SEGMENTED: + case REBASE_TYPES::AUTH_SEGMENTED: + { + LIEF_ERR("Segmented rebase is not supported"); + return; + } + case REBASE_TYPES::UNKNOWN: { LIEF_ERR("Can't set target: unknown rebase type"); @@ -194,6 +215,10 @@ uint32_t RelocationFixup::next() const { return p64_rebase_->next; case REBASE_TYPES::PTR32_REBASE: return p32_rebase_->next; + case REBASE_TYPES::SEGMENTED: + return segmented_rebase_->next; + case REBASE_TYPES::AUTH_SEGMENTED: + return auth_segmented_rebase_->next; case REBASE_TYPES::UNKNOWN: return 0; } @@ -214,6 +239,12 @@ void RelocationFixup::next(uint32_t value) { case REBASE_TYPES::PTR32_REBASE: p32_rebase_->next = value; break; + case REBASE_TYPES::SEGMENTED: + segmented_rebase_->next = value; + break; + case REBASE_TYPES::AUTH_SEGMENTED: + auth_segmented_rebase_->next = value; + break; case REBASE_TYPES::UNKNOWN: return; } @@ -240,6 +271,16 @@ void RelocationFixup::set(const details::dyld_chained_ptr_32_rebase& fixup) { p32_rebase_ = new details::dyld_chained_ptr_32_rebase(fixup); } +void RelocationFixup::set(const details::dyld_chained_ptr_arm64e_segmented_rebase& fixup) { + rtypes_ = REBASE_TYPES::SEGMENTED; + segmented_rebase_ = new details::dyld_chained_ptr_arm64e_segmented_rebase(fixup); +} + +void RelocationFixup::set(const details::dyld_chained_ptr_arm64e_auth_segmented_rebase& fixup) { + rtypes_ = REBASE_TYPES::AUTH_SEGMENTED; + auth_segmented_rebase_ = new details::dyld_chained_ptr_arm64e_auth_segmented_rebase(fixup); +} + } }