From 3def579f75965aa19c021d840a759bce2afc0a31 Mon Sep 17 00:00:00 2001 From: Romain Thomas Date: Sat, 5 Oct 2024 05:23:39 +0200 Subject: [PATCH] Add support for creating and analyzing chained fixups & bindings --- api/python/lief/MachO.pyi | 217 +++++++ api/python/src/MachO/init.cpp | 2 + api/python/src/MachO/objects/CMakeLists.txt | 1 + .../objects/pyChainedPointerAnalysis.cpp | 561 ++++++++++++++++++ include/LIEF/MachO.hpp | 1 + include/LIEF/MachO/BindingInfo.hpp | 2 + include/LIEF/MachO/ChainedBindingInfo.hpp | 9 +- include/LIEF/MachO/ChainedPointerAnalysis.hpp | 388 ++++++++++++ include/LIEF/MachO/DyldChainedFixups.hpp | 10 +- .../LIEF/MachO/DyldChainedFixupsCreator.hpp | 171 ++++++ include/LIEF/MachO/RelocationFixup.hpp | 2 + include/LIEF/MachO/SegmentCommand.hpp | 8 +- src/MachO/BinaryParser.tcc | 40 +- src/MachO/Builder.tcc | 32 +- src/MachO/CMakeLists.txt | 2 + src/MachO/ChainedBindingInfoList.cpp | 18 +- src/MachO/ChainedBindingInfoList.hpp | 7 + src/MachO/ChainedPointerAnalysis.cpp | 517 ++++++++++++++++ src/MachO/DyldChainedFixups.cpp | 6 - src/MachO/DyldChainedFixupsCreator.cpp | 417 +++++++++++++ tests/unittests/test_macho.cpp | 84 +++ 21 files changed, 2437 insertions(+), 58 deletions(-) create mode 100644 api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp create mode 100644 include/LIEF/MachO/ChainedPointerAnalysis.hpp create mode 100644 include/LIEF/MachO/DyldChainedFixupsCreator.hpp create mode 100644 src/MachO/ChainedPointerAnalysis.cpp create mode 100644 src/MachO/DyldChainedFixupsCreator.cpp diff --git a/api/python/lief/MachO.pyi b/api/python/lief/MachO.pyi index 2635a0dc..2316d6c6 100644 --- a/api/python/lief/MachO.pyi +++ b/api/python/lief/MachO.pyi @@ -9,6 +9,7 @@ import lief.MachO.Binary # type: ignore import lief.MachO.BuildToolVersion # type: ignore import lief.MachO.BuildVersion # type: ignore import lief.MachO.Builder # type: ignore +import lief.MachO.ChainedPointerAnalysis # type: ignore import lief.MachO.DataCodeEntry # type: ignore import lief.MachO.DyldBindingInfo # type: ignore import lief.MachO.DyldChainedFixups # type: ignore @@ -481,6 +482,222 @@ class ChainedBindingInfo(BindingInfo): @property def sign_extended_addend(self) -> int: ... +class ChainedPointerAnalysis: + class dyld_chained_ptr_32_bind_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addend(self) -> int: ... + @property + def bind(self) -> bool: ... + @property + def next(self) -> int: ... + @property + def ordinal(self) -> int: ... + + class dyld_chained_ptr_32_cache_rebase_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def next(self) -> int: ... + @property + def target(self) -> int: ... + + class dyld_chained_ptr_32_firmware_rebase_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def next(self) -> int: ... + @property + def target(self) -> int: ... + + class dyld_chained_ptr_32_rebase_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def bind(self) -> int: ... + @property + def next(self) -> int: ... + @property + def target(self) -> int: ... + + class dyld_chained_ptr_64_bind_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addend(self) -> int: ... + @property + def bind(self) -> bool: ... + @property + def next(self) -> int: ... + @property + def ordinal(self) -> int: ... + @property + def reserved(self) -> int: ... + + class dyld_chained_ptr_64_kernel_cache_rebase_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addr_div(self) -> int: ... + @property + def cache_level(self) -> int: ... + @property + def diversity(self) -> int: ... + @property + def is_auth(self) -> bool: ... + @property + def key(self) -> int: ... + @property + def next(self) -> int: ... + @property + def ordinal(self) -> int: ... + + class dyld_chained_ptr_64_rebase_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def bind(self) -> bool: ... + @property + def high8(self) -> int: ... + @property + def next(self) -> int: ... + @property + def reserved(self) -> int: ... + @property + def target(self) -> int: ... + @property + def unpack_target(self) -> int: ... + + class dyld_chained_ptr_arm64e_auth_bind24_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addr_div(self) -> int: ... + @property + def auth(self) -> bool: ... + @property + def bind(self) -> bool: ... + @property + def diversity(self) -> int: ... + @property + def key(self) -> int: ... + @property + def next(self) -> int: ... + @property + def ordinal(self) -> int: ... + @property + def zero(self) -> int: ... + + class dyld_chained_ptr_arm64e_auth_bind_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addr_div(self) -> int: ... + @property + def auth(self) -> bool: ... + @property + def bind(self) -> bool: ... + @property + def diversity(self) -> int: ... + @property + def key(self) -> int: ... + @property + def next(self) -> int: ... + @property + def ordinal(self) -> int: ... + @property + def zero(self) -> int: ... + + class dyld_chained_ptr_arm64e_auth_rebase_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addr_div(self) -> int: ... + @property + def auth(self) -> int: ... + @property + def bind(self) -> bool: ... + @property + def diversity(self) -> int: ... + @property + def key(self) -> int: ... + @property + def next(self) -> int: ... + @property + def target(self) -> int: ... + + class dyld_chained_ptr_arm64e_bind24_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addend(self) -> int: ... + @property + def auth(self) -> bool: ... + @property + def bind(self) -> bool: ... + @property + def next(self) -> int: ... + @property + def ordinal(self) -> int: ... + @property + def zero(self) -> int: ... + + class dyld_chained_ptr_arm64e_bind_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def addend(self) -> int: ... + @property + def auth(self) -> bool: ... + @property + def bind(self) -> bool: ... + @property + def next(self) -> int: ... + @property + def ordinal(self) -> int: ... + @property + def zero(self) -> int: ... + + class dyld_chained_ptr_arm64e_rebase_t: + def __init__(self, *args, **kwargs) -> None: ... + @property + def auth(self) -> bool: ... + @property + def bind(self) -> bool: ... + @property + def high8(self) -> int: ... + @property + def next(self) -> int: ... + @property + def target(self) -> int: ... + @property + def unpack_target(self) -> int: ... + def __init__(self, *args, **kwargs) -> None: ... + @staticmethod + def from_value(ptr: int, size: int) -> Optional[lief.MachO.ChainedPointerAnalysis]: ... + def get_as(self, arg: lief.MachO.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]: ... + @staticmethod + def stride(fmt: lief.MachO.DYLD_CHAINED_PTR_FORMAT) -> int: ... + @property + def dyld_chained_ptr_32_bind(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_bind_t: ... + @property + def dyld_chained_ptr_32_cache_rebase(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_cache_rebase_t: ... + @property + def dyld_chained_ptr_32_firmware_rebase(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_firmware_rebase_t: ... + @property + def dyld_chained_ptr_32_rebase(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_32_rebase_t: ... + @property + def dyld_chained_ptr_64_bind(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_bind_t: ... + @property + def dyld_chained_ptr_64_kernel_cache_rebase(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_kernel_cache_rebase_t: ... + @property + def dyld_chained_ptr_64_rebase(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_64_rebase_t: ... + @property + def dyld_chained_ptr_arm64e_auth_bind(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_bind_t: ... + @property + def dyld_chained_ptr_arm64e_auth_bind24(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_bind24_t: ... + @property + def dyld_chained_ptr_arm64e_auth_rebase(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_auth_rebase_t: ... + @property + def dyld_chained_ptr_arm64e_bind(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_bind_t: ... + @property + def dyld_chained_ptr_arm64e_bind24(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_bind24_t: ... + @property + def dyld_chained_ptr_arm64e_rebase(self) -> lief.MachO.ChainedPointerAnalysis.dyld_chained_ptr_arm64e_rebase_t: ... + @property + def size(self) -> int: ... + @property + def value(self) -> int: ... + class CodeSignature(LoadCommand): data_offset: int data_size: int diff --git a/api/python/src/MachO/init.cpp b/api/python/src/MachO/init.cpp index 4815fcd4..55dad351 100644 --- a/api/python/src/MachO/init.cpp +++ b/api/python/src/MachO/init.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #define CREATE(X,Y) create(Y) @@ -75,6 +76,7 @@ void init_objects(nb::module_& m) { CREATE(ParserConfig, m); CREATE(Parser, m); + CREATE(ChainedPointerAnalysis, m); CREATE(FatBinary, m); CREATE(Binary, m); CREATE(Header, m); diff --git a/api/python/src/MachO/objects/CMakeLists.txt b/api/python/src/MachO/objects/CMakeLists.txt index ccceb0b4..84c73c39 100644 --- a/api/python/src/MachO/objects/CMakeLists.txt +++ b/api/python/src/MachO/objects/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(pyLIEF PRIVATE pyBuildVersion.cpp pyBuilder.cpp pyChainedBindingInfo.cpp + pyChainedPointerAnalysis.cpp pyCodeSignature.cpp pyCodeSignatureDir.cpp pyDataCodeEntry.cpp diff --git a/api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp b/api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp new file mode 100644 index 00000000..26503fca --- /dev/null +++ b/api/python/src/MachO/objects/pyChainedPointerAnalysis.cpp @@ -0,0 +1,561 @@ +/* Copyright 2017 - 2024 R. Thomas + * Copyright 2017 - 2024 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 + +#include +#include +#include +#include + +#include "LIEF/MachO/ChainedPointerAnalysis.hpp" + +#include "MachO/pyMachO.hpp" +#include "typing.hpp" + +namespace LIEF::MachO::py { + +struct ChainedPointer : public nanobind::object { + LIEF_PY_DEFAULT_CTOR(ChainedPointer, nanobind::object); + + NB_OBJECT_DEFAULT(ChainedPointer, object, + "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" + "]", + check) + + static bool check(handle h) { + return true; + } +}; + +template<> +void create(nb::module_& m) { + nb::class_ clazz(m, "ChainedPointerAnalysis"); + nb::class_( + clazz, "dyld_chained_ptr_arm64e_rebase_t" + ) + .def_prop_ro("unpack_target", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t::unpack_target) + + .def_prop_ro("target", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t& ptr) { + return (uint64_t)ptr.target; + } + ) + .def_prop_ro("high8", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t& ptr) { + return (uint64_t)ptr.high8; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t& ptr) { + return (bool)ptr.bind; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t& ptr) { + return (bool)ptr.auth; + } + ) + + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_arm64e_bind_t" + ) + .def_prop_ro("ordinal", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t& ptr) { + return (uint64_t)ptr.ordinal; + } + ) + .def_prop_ro("zero", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t& ptr) { + return (uint64_t)ptr.zero; + } + ) + .def_prop_ro("addend", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t& ptr) { + return (uint64_t)ptr.addend; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t& ptr) { + return (bool)ptr.bind; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t& ptr) { + return (bool)ptr.auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_arm64e_auth_rebase_t" + ) + .def_prop_ro("target", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& ptr) { + return (uint64_t)ptr.target; + } + ) + .def_prop_ro("diversity", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& ptr) { + return (uint64_t)ptr.diversity; + } + ) + .def_prop_ro("addr_div", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& ptr) { + return (uint64_t)ptr.addr_div; + } + ) + .def_prop_ro("key", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& ptr) { + return (uint64_t)ptr.key; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& ptr) { + return (bool)ptr.bind; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& ptr) { + return (uint64_t)ptr.auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_arm64e_auth_bind_t") + + .def_prop_ro("ordinal", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (uint64_t)ptr.ordinal; + } + ) + .def_prop_ro("zero", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (uint64_t)ptr.zero; + } + ) + .def_prop_ro("diversity", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (uint64_t)ptr.diversity; + } + ) + .def_prop_ro("addr_div", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (uint64_t)ptr.addr_div; + } + ) + .def_prop_ro("key", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (uint64_t)ptr.key; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (bool)ptr.bind; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& ptr) { + return (bool)ptr.auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_64_rebase_t" + ) + + .def_prop_ro("unpack_target", + &ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t::unpack_target) + + .def_prop_ro("target", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t& ptr) { + return (uint64_t)ptr.target; + } + ) + .def_prop_ro("high8", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t& ptr) { + return (uint64_t)ptr.high8; + } + ) + .def_prop_ro("reserved", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t& ptr) { + return (uint64_t)ptr.reserved; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t& ptr) { + return (bool)ptr.bind; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_arm64e_bind24_t" + ) + .def_prop_ro("ordinal", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t& ptr) { + return (uint64_t)ptr.ordinal; + } + ) + .def_prop_ro("zero", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t& ptr) { + return (uint64_t)ptr.zero; + } + ) + .def_prop_ro("addend", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t& ptr) { + return (uint64_t)ptr.addend; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t& ptr) { + return (bool)ptr.bind; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t& ptr) { + return (bool)ptr.auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_arm64e_auth_bind24_t" + ) + .def_prop_ro("ordinal", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (uint64_t)ptr.ordinal; + } + ) + .def_prop_ro("zero", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (uint64_t)ptr.zero; + } + ) + .def_prop_ro("diversity", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (uint64_t)ptr.diversity; + } + ) + .def_prop_ro("addr_div", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (uint64_t)ptr.addr_div; + } + ) + .def_prop_ro("key", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (uint64_t)ptr.key; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (bool)ptr.bind; + } + ) + .def_prop_ro("auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& ptr) { + return (bool)ptr.auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_64_bind_t" + ) + .def_prop_ro("ordinal", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t& ptr) { + return (uint64_t)ptr.ordinal; + } + ) + .def_prop_ro("addend", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t& ptr) { + return (uint64_t)ptr.addend; + } + ) + .def_prop_ro("reserved", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t& ptr) { + return (uint64_t)ptr.reserved; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t& ptr) { + return (bool)ptr.bind; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_64_kernel_cache_rebase_t" + ) + .def_prop_ro("ordinal", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& ptr) { + return (uint64_t)ptr.target; + } + ) + .def_prop_ro("cache_level", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& ptr) { + return (uint64_t)ptr.cache_level; + } + ) + .def_prop_ro("diversity", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& ptr) { + return (uint64_t)ptr.diversity; + } + ) + .def_prop_ro("addr_div", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& ptr) { + return (uint64_t)ptr.addr_div; + } + ) + .def_prop_ro("key", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& ptr) { + return (uint64_t)ptr.key; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("is_auth", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& ptr) { + return (bool)ptr.is_auth; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_32_rebase_t" + ) + .def_prop_ro("target", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_rebase_t& ptr) { + return (uint64_t)ptr.target; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_rebase_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_rebase_t& ptr) { + return (uint64_t)ptr.bind; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_32_rebase_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_32_bind_t" + ) + .def_prop_ro("ordinal", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_bind_t& ptr) { + return (uint64_t)ptr.ordinal; + } + ) + .def_prop_ro("addend", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_bind_t& ptr) { + return (uint64_t)ptr.addend; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_bind_t& ptr) { + return (uint64_t)ptr.next; + } + ) + .def_prop_ro("bind", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_bind_t& ptr) { + return (bool)ptr.bind; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_32_bind_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_32_cache_rebase_t" + ) + .def_prop_ro("target", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_cache_rebase_t& ptr) { + return (uint64_t)ptr.target; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_cache_rebase_t& ptr) { + return (uint64_t)ptr.next; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_32_cache_rebase_t); + ; + nb::class_( + clazz, "dyld_chained_ptr_32_firmware_rebase_t" + ) + .def_prop_ro("target", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_firmware_rebase_t& ptr) { + return (uint64_t)ptr.target; + } + ) + .def_prop_ro("next", + [] (const ChainedPointerAnalysis::dyld_chained_ptr_32_firmware_rebase_t& ptr) { + return (uint64_t)ptr.next; + } + ) + LIEF_DEFAULT_STR(ChainedPointerAnalysis::dyld_chained_ptr_32_firmware_rebase_t); + ; + clazz + .def_static("stride", + &ChainedPointerAnalysis::stride, "fmt"_a) + .def_static("from_value", + &ChainedPointerAnalysis::from_value, "ptr"_a, "size"_a) + .def_prop_ro("value", + &ChainedPointerAnalysis::value) + .def_prop_ro("size", + &ChainedPointerAnalysis::size) + .def_prop_ro("dyld_chained_ptr_arm64e_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase) + .def_prop_ro("dyld_chained_ptr_arm64e_bind", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind) + .def_prop_ro("dyld_chained_ptr_arm64e_auth_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase) + .def_prop_ro("dyld_chained_ptr_arm64e_auth_bind", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind) + .def_prop_ro("dyld_chained_ptr_64_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_64_rebase) + .def_prop_ro("dyld_chained_ptr_arm64e_bind24", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24) + .def_prop_ro("dyld_chained_ptr_arm64e_auth_bind24", + &ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24) + .def_prop_ro("dyld_chained_ptr_64_bind", + &ChainedPointerAnalysis::dyld_chained_ptr_64_bind) + .def_prop_ro("dyld_chained_ptr_64_kernel_cache_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase) + .def_prop_ro("dyld_chained_ptr_32_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_32_rebase) + .def_prop_ro("dyld_chained_ptr_32_bind", + &ChainedPointerAnalysis::dyld_chained_ptr_32_bind) + .def_prop_ro("dyld_chained_ptr_32_cache_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_32_cache_rebase) + .def_prop_ro("dyld_chained_ptr_32_firmware_rebase", + &ChainedPointerAnalysis::dyld_chained_ptr_32_firmware_rebase) + .def("get_as", + [] (const ChainedPointerAnalysis& self, DYLD_CHAINED_PTR_FORMAT fmt) -> ChainedPointer { + ChainedPointerAnalysis::union_pointer_t ptr = self.get_as(fmt); + switch (ptr.type) { + case ChainedPointerAnalysis::PTR_TYPE::UNKNOWN: + return nb::cast(ptr.raw); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE: + return nb::cast(ptr.arm64e_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND: + return nb::cast(ptr.arm64e_bind); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE: + return nb::cast(ptr.arm64e_auth_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND: + return nb::cast(ptr.arm64e_auth_bind); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE: + return nb::cast(ptr.ptr_64_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24: + return nb::cast(ptr.arm64e_bind24); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: + return nb::cast(ptr.arm64e_auth_bind24); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_BIND: + return nb::cast(ptr.ptr_64_bind); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE: + return nb::cast(ptr.ptr_64_kernel_cache_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE: + return nb::cast(ptr.ptr_32_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_BIND: + return nb::cast(ptr.ptr_32_bind); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE: + return nb::cast(ptr.ptr_32_cache_rebase); + + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: + return nb::cast(ptr.ptr_32_firmware_rebase); + } + return nb::none(); + } + ) + ; + +} +} diff --git a/include/LIEF/MachO.hpp b/include/LIEF/MachO.hpp index 9eb454a2..460e3fad 100644 --- a/include/LIEF/MachO.hpp +++ b/include/LIEF/MachO.hpp @@ -34,6 +34,7 @@ #include "LIEF/MachO/DataInCode.hpp" #include "LIEF/MachO/DyldBindingInfo.hpp" #include "LIEF/MachO/DyldChainedFixups.hpp" +#include "LIEF/MachO/DyldChainedFixupsCreator.hpp" #include "LIEF/MachO/DyldEnvironment.hpp" #include "LIEF/MachO/DyldExportsTrie.hpp" #include "LIEF/MachO/DyldInfo.hpp" diff --git a/include/LIEF/MachO/BindingInfo.hpp b/include/LIEF/MachO/BindingInfo.hpp index 7bebbc36..06430199 100644 --- a/include/LIEF/MachO/BindingInfo.hpp +++ b/include/LIEF/MachO/BindingInfo.hpp @@ -27,6 +27,7 @@ class DylibCommand; class SegmentCommand; class Symbol; class BinaryParser; +class DyldChainedFixupsCreator; //! Class that provides an interface over a *binding* operation. //! @@ -38,6 +39,7 @@ class BinaryParser; class LIEF_API BindingInfo : public Object { friend class BinaryParser; + friend class DyldChainedFixupsCreator; public: enum class TYPES { diff --git a/include/LIEF/MachO/ChainedBindingInfo.hpp b/include/LIEF/MachO/ChainedBindingInfo.hpp index def3dbe9..8aea25e4 100644 --- a/include/LIEF/MachO/ChainedBindingInfo.hpp +++ b/include/LIEF/MachO/ChainedBindingInfo.hpp @@ -23,9 +23,11 @@ namespace LIEF { namespace MachO { -class Symbol; class BinaryParser; class Builder; +class DyldChainedFixupsCreator; +class ChainedBindingInfoList; +class Symbol; namespace details { struct dyld_chained_ptr_arm64e_bind; @@ -47,9 +49,10 @@ class LIEF_API ChainedBindingInfo : public BindingInfo { friend class BinaryParser; friend class Builder; + friend class DyldChainedFixupsCreator; + friend class ChainedBindingInfoList; public: - ChainedBindingInfo() = delete; explicit ChainedBindingInfo(DYLD_CHAINED_FORMAT fmt, bool is_weak); @@ -108,7 +111,7 @@ class LIEF_API ChainedBindingInfo : public BindingInfo { return os; } - private: + protected: void clear(); enum class BIND_TYPES { UNKNOWN = 0, diff --git a/include/LIEF/MachO/ChainedPointerAnalysis.hpp b/include/LIEF/MachO/ChainedPointerAnalysis.hpp new file mode 100644 index 00000000..eae5aa5e --- /dev/null +++ b/include/LIEF/MachO/ChainedPointerAnalysis.hpp @@ -0,0 +1,388 @@ +/* Copyright 2017 - 2024 R. Thomas + * Copyright 2017 - 2024 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_CHAINED_PTR_ANALYSIS_H +#define LIEF_MACHO_CHAINED_PTR_ANALYSIS_H +#include +#include +#include + +#include "LIEF/MachO/DyldChainedFormat.hpp" +#include "LIEF/config.h" +#include "LIEF/errors.hpp" + +namespace LIEF { +class BinaryStream; +namespace MachO { +class ChainedPointerAnalysis { + public: + // DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E + struct dyld_chained_ptr_arm64e_rebase_t + { + uint64_t target : 43, + high8 : 8, + next : 11, + bind : 1, + auth : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_rebase_t& chain); + + uint64_t unpack_target() const { + return uint64_t(high8) | target; + } + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E + struct dyld_chained_ptr_arm64e_bind_t + { + uint64_t ordinal : 16, + zero : 16, + addend : 19, + next : 11, + bind : 1, + auth : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_bind_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E + struct dyld_chained_ptr_arm64e_auth_rebase_t + { + uint64_t target : 32, + diversity : 16, + addr_div : 1, + key : 2, + next : 11, + bind : 1, + auth : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_auth_rebase_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E + struct dyld_chained_ptr_arm64e_auth_bind_t + { + uint64_t ordinal : 16, + zero : 16, + diversity : 16, + addr_div : 1, + key : 2, + next : 11, + bind : 1, + auth : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_auth_bind_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_64 & DYLD_CHAINED_PTR_FORMAT::PTR_64_OFFSET + struct dyld_chained_ptr_64_rebase_t + { + uint64_t target : 36, + high8 : 8, + reserved : 7, + next : 12, + bind : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_64_rebase_t& chain); + + uint64_t unpack_target() const { + return uint64_t(high8) | target; + } + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24 + struct dyld_chained_ptr_arm64e_bind24_t + { + uint64_t ordinal : 24, + zero : 8, + addend : 19, + next : 11, + bind : 1, + auth : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_bind24_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24 + struct dyld_chained_ptr_arm64e_auth_bind24_t + { + uint64_t ordinal : 24, + zero : 8, + diversity : 16, + addr_div : 1, + key : 2, + next : 11, + bind : 1, + auth : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_arm64e_auth_bind24_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_64 + struct dyld_chained_ptr_64_bind_t + { + uint64_t ordinal : 24, + addend : 8, + reserved : 19, + next : 12, + bind : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_64_bind_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE + struct dyld_chained_ptr_64_kernel_cache_rebase_t + { + uint64_t target : 30, + cache_level : 2, + diversity : 16, + addr_div : 1, + key : 2, + next : 12, + is_auth : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_64_kernel_cache_rebase_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_32 + struct dyld_chained_ptr_32_rebase_t + { + uint32_t target : 26, + next : 5, + bind : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_32_rebase_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_32 + struct dyld_chained_ptr_32_bind_t + { + uint32_t ordinal : 20, + addend : 6, + next : 5, + bind : 1; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_32_bind_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_32_CACHE + struct dyld_chained_ptr_32_cache_rebase_t + { + uint32_t target : 30, + next : 2; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_32_cache_rebase_t& chain); + }; + + // DYLD_CHAINED_PTR_FORMAT::PTR_32_FIRMWARE + struct dyld_chained_ptr_32_firmware_rebase_t + { + uint32_t target : 26, + next : 6; + + friend std::ostream& operator<<(std::ostream& os, const dyld_chained_ptr_32_firmware_rebase_t& chain); + }; + + enum class PTR_TYPE { + UNKNOWN = 0, + DYLD_CHAINED_PTR_ARM64E_REBASE, + DYLD_CHAINED_PTR_ARM64E_BIND, + DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE, + DYLD_CHAINED_PTR_ARM64E_AUTH_BIND, + DYLD_CHAINED_PTR_64_REBASE, + DYLD_CHAINED_PTR_ARM64E_BIND24, + DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24, + DYLD_CHAINED_PTR_64_BIND, + DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE, + DYLD_CHAINED_PTR_32_REBASE, + DYLD_CHAINED_PTR_32_BIND, + DYLD_CHAINED_PTR_32_CACHE_REBASE, + DYLD_CHAINED_PTR_32_FIRMWARE_REBASE + }; + + static std::unique_ptr from_value(uint64_t value, + size_t size) + { + return std::unique_ptr( + new ChainedPointerAnalysis(value, size)); + } + + static size_t stride(DYLD_CHAINED_PTR_FORMAT fmt) { + switch (fmt) { + case DYLD_CHAINED_PTR_FORMAT::NONE: + return 0; + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24: + return 8; + + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_FIRMWARE: + case DYLD_CHAINED_PTR_FORMAT::PTR_32_FIRMWARE: + case DYLD_CHAINED_PTR_FORMAT::PTR_64: + case DYLD_CHAINED_PTR_FORMAT::PTR_64_OFFSET: + case DYLD_CHAINED_PTR_FORMAT::PTR_32: + case DYLD_CHAINED_PTR_FORMAT::PTR_32_CACHE: + case DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE: + return 4; + + case DYLD_CHAINED_PTR_FORMAT::PTR_X86_64_KERNEL_CACHE: + return 1; + } + return 0; + } + + static size_t ptr_size(DYLD_CHAINED_PTR_FORMAT fmt) { + switch (fmt) { + case DYLD_CHAINED_PTR_FORMAT::NONE: + return 0; + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_FIRMWARE: + case DYLD_CHAINED_PTR_FORMAT::PTR_64: + case DYLD_CHAINED_PTR_FORMAT::PTR_64_OFFSET: + case DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_FORMAT::PTR_X86_64_KERNEL_CACHE: + return sizeof(uint64_t); + + case DYLD_CHAINED_PTR_FORMAT::PTR_32_FIRMWARE: + case DYLD_CHAINED_PTR_FORMAT::PTR_32: + case DYLD_CHAINED_PTR_FORMAT::PTR_32_CACHE: + return sizeof(uint32_t); + } + return 0; + } + + ChainedPointerAnalysis(uint64_t value, size_t size) : + value_(value), + size_(size) + {} + + ChainedPointerAnalysis(const ChainedPointerAnalysis&) = default; + ChainedPointerAnalysis& operator=(const ChainedPointerAnalysis&) = default; + + ChainedPointerAnalysis(ChainedPointerAnalysis&&) noexcept = default; + ChainedPointerAnalysis& operator=(ChainedPointerAnalysis&&) noexcept = default; + + ~ChainedPointerAnalysis() = default; + + uint64_t value() const { + return value_; + } + + size_t size() const { + return size_; + } + + const dyld_chained_ptr_arm64e_rebase_t dyld_chained_ptr_arm64e_rebase() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_arm64e_bind_t& dyld_chained_ptr_arm64e_bind() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_arm64e_auth_rebase_t dyld_chained_ptr_arm64e_auth_rebase() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_arm64e_auth_bind_t dyld_chained_ptr_arm64e_auth_bind() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_64_rebase_t dyld_chained_ptr_64_rebase() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_arm64e_bind24_t dyld_chained_ptr_arm64e_bind24() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_arm64e_auth_bind24_t dyld_chained_ptr_arm64e_auth_bind24() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_64_bind_t dyld_chained_ptr_64_bind() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_64_kernel_cache_rebase_t dyld_chained_ptr_64_kernel_cache_rebase() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_32_rebase_t dyld_chained_ptr_32_rebase() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_32_bind_t dyld_chained_ptr_32_bind() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_32_cache_rebase_t dyld_chained_ptr_32_cache_rebase() const { + return *reinterpret_cast(&value_); + } + + const dyld_chained_ptr_32_firmware_rebase_t dyld_chained_ptr_32_firmware_rebase() const { + return *reinterpret_cast(&value_); + } + + struct union_pointer_t { + PTR_TYPE type = PTR_TYPE::UNKNOWN; + union { + dyld_chained_ptr_arm64e_rebase_t arm64e_rebase; + dyld_chained_ptr_arm64e_bind_t arm64e_bind; + dyld_chained_ptr_arm64e_auth_rebase_t arm64e_auth_rebase; + dyld_chained_ptr_arm64e_auth_bind_t arm64e_auth_bind; + dyld_chained_ptr_64_rebase_t ptr_64_rebase; + dyld_chained_ptr_arm64e_bind24_t arm64e_bind24; + dyld_chained_ptr_arm64e_auth_bind24_t arm64e_auth_bind24; + dyld_chained_ptr_64_bind_t ptr_64_bind; + dyld_chained_ptr_64_kernel_cache_rebase_t ptr_64_kernel_cache_rebase; + dyld_chained_ptr_32_rebase_t ptr_32_rebase; + 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; + uint64_t raw; + }; + uint32_t next() const; + + result ordinal() const; + result target() const; + + bool is_bind() const { + return (bool)ordinal(); + } + bool is_auth() const; + + friend std::ostream& operator<<(std::ostream& os, const union_pointer_t& ptr); + }; + + static_assert(sizeof(union_pointer_t) == 16); + + union_pointer_t get_as(DYLD_CHAINED_PTR_FORMAT fmt) const; + + static uint64_t walk_chain( + BinaryStream& stream, DYLD_CHAINED_PTR_FORMAT format, + const std::function& callback); + + private: + uint64_t value_ = 0; + size_t size_ = 0; +}; +} +} +#endif diff --git a/include/LIEF/MachO/DyldChainedFixups.hpp b/include/LIEF/MachO/DyldChainedFixups.hpp index 80a64fa4..cad1e6bd 100644 --- a/include/LIEF/MachO/DyldChainedFixups.hpp +++ b/include/LIEF/MachO/DyldChainedFixups.hpp @@ -31,6 +31,7 @@ class BindingInfoIterator; class Builder; class ChainedBindingInfo; class ChainedBindingInfoList; +class DyldChainedFixupsCreator; class LinkEdit; class SegmentCommand; @@ -51,6 +52,7 @@ class LIEF_API DyldChainedFixups : public LoadCommand { friend class LinkEdit; friend class BindingInfoIterator; friend class Binary; + friend class DyldChainedFixupsCreator; public: //! Structure that mirrors the raw dyld_chained_starts_in_segment @@ -63,7 +65,7 @@ class LIEF_API DyldChainedFixups : public LoadCommand { uint16_t page_size = 0; ///< Likely 0x1000 for x86/x86_64 architectures and 0x4000 for ARM64 architecture uint64_t segment_offset = 0; ///< Offset of the segment's data from the beginning of the file (it should match SegmentCommand::file_offset) uint32_t max_valid_pointer = 0; ///< for 32-bit OS, any value beyond this is not a pointer - DYLD_CHAINED_PTR_FORMAT pointer_format; ///< How pointers are encoded + DYLD_CHAINED_PTR_FORMAT pointer_format = DYLD_CHAINED_PTR_FORMAT::NONE; ///< How pointers are encoded //! How many pages are in the page_start array size_t page_count() const { @@ -79,7 +81,11 @@ class LIEF_API DyldChainedFixups : public LoadCommand { private: friend class BinaryParser; - chained_starts_in_segment(uint32_t offset, SegmentCommand& segment); + friend class DyldChainedFixupsCreator; + chained_starts_in_segment(uint32_t offset, SegmentCommand& segment) : + offset(offset), + segment(segment) + {} chained_starts_in_segment(uint32_t offset, const details::dyld_chained_starts_in_segment& info, SegmentCommand& segment); }; diff --git a/include/LIEF/MachO/DyldChainedFixupsCreator.hpp b/include/LIEF/MachO/DyldChainedFixupsCreator.hpp new file mode 100644 index 00000000..ca7d7355 --- /dev/null +++ b/include/LIEF/MachO/DyldChainedFixupsCreator.hpp @@ -0,0 +1,171 @@ +/* Copyright 2017 - 2024 R. Thomas + * Copyright 2017 - 2024 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_DYLD_CHAINED_FIXUPS_CREATOR_H +#define LIEF_MACHO_DYLD_CHAINED_FIXUPS_CREATOR_H +#include "LIEF/visibility.h" +#include "LIEF/MachO/DyldChainedFormat.hpp" +#include "LIEF/MachO/DyldChainedFixups.hpp" +#include "LIEF/errors.hpp" + +#include +#include +#include +#include + +namespace LIEF { +namespace MachO { +class RelocationFixup; +class Symbol; +class Binary; +class ChainedBindingInfo; +class SegmentCommand; +class ChainedBindingInfoList; + +class strong_map_t; + +// This class aims to provide facilities to (re)create the LC_DYLD_CHAINED_FIXUPS +// command. +class LIEF_LOCAL DyldChainedFixupsCreator { + public: + static constexpr uint32_t MAX_IMPORTS = (uint32_t(1) << 24) - 1; + static constexpr uint32_t BIND24_THRESHOLD = (uint32_t(1) << 16) - 1; + struct binding_info_t { + uint64_t address = 0; + std::string library; + std::string symbol; + bool weak; + uint64_t addend; + }; + + struct reloc_info_t { + uint64_t addr = 0; + uint64_t target = 0; + }; + DyldChainedFixupsCreator() = default; + + DyldChainedFixupsCreator& fixups_version(uint32_t value) { + fixups_version_ = value; + return *this; + } + + DyldChainedFixupsCreator& imports_format(DYLD_CHAINED_FORMAT fmt) { + imports_format_ = fmt; + return *this; + } + + DyldChainedFixupsCreator& add_relocation(uint64_t address, uint64_t target); + + DyldChainedFixupsCreator& + add_relocations(const std::vector& relocations) + { + std::copy(relocations.begin(), relocations.end(), + std::back_inserter(relocations_)); + return *this; + } + + DyldChainedFixupsCreator& add_binding(uint64_t address, + std::string symbol, std::string library, + uint64_t addend = 0, bool weak = false); + + DyldChainedFixupsCreator& add_binding(uint64_t address, + std::string symbol, + uint64_t addend = 0, bool weak = false) + { + return add_binding(address, std::move(symbol), "", addend, weak); + } + + DyldChainedFixupsCreator& add_bindings(const std::vector& bindings) { + std::copy(bindings.begin(), bindings.end(), std::back_inserter(bindings_)); + return *this; + } + + DyldChainedFixups* create(Binary& target); + + private: + struct binding_rebase_t { + enum TYPE { + UNKNOWN = 0, + FIXUP, BINDING, + }; + union { + ChainedBindingInfo* binding = nullptr; + RelocationFixup* fixup; + }; + TYPE type = UNKNOWN; + + binding_rebase_t(ChainedBindingInfo& info) : + binding(&info), + type(BINDING) + {} + + binding_rebase_t(RelocationFixup& fixup) : + fixup(&fixup), + type(FIXUP) + {} + + uint64_t addr() const; + + bool is_binding() const { + return type == BINDING; + } + + bool is_fixup() const { + return type == FIXUP; + } + + friend bool operator<(const binding_rebase_t& lhs, + const binding_rebase_t& rhs) { + return lhs.addr() < rhs.addr(); + } + + friend bool operator>(const binding_rebase_t& lhs, + const binding_rebase_t& rhs) { + return lhs.addr() > rhs.addr(); + } + + friend bool operator>=(const binding_rebase_t& lhs, + const binding_rebase_t& rhs) { + return !(lhs < rhs); + } + + friend bool operator<=(const binding_rebase_t& lhs, + const binding_rebase_t& rhs) { + return !(lhs > rhs); + } + + }; + result lib2ord(const Binary& bin, const Symbol& sym, + const std::string& lib); + const Symbol* find_symbol(const Binary& bin, const std::string& name); + + static DYLD_CHAINED_PTR_FORMAT pointer_format(const Binary& bin, size_t imp_count); + ok_error_t process_relocations(Binary& target, DYLD_CHAINED_PTR_FORMAT ptr_fmt); + ok_error_t process_bindings(Binary& target, + strong_map_t& strong_map, + std::unordered_map& symbols_idx, + DyldChainedFixups* cmd, + DyldChainedFixups::binding_info_t& all_bindings); + + uint32_t fixups_version_ = 0; + DYLD_CHAINED_FORMAT imports_format_; + std::vector bindings_; + std::vector relocations_; + std::unordered_map> segment_chains_; + std::unordered_map lib2ord_; +}; +} +} +#endif diff --git a/include/LIEF/MachO/RelocationFixup.hpp b/include/LIEF/MachO/RelocationFixup.hpp index 5ed23ab6..170f95ca 100644 --- a/include/LIEF/MachO/RelocationFixup.hpp +++ b/include/LIEF/MachO/RelocationFixup.hpp @@ -35,6 +35,7 @@ struct dyld_chained_ptr_32_rebase; class BinaryParser; class Builder; +class DyldChainedFixupsCreator; //! Class that represents a rebase relocation found in the `LC_DYLD_CHAINED_FIXUPS` command. //! @@ -50,6 +51,7 @@ class LIEF_API RelocationFixup : public Relocation { friend class BinaryParser; friend class Builder; + friend class DyldChainedFixupsCreator; public: RelocationFixup() = delete; diff --git a/include/LIEF/MachO/SegmentCommand.hpp b/include/LIEF/MachO/SegmentCommand.hpp index 203cfba3..ad289f9a 100644 --- a/include/LIEF/MachO/SegmentCommand.hpp +++ b/include/LIEF/MachO/SegmentCommand.hpp @@ -33,12 +33,13 @@ namespace LIEF { class SpanStream; namespace MachO { -class BinaryParser; class Binary; +class BinaryParser; class Builder; -class Section; -class Relocation; +class DyldChainedFixupsCreator; class DyldInfo; +class Relocation; +class Section; namespace details { struct segment_command_32; @@ -48,6 +49,7 @@ struct segment_command_64; //! Class which represents a LoadCommand::TYPE::SEGMENT / LoadCommand::TYPE::SEGMENT_64 command class LIEF_API SegmentCommand : public LoadCommand { + friend class DyldChainedFixupsCreator; friend class BinaryParser; friend class Binary; friend class Section; diff --git a/src/MachO/BinaryParser.tcc b/src/MachO/BinaryParser.tcc index 17fac108..5667cc43 100644 --- a/src/MachO/BinaryParser.tcc +++ b/src/MachO/BinaryParser.tcc @@ -25,6 +25,7 @@ #include "LIEF/BinaryStream/MemoryStream.hpp" #include "LIEF/MachO/Binary.hpp" +#include "LIEF/MachO/ChainedPointerAnalysis.hpp" #include "LIEF/MachO/BinaryParser.hpp" #include "LIEF/MachO/BuildVersion.hpp" #include "LIEF/MachO/ChainedBindingInfo.hpp" @@ -2580,7 +2581,9 @@ ok_error_t BinaryParser::parse_chained_fixup(const details::dyld_chained_fixups_ break; } - LIEF_DEBUG(" seg_offset[{}] = {} ({})", seg_idx, seg_info_offset, binary_->segments_[seg_idx]->name()); + LIEF_DEBUG(" 0x{:06x} seg_offset[{}] = {} ({})", + stream.pos() - sizeof(uint32_t), + seg_idx, seg_info_offset, binary_->segments_[seg_idx]->name()); if (seg_info_offset == 0) { struct DyldChainedFixups::chained_starts_in_segment info(0, {}, *binary_->segments_[seg_idx]); chained_fixups_->chained_starts_in_segment_.push_back(std::move(info)); @@ -2769,38 +2772,13 @@ ok_error_t BinaryParser::walk_chain(SegmentCommand& segment, } -inline uintptr_t stride_size(DYLD_CHAINED_PTR_FORMAT fmt) { - switch (fmt) { - case DYLD_CHAINED_PTR_FORMAT::NONE: - return 0; - case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E: - case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND: - case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24: - return 8; - - case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_KERNEL: - case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_FIRMWARE: - case DYLD_CHAINED_PTR_FORMAT::PTR_32_FIRMWARE: - case DYLD_CHAINED_PTR_FORMAT::PTR_64: - case DYLD_CHAINED_PTR_FORMAT::PTR_64_OFFSET: - case DYLD_CHAINED_PTR_FORMAT::PTR_32: - case DYLD_CHAINED_PTR_FORMAT::PTR_32_CACHE: - case DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE: - return 4; - - case DYLD_CHAINED_PTR_FORMAT::PTR_X86_64_KERNEL_CACHE: - return 1; - } - return 0; -} - template result BinaryParser::next_chain(uint64_t& chain_address, uint64_t chain_offset, const details::dyld_chained_starts_in_segment& seg_info) { const auto ptr_fmt = static_cast(seg_info.pointer_format); static constexpr uint64_t CHAIN_END = 0; - const uintptr_t stride = stride_size(ptr_fmt); + const uintptr_t stride = ChainedPointerAnalysis::stride(ptr_fmt); switch (ptr_fmt) { case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E: @@ -3067,7 +3045,7 @@ ok_error_t BinaryParser::do_chained_fixup(SegmentCommand& segment, reloc->address_ = chain_address; reloc->architecture_ = binary_->header().cpu_type(); reloc->segment_ = &segment; - reloc->size_ = stride_size(ptr_fmt) * BYTE_BITS; + reloc->size_ = ChainedPointerAnalysis::stride(ptr_fmt) * BYTE_BITS; reloc->offset_ = chain_offset; if (Section* section = binary_->section_from_virtual_address(address)) { @@ -3138,7 +3116,7 @@ ok_error_t BinaryParser::do_chained_fixup(SegmentCommand& segment, reloc->address_ = chain_address; reloc->architecture_ = binary_->header().cpu_type(); reloc->segment_ = &segment; - reloc->size_ = stride_size(ptr_fmt) * BYTE_BITS; + reloc->size_ = ChainedPointerAnalysis::stride(ptr_fmt) * BYTE_BITS; reloc->offset_ = chain_offset; if (Section* section = binary_->section_from_virtual_address(address)) { @@ -3226,7 +3204,7 @@ ok_error_t BinaryParser::do_chained_fixup(SegmentCommand& segment, reloc->address_ = chain_address; reloc->architecture_ = binary_->header().cpu_type(); reloc->segment_ = &segment; - reloc->size_ = stride_size(ptr_fmt) * BYTE_BITS; + reloc->size_ = ChainedPointerAnalysis::stride(ptr_fmt) * BYTE_BITS; reloc->offset_ = chain_offset; if (Section* section = binary_->section_from_virtual_address(address)) { @@ -3310,7 +3288,7 @@ ok_error_t BinaryParser::do_chained_fixup(SegmentCommand& segment, reloc->address_ = chain_address; reloc->architecture_ = binary_->header().cpu_type(); reloc->segment_ = &segment; - reloc->size_ = stride_size(ptr_fmt) * BYTE_BITS; + reloc->size_ = ChainedPointerAnalysis::stride(ptr_fmt) * BYTE_BITS; reloc->offset_ = chain_offset; if (Section* section = binary_->section_from_virtual_address(address)) { diff --git a/src/MachO/Builder.tcc b/src/MachO/Builder.tcc index 23feef8f..511800a2 100644 --- a/src/MachO/Builder.tcc +++ b/src/MachO/Builder.tcc @@ -1196,7 +1196,7 @@ ok_error_t Builder::build(DyldChainedFixups& fixups) { lnk_data.align(8); const size_t segs_header_off = lnk_data.size(); - if (segs_header_off != fixups.starts_offset()) { + if (fixups.starts_offset() > 0 && segs_header_off != fixups.starts_offset()) { LIEF_INFO("segs_header_off could be wrong!"); } auto starts_in_segment = fixups.chained_starts_in_segments(); @@ -1226,7 +1226,7 @@ ok_error_t Builder::build(DyldChainedFixups& fixups) { // ----- - for (const auto& seg_info : starts_in_segment) { + for (const DyldChainedFixups::chained_starts_in_segment& seg_info : starts_in_segment) { if (seg_info.page_count() == 0) { ++seg_idx; continue; @@ -1253,6 +1253,9 @@ ok_error_t Builder::build(DyldChainedFixups& fixups) { lnk_data.align(8); auto* seg_info_offsets = reinterpret_cast(lnk_data.raw().data() + segs_info_off); seg_info_offsets[seg_idx] = lnk_data.size() - segs_header_off; + LIEF_DEBUG("0x{:06x} seg_info_offsets[{}] = 0x{:016x}", + segs_info_off + sizeof(uint32_t) * seg_idx, + seg_idx, seg_info_offsets[seg_idx]); lnk_data.write(seg); for (uint16_t off : seg_info.page_start) { lnk_data.write(off); @@ -1342,45 +1345,50 @@ ok_error_t Builder::build(DyldChainedFixups& fixups) { } } - for (ChainedBindingInfo* elements : info->elements_) { - const uint64_t rel_offset = elements->offset_ - elements->segment()->file_offset(); - uint8_t* data_ptr = elements->segment_->writable_content().data() + rel_offset; + for (ChainedBindingInfo* binding : info->elements_) { + const uint64_t rel_offset = binding->offset_ - binding->segment()->file_offset(); + uint8_t* data_ptr = binding->segment_->writable_content().data() + rel_offset; + LIEF_DEBUG("Write binding (offset=0x{:010x}): 0x{:016x} {} in {} offset=0x{:010x}", + binding->offset_, binding->address(), binding->symbol()->name(), + binding->segment_->name(), + binding->segment()->file_offset() + rel_offset); + // Rewrite the raw chained binding - switch (elements->btypes_) { + switch (binding->btypes_) { case ChainedBindingInfo::BIND_TYPES::ARM64E_BIND: { auto& raw_bind = *reinterpret_cast(data_ptr); - raw_bind.bind = *elements->arm64_bind_; + raw_bind.bind = *binding->arm64_bind_; break; } case ChainedBindingInfo::BIND_TYPES::ARM64E_AUTH_BIND: { auto& raw_bind = *reinterpret_cast(data_ptr); - raw_bind.auth_bind = *elements->arm64_auth_bind_; + raw_bind.auth_bind = *binding->arm64_auth_bind_; break; } case ChainedBindingInfo::BIND_TYPES::ARM64E_BIND24: { auto& raw_bind = *reinterpret_cast(data_ptr); - raw_bind.bind24 = *elements->arm64_bind24_; + raw_bind.bind24 = *binding->arm64_bind24_; break; } case ChainedBindingInfo::BIND_TYPES::ARM64E_AUTH_BIND24: { auto& raw_bind = *reinterpret_cast(data_ptr); - raw_bind.auth_bind24 = *elements->arm64_auth_bind24_; + raw_bind.auth_bind24 = *binding->arm64_auth_bind24_; break; } case ChainedBindingInfo::BIND_TYPES::PTR64_BIND: { auto& raw_bind = *reinterpret_cast(data_ptr); - raw_bind.bind = *elements->p64_bind_; + raw_bind.bind = *binding->p64_bind_; break; } case ChainedBindingInfo::BIND_TYPES::PTR32_BIND: { auto& raw_bind = *reinterpret_cast(data_ptr); - raw_bind.bind = *elements->p32_bind_; + raw_bind.bind = *binding->p32_bind_; break; } case ChainedBindingInfo::BIND_TYPES::UNKNOWN: break; diff --git a/src/MachO/CMakeLists.txt b/src/MachO/CMakeLists.txt index a54a4ff3..4fc803df 100644 --- a/src/MachO/CMakeLists.txt +++ b/src/MachO/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources(LIB_LIEF PRIVATE ChainedBindingInfo.cpp ChainedBindingInfoList.cpp ChainedFixup.cpp + ChainedPointerAnalysis.cpp CodeSignature.cpp CodeSignatureDir.cpp endianness_support.cpp @@ -19,6 +20,7 @@ target_sources(LIB_LIEF PRIVATE DataInCode.cpp DyldBindingInfo.cpp DyldChainedFixups.cpp + DyldChainedFixupsCreator.cpp DyldChainedFormat.cpp DyldEnvironment.cpp DyldExportsTrie.cpp diff --git a/src/MachO/ChainedBindingInfoList.cpp b/src/MachO/ChainedBindingInfoList.cpp index 53eada3c..84cd8d9d 100644 --- a/src/MachO/ChainedBindingInfoList.cpp +++ b/src/MachO/ChainedBindingInfoList.cpp @@ -14,13 +14,29 @@ * limitations under the License. */ #include "ChainedBindingInfoList.hpp" -#include "MachO/ChainedFixup.hpp" #include "LIEF/MachO/ChainedBindingInfo.hpp" namespace LIEF { namespace MachO { +std::unique_ptr + ChainedBindingInfoList::create(const ChainedBindingInfo& other) +{ + auto result = std::make_unique(other.format(), other.is_weak_import()); + result->segment_ = const_cast(other.segment()); + result->symbol_ = const_cast(other.symbol()); + result->library_ordinal_ = other.library_ordinal(); + result->addend_ = other.addend(); + result->library_ = const_cast(other.library()); + result->address_ = other.address(); + result->ptr_format_ = other.ptr_format(); + result->offset_ = other.offset(); + result->btypes_ = other.btypes_; + result->arm64_bind_ = other.arm64_bind_; + return result; +} + void ChainedBindingInfoList::swap(ChainedBindingInfoList& other) noexcept { ChainedBindingInfo::swap(other); std::swap(elements_, other.elements_); diff --git a/src/MachO/ChainedBindingInfoList.hpp b/src/MachO/ChainedBindingInfoList.hpp index 9b5e96cc..ede1df8c 100644 --- a/src/MachO/ChainedBindingInfoList.hpp +++ b/src/MachO/ChainedBindingInfoList.hpp @@ -17,24 +17,31 @@ #define LIEF_MACHO_CHAINED_BINDING_INFO_LIST_H #include +#include #include "LIEF/MachO/ChainedBindingInfo.hpp" namespace LIEF { namespace MachO { class BinaryParser; class Builder; +class DyldChainedFixupsCreator; class ChainedBindingInfoList : public ChainedBindingInfo { friend class BinaryParser; friend class Builder; + friend class DyldChainedFixupsCreator; public: + static std::unique_ptr + create(const ChainedBindingInfo& other); + ChainedBindingInfoList() = delete; explicit ChainedBindingInfoList(DYLD_CHAINED_FORMAT fmt, bool is_weak) : ChainedBindingInfo(fmt, is_weak) {} + ChainedBindingInfoList& operator=(ChainedBindingInfoList other) = delete; ChainedBindingInfoList(const ChainedBindingInfoList& other) = delete; diff --git a/src/MachO/ChainedPointerAnalysis.cpp b/src/MachO/ChainedPointerAnalysis.cpp new file mode 100644 index 00000000..334536fd --- /dev/null +++ b/src/MachO/ChainedPointerAnalysis.cpp @@ -0,0 +1,517 @@ +/* Copyright 2017 - 2024 R. Thomas + * Copyright 2017 - 2024 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 + +#include "LIEF/BinaryStream/BinaryStream.hpp" +#include "LIEF/MachO/ChainedPointerAnalysis.hpp" +#include + +namespace LIEF::MachO { +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t& chain) +{ + os << fmt::format("target: 0x{:010x} high8: 0x{:02x}, next: 0x{:03x}, " + "bind: {}, auth: {}", + chain.target, chain.high8, chain.next, (bool)chain.bind, (bool)chain.auth + ); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind_t& chain) +{ + os << fmt::format("ordinal: 0x{:04x} zero: 0x{:04x}, addend: 0x{:05x}, " + "next: 0x{:03x} bind: {}, auth: {}", + chain.ordinal, chain.zero, chain.addend, chain.next, + (bool)chain.bind, (bool)chain.auth + ); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_rebase_t& chain) +{ + os << fmt::format("target: 0x{:08x} diversity: 0x{:04x}, addr_div: {}, " + "key: 0x{:x} next: 0x{:03x} bind: {}, auth: {}", + chain.target, chain.diversity, chain.addr_div, chain.key, chain.next, + (bool)chain.bind, (bool)chain.auth + ); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind_t& chain) +{ + os << fmt::format("ordinal: 0x{:04x} zero: 0x{:04x}, diversity: 0x{:04x}, " + "addr_div: {} key: 0x{:x} next: 0x{:03x} bind: {}, auth: {}", + chain.ordinal, chain.zero, chain.diversity, chain.addr_div, chain.key, + chain.next, (bool)chain.bind, (bool)chain.auth + ); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t& chain) +{ + os << fmt::format("target: 0x{:010x} high8: 0x{:02x}, reserved: 0x{:02x}, " + "next: 0x{:04x} bind: {}", + chain.target, chain.high8, chain.reserved, chain.next, (bool)chain.bind); + return os; +} +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_bind24_t& chain) +{ + + os << fmt::format("ordinal: 0x{:06x} zero: 0x{:02x}, addend: 0x{:05x}, " + "next: 0x{:03x} bind: {}, auth: {}", + chain.ordinal, chain.zero, chain.addend, chain.next, (bool)chain.bind, + (bool)chain.auth); + return os; +} +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_bind24_t& chain) +{ + os << fmt::format("ordinal: 0x{:06x} zero: 0x{:02x}, diversity: 0x{:04x}, " + "addr_div: {}, key: 0x{:x}, next: 0x{:03x} bind: {}, auth: {}", + chain.ordinal, chain.zero, chain.diversity, chain.addr_div, chain.key, + chain.next, (bool)chain.bind, (bool)chain.auth); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t& chain) +{ + os << fmt::format("ordinal: 0x{:06x} addend: 0x{:04x}, reserved: 0x{:05x}, " + "next: 0x{:04x} bind: {}", + chain.ordinal, chain.addend, chain.reserved, chain.next, (bool)chain.bind); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_64_kernel_cache_rebase_t& chain) +{ + os << fmt::format("target: 0x{:08x} cache_level: {}, diversity: 0x{:04x}, " + "addr_div: {} key: {} next: 0x{:03x}, auth: {}", + chain.target, chain.cache_level, chain.diversity, chain.addr_div, + chain.key, chain.next, (bool)chain.is_auth); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_32_rebase_t& chain) +{ + os << fmt::format("target: 0x{:08x} next: 0x{:02x}, bind: {}", + chain.target, chain.next, (bool)chain.bind); + return os; +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::dyld_chained_ptr_32_bind_t& chain) +{ + os << fmt::format("ordinal: 0x{:05x} addend: 0x{:02x}, next: 0x{:x}, bind: {}", + chain.ordinal, chain.addend, chain.next, (bool)chain.bind); + return 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}}", + chain.target, chain.next); + return 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}}", + chain.target, chain.next); + return os; +} + +template +ChainedPointerAnalysis::union_pointer_t create_impl(const T& value) { + ChainedPointerAnalysis::union_pointer_t out; + out.raw = 0; + out.type = ChainedPointerAnalysis::PTR_TYPE::UNKNOWN; + + if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE; + out.arm64e_rebase = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND; + out.arm64e_bind = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE; + out.arm64e_auth_rebase = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND; + out.arm64e_auth_bind = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE; + out.ptr_64_rebase = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24; + out.arm64e_bind24 = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24; + out.arm64e_auth_bind24 = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_BIND; + out.ptr_64_bind = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE; + out.ptr_64_kernel_cache_rebase = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE; + out.ptr_32_rebase = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_BIND; + out.ptr_32_bind = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE; + out.ptr_32_cache_rebase = value; + } + + else if constexpr (std::is_same_v) + { + out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE; + out.ptr_32_firmware_rebase = value; + } + + return out; +} + + +ChainedPointerAnalysis::union_pointer_t ChainedPointerAnalysis::get_as(DYLD_CHAINED_PTR_FORMAT fmt) const { + switch (fmt) { + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24: + { + if ((bool)dyld_chained_ptr_arm64e_auth_rebase().auth && + (bool)dyld_chained_ptr_arm64e_auth_rebase().bind) + { + if (fmt == DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24) { + return create_impl(dyld_chained_ptr_arm64e_auth_bind24()); + } + return create_impl(dyld_chained_ptr_arm64e_auth_bind()); + } + + if ( dyld_chained_ptr_arm64e_auth_rebase().auth && + !dyld_chained_ptr_arm64e_auth_rebase().bind) + { + return create_impl(dyld_chained_ptr_arm64e_auth_rebase()); + } + if (!dyld_chained_ptr_arm64e_auth_rebase().auth && + dyld_chained_ptr_arm64e_auth_rebase().bind) + { + if (fmt == DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24) { + return create_impl(dyld_chained_ptr_arm64e_bind24()); + } + return create_impl(dyld_chained_ptr_arm64e_bind()); + } + + if (!dyld_chained_ptr_arm64e_auth_rebase().auth && + !dyld_chained_ptr_arm64e_auth_rebase().bind) + { + return create_impl(dyld_chained_ptr_arm64e_rebase()); + } + + return {}; + } + + case DYLD_CHAINED_PTR_FORMAT::PTR_64: + case DYLD_CHAINED_PTR_FORMAT::PTR_64_OFFSET: + { + if (dyld_chained_ptr_64_rebase().bind) { + return create_impl(dyld_chained_ptr_64_bind()); + } + return create_impl(dyld_chained_ptr_64_rebase()); + } + + case DYLD_CHAINED_PTR_FORMAT::PTR_32: + { + if (dyld_chained_ptr_32_bind().bind) { + return create_impl(dyld_chained_ptr_32_bind()); + } + return create_impl(dyld_chained_ptr_32_rebase()); + } + + case DYLD_CHAINED_PTR_FORMAT::PTR_32_CACHE: + return create_impl(dyld_chained_ptr_32_cache_rebase()); + + case DYLD_CHAINED_PTR_FORMAT::PTR_32_FIRMWARE: + return create_impl(dyld_chained_ptr_32_firmware_rebase()); + + case DYLD_CHAINED_PTR_FORMAT::PTR_64_KERNEL_CACHE: + return create_impl(dyld_chained_ptr_64_kernel_cache_rebase()); + + default: + return {}; + } + return {}; +} + +uint32_t ChainedPointerAnalysis::union_pointer_t::next() const { + switch (type) { + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE: + return arm64e_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND: + return arm64e_bind.next; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE: + return arm64e_auth_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND: + return arm64e_auth_bind.next; + case PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE: + return ptr_64_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24: + return arm64e_bind24.next; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: + return arm64e_auth_bind24.next; + case PTR_TYPE::DYLD_CHAINED_PTR_64_BIND: + return ptr_64_bind.next; + case PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE: + return ptr_64_kernel_cache_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE: + return ptr_32_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_32_BIND: + return ptr_32_bind.next; + case PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE: + return ptr_32_cache_rebase.next; + case PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: + return ptr_32_firmware_rebase.next; + case PTR_TYPE::UNKNOWN: + return 0; + } + return 0; +} + +result ChainedPointerAnalysis::union_pointer_t::ordinal() const { + switch (type) { + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND: + return arm64e_bind.ordinal; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND: + return arm64e_auth_bind.ordinal; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24: + return arm64e_bind24.ordinal; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: + return arm64e_auth_bind24.ordinal; + case PTR_TYPE::DYLD_CHAINED_PTR_64_BIND: + return ptr_64_bind.ordinal; + case PTR_TYPE::DYLD_CHAINED_PTR_32_BIND: + return ptr_32_bind.ordinal; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE: + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE: + case PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE: + case PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE: + 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::UNKNOWN: + return make_error_code(lief_errors::not_found); + } + return make_error_code(lief_errors::not_found); +} + +bool ChainedPointerAnalysis::union_pointer_t::is_auth() const { + switch (type) { + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE: + return arm64e_rebase.auth; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND: + return arm64e_bind.auth; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE: + return arm64e_auth_rebase.auth; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND: + return arm64e_auth_bind.auth; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24: + return arm64e_bind24.auth; + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: + return arm64e_auth_bind24.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: + case PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE: + case PTR_TYPE::DYLD_CHAINED_PTR_32_BIND: + case PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE: + case PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: + case PTR_TYPE::UNKNOWN: + return false; + } + return false; +} + +result ChainedPointerAnalysis::union_pointer_t::target() const { + switch (type) { + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE: + return arm64e_rebase.unpack_target(); + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE: + return arm64e_auth_rebase.target; + case PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE: + return ptr_64_rebase.unpack_target(); + case PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE: + return ptr_64_kernel_cache_rebase.target; + case PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE: + return ptr_32_rebase.target; + case PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE: + 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_32_BIND: + case PTR_TYPE::DYLD_CHAINED_PTR_64_BIND: + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND: + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24: + case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND: + case PTR_TYPE::UNKNOWN: + return make_error_code(lief_errors::not_found); + } + return make_error_code(lief_errors::not_found); +} + +std::ostream& operator<<(std::ostream& os, + const ChainedPointerAnalysis::union_pointer_t& ptr) +{ + switch (ptr.type) { + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE: + os << ptr.arm64e_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND: + os << ptr.arm64e_bind; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE: + os << ptr.arm64e_auth_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND: + os << ptr.arm64e_auth_bind; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE: + os << ptr.ptr_64_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24: + os << ptr.arm64e_bind24; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24: + os << ptr.arm64e_auth_bind24; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_BIND: + os << ptr.ptr_64_bind; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE: + os << ptr.ptr_64_kernel_cache_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE: + os << ptr.ptr_32_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_BIND: + os << ptr.ptr_32_bind; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE: + os << ptr.ptr_32_cache_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE: + os << ptr.ptr_32_firmware_rebase; + break; + case ChainedPointerAnalysis::PTR_TYPE::UNKNOWN: + break; + } + return os; +} + +uint64_t ChainedPointerAnalysis::walk_chain( + BinaryStream& stream, DYLD_CHAINED_PTR_FORMAT format, + const std::function& callback) +{ + const size_t ptr_sizeof = ChainedPointerAnalysis::ptr_size(format); + const size_t stride = ChainedPointerAnalysis::stride(format); + assert(ptr_sizeof == 8 || ptr_sizeof == 4); + const uint64_t start_pos = stream.pos(); + while (1) { + uint64_t value = ptr_sizeof == sizeof(uint64_t) ? + stream.peek().value_or(0) : + stream.peek().value_or(0); + if (value == 0) { + break; + } + ChainedPointerAnalysis analysis(value, ptr_sizeof); + union_pointer_t ptr = analysis.get_as(format); + const uint64_t offset = stream.pos(); + if (callback(offset, ptr)) { + return start_pos - stream.pos(); + } + + const uint32_t next = ptr.next() * stride; + if (next == 0) { + break; + } + + if (!stream.can_read(stream.pos() + next, ptr_sizeof)) { + break; + } + + stream.increment_pos(next); + } + return stream.pos() - start_pos; +} +} diff --git a/src/MachO/DyldChainedFixups.cpp b/src/MachO/DyldChainedFixups.cpp index 607d0d95..380b4404 100644 --- a/src/MachO/DyldChainedFixups.cpp +++ b/src/MachO/DyldChainedFixups.cpp @@ -143,12 +143,6 @@ DyldChainedFixups::chained_starts_in_segment::chained_starts_in_segment(uint32_t segment{segment} {} -DyldChainedFixups::chained_starts_in_segment::chained_starts_in_segment(uint32_t offset, SegmentCommand& segment) : - offset{offset}, - segment{segment} -{} - - std::ostream& operator<<(std::ostream& os, const DyldChainedFixups::chained_starts_in_segment& info) { os << fmt::format("size = {}\n", info.size); os << fmt::format("page_size = 0x{:x}\n", info.page_size); diff --git a/src/MachO/DyldChainedFixupsCreator.cpp b/src/MachO/DyldChainedFixupsCreator.cpp new file mode 100644 index 00000000..369e66cf --- /dev/null +++ b/src/MachO/DyldChainedFixupsCreator.cpp @@ -0,0 +1,417 @@ +/* Copyright 2017 - 2024 R. Thomas + * Copyright 2017 - 2024 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 + +#include "LIEF/MachO/Binary.hpp" +#include "LIEF/MachO/Symbol.hpp" +#include "LIEF/MachO/RelocationFixup.hpp" +#include "LIEF/MachO/DylibCommand.hpp" +#include "LIEF/MachO/ChainedPointerAnalysis.hpp" +#include "LIEF/MachO/SegmentCommand.hpp" +#include "LIEF/MachO/ChainedBindingInfo.hpp" +#include "LIEF/MachO/DyldChainedFixups.hpp" +#include "LIEF/MachO/DyldChainedFixupsCreator.hpp" + +#include "MachO/ChainedBindingInfoList.hpp" +#include "MachO/ChainedFixup.hpp" + +#include "MachO/Structures.hpp" + +#include "logging.hpp" + +namespace LIEF::MachO { + +struct strong_symbol_t { + std::string symbol; + size_t libord = 0; + friend bool operator==(const strong_symbol_t& lhs, const strong_symbol_t& rhs) { + return lhs.libord == rhs.libord && lhs.symbol == rhs.symbol; + } +}; + +class StrongSymbolHash { + public: + size_t operator()(const strong_symbol_t& sym) const noexcept{ + return std::hash{}(sym.symbol + std::to_string(sym.libord)); + } +}; + +class strong_map_t : public std::unordered_map< + strong_symbol_t, ChainedBindingInfoList*, StrongSymbolHash +> +{}; + +uint64_t DyldChainedFixupsCreator::binding_rebase_t::addr() const { + return type == binding_rebase_t::BINDING ? + binding->address() : fixup->address(); +} + + +DYLD_CHAINED_PTR_FORMAT + DyldChainedFixupsCreator::pointer_format(const Binary& bin, size_t imp_count) +{ + if (imp_count > MAX_IMPORTS) { + LIEF_ERR("Too many imports (0x{:06x})", imp_count); + return DYLD_CHAINED_PTR_FORMAT::NONE; + } + // Logic taken from ld64 -- OutputFile::chainedPointerFormat() + const Header::CPU_TYPE arch = bin.header().cpu_type(); + if (arch == Header::CPU_TYPE::ARM64) { + if (bin.support_arm64_ptr_auth()) { + if (imp_count > BIND24_THRESHOLD) { + return DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24; + } + return DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E; + } + } + return DYLD_CHAINED_PTR_FORMAT::PTR_64; +} + +result DyldChainedFixupsCreator::lib2ord( + const Binary& bin, const Symbol& sym, const std::string& lib) +{ + { + int ord = sym.library_ordinal(); + if (Symbol::is_valid_index_ordinal(ord)) { + return sym.library_ordinal(); + } + if (lib.empty()) { + return ord; + } + } + + + if (auto it = lib2ord_.find(lib); it != lib2ord_.end()) { + return it->second; + } + + if (lib.empty()) { + LIEF_ERR("No library for the symbol: '{}'", sym.name()); + return make_error_code(lief_errors::not_found); + } + + auto libs = bin.libraries(); + + auto it = std::find_if(libs.begin(), libs.end(), + [&lib] (const DylibCommand& dylib) { + return dylib.name() == lib; + } + ); + + if (it == libs.end()) { + LIEF_ERR("Library '{}' not found!", lib); + return make_error_code(lief_errors::not_found); + } + + size_t pos = std::distance(libs.begin(), it); + lib2ord_.insert({lib, pos}); + return pos; +} + + +const Symbol* DyldChainedFixupsCreator::find_symbol( + const Binary& bin, const std::string& name) +{ + for (const Symbol& sym : bin.symbols()) { + if (sym.name() == name) { + return &sym; + } + } + return nullptr; +} + +DyldChainedFixupsCreator& DyldChainedFixupsCreator::add_binding( + uint64_t address, std::string symbol, std::string library, + uint64_t addend, bool weak) +{ + bindings_.push_back({ + address, std::move(library), std::move(symbol), weak, addend + }); + return *this; +} + + +ok_error_t DyldChainedFixupsCreator::process_relocations( + Binary& target, DYLD_CHAINED_PTR_FORMAT ptr_fmt) +{ + const uint64_t imagebase = target.imagebase(); + for (const reloc_info_t& info : relocations_) { + auto fixup = std::make_unique(ptr_fmt, target.imagebase()); + fixup->address_ = info.addr; + SegmentCommand* segment = target.segment_from_virtual_address(fixup->address()); + if (segment == nullptr) { + LIEF_WARN("Can't find segment for the relocation: 0x{:016x}", fixup->address()); + continue; + } + + switch (ptr_fmt) { + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E: + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24: + { + details::dyld_chained_ptr_arm64e_rebase raw; + std::memset(&raw, 0, sizeof(raw)); + details::pack_target(raw, info.target - imagebase); + fixup->set(raw); + break; + } + case DYLD_CHAINED_PTR_FORMAT::PTR_64: + { + details::dyld_chained_ptr_64_rebase raw; + std::memset(&raw, 0, sizeof(raw)); + details::pack_target(raw, info.target - imagebase); + fixup->set(raw); + break; + } + default: + { + LIEF_WARN("Chained ptr format: {} is not supported", to_string(ptr_fmt)); + break; + } + } + segment_chains_[segment].emplace_back(*fixup); + segment->relocations_.push_back(std::move(fixup)); + } + return ok(); +} + +ok_error_t DyldChainedFixupsCreator::process_bindings( + Binary& target, strong_map_t& strong_map, + std::unordered_map& symbols_idx, + DyldChainedFixups* cmd, + DyldChainedFixups::binding_info_t& all_bindings +) +{ + for (const binding_info_t& info : bindings_) { + const Symbol* sym = find_symbol(target, info.symbol); + + if (sym == nullptr) { + LIEF_ERR("Can't resolve symbol: '{}", info.symbol); + continue; + } + + result libord = lib2ord(target, *sym, info.library); + if (!libord) { + continue; + } + + SegmentCommand* segment = target.segment_from_virtual_address(info.address); + if (segment == nullptr) { + LIEF_ERR("Can't find segment associated with address: 0x{:016x}", info.address); + continue; + } + + const size_t ord = *libord; + + auto binding = std::make_unique(imports_format_, info.weak); + binding->symbol_ = const_cast(sym); + binding->library_ordinal_ = ord; + binding->addend_ = info.addend; + binding->address_ = info.address; + binding->segment_ = segment; + + auto libraries = target.libraries(); + + if (ord > 0 && ord < libraries.size()) { + binding->library_ = &libraries[ord - 1]; + } + + segment_chains_[segment].emplace_back(*binding); + strong_symbol_t strong_sym{sym->name(), ord}; + auto it = strong_map.find(strong_sym); + if (it != strong_map.end()) { + it->second->elements_.push_back(binding.get()); + } else { + symbols_idx.insert({info.symbol, cmd->internal_bindings_.size()}); + auto binding_list = ChainedBindingInfoList::create(*binding); + binding_list->elements_.push_back(binding.get()); + strong_map.insert({strong_sym, binding_list.get()}); + cmd->internal_bindings_.push_back(std::move(binding_list)); + } + + all_bindings.push_back(std::move(binding)); + } + return ok(); +} + +DyldChainedFixups* DyldChainedFixupsCreator::create(Binary& target) { + const uint64_t imagebase = target.imagebase(); + auto cmd = std::make_unique(); + cmd->command_ = LoadCommand::TYPE::DYLD_CHAINED_FIXUPS; + cmd->size_ = sizeof(details::linkedit_data_command); + + cmd->fixups_version_ = fixups_version_; + cmd->symbols_format_ = /* uncompressed */0; + cmd->imports_format_ = DYLD_CHAINED_FORMAT::IMPORT; + + DyldChainedFixups::binding_info_t all_bindings; + all_bindings.reserve(bindings_.size()); + + std::unordered_map symbols_idx; + strong_map_t strong_map; + if (!process_bindings(target, strong_map, symbols_idx, cmd.get(), all_bindings)) { + return nullptr; + } + + const size_t import_count = cmd->internal_bindings_.size(); + const DYLD_CHAINED_PTR_FORMAT import_ptr_fmt = pointer_format(target, import_count); + + if (!process_relocations(target, import_ptr_fmt)) { + return nullptr; + } + + for (auto& [k, v] : segment_chains_) { + std::sort(v.begin(), v.end()); + } + + const uint32_t page_size = target.page_size(); + + for (SegmentCommand& segment : target.segments()) { + LIEF_DEBUG("Processing segment: {}", segment.name()); + DyldChainedFixups::chained_starts_in_segment info(/*offset=*/0, segment); + info.page_size = page_size; + info.pointer_format = import_ptr_fmt; + + const size_t stride = ChainedPointerAnalysis::stride(info.pointer_format); + + auto it = segment_chains_.find(&segment); + if (it != segment_chains_.end()) { + std::vector& chains = it->second; + LIEF_DEBUG(" {}: #{} values", segment.name(), chains.size()); + const uint64_t segment_offset = segment.virtual_address() - imagebase; + + for (size_t i = 0; i < chains.size(); ++i) { + uint32_t next = 0; + binding_rebase_t& element = chains[i]; + //LIEF_DEBUG(" 0x{:016x}: {}", binding->address(), binding->symbol()->name()); + uint64_t address = element.addr(); + uint64_t target_offset = address - imagebase; + LIEF_DEBUG(" address: 0x{:016x}", address); + LIEF_DEBUG(" target offset: 0x{:016x}", target_offset); + LIEF_DEBUG(" segment offset: 0x{:016x}", segment_offset); + assert (target_offset >= segment_offset); + uint64_t offset = target_offset - segment_offset; + + uint32_t page_idx = offset / page_size; + uint32_t delta = offset % page_size; + LIEF_DEBUG(" page_idx: {}", page_idx); + LIEF_DEBUG(" delta: 0x{:016x}", delta); + LIEF_DEBUG(" offset: 0x{:016x}" , offset); + + if (i < chains.size() - 1) { + binding_rebase_t& next_element = chains[i + 1]; + uint64_t next_address = next_element.addr(); + uint64_t next_target_offset = next_address - imagebase; + uint64_t next_offset = next_target_offset - segment_offset; + assert (next_target_offset >= segment_offset); + + uint32_t next_page_idx = next_offset / page_size; + uint32_t next_delta = next_offset % page_size; + LIEF_DEBUG(" [next]address: 0x{:016x}", address); + LIEF_DEBUG(" [next]target offset: 0x{:016x}", next_target_offset); + + LIEF_DEBUG(" [next]page_idx: {}", next_page_idx); + LIEF_DEBUG(" [next]delta: 0x{:016x}", next_delta); + LIEF_DEBUG(" [next]offset: 0x{:016x}" , next_offset); + + if (next_page_idx == page_idx) { + assert((next_delta - delta) % stride == 0); + next = (next_delta - delta) / stride; + } + } + LIEF_DEBUG(" next: {} (stride: {})", next, stride); + + if (page_idx >= info.page_start.size()) { + info.page_start.resize(page_idx + 1, /* DYLD_CHAINED_PTR_START_NONE */0xFFFF); + info.page_start[page_idx] = delta; + } + if (element.is_binding()) { + const uint16_t ordinal = symbols_idx.at(element.binding->symbol()->name()); + switch (import_ptr_fmt) { + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_USERLAND24: + { + details::dyld_chained_ptr_arm64e_bind24 raw; + std::memset(&raw, 0, sizeof(raw)); + raw.bind = 1; + raw.next = next; + raw.ordinal = ordinal; + element.binding->set(raw); + break; + } + + case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E: + { + details::dyld_chained_ptr_arm64e_bind raw; + std::memset(&raw, 0, sizeof(raw)); + raw.bind = 1; + raw.next = next; + raw.ordinal = ordinal; + element.binding->set(raw); + break; + } + + case DYLD_CHAINED_PTR_FORMAT::PTR_64: + { + details::dyld_chained_ptr_64_bind raw; + std::memset(&raw, 0, sizeof(raw)); + raw.bind = 1; + raw.next = next; + raw.ordinal = ordinal; + element.binding->set(raw); + break; + } + default: + { + LIEF_WARN("Chained ptr format: {} is not supported", to_string(import_ptr_fmt)); + break; + } + } + + element.binding->offset( + (element.binding->address() - segment.virtual_address()) + + segment.file_offset() + ); + } else { + assert(element.is_fixup()); + element.fixup->next(next); + element.fixup->offset( + (element.binding->address() - segment.virtual_address()) + + segment.file_offset() + ); + } + } + } + LIEF_DEBUG("Adding chained_starts_in_segment[{}] ({} pages)", + info.segment.name(), info.page_count()); + cmd->chained_starts_in_segment_.push_back(info); + } + + cmd->all_bindings_ = std::move(all_bindings); + LoadCommand* added = target.add(std::move(cmd)); + if (added == nullptr) { + return nullptr; + } + return static_cast(added); +} + + +DyldChainedFixupsCreator& + DyldChainedFixupsCreator::add_relocation(uint64_t address, uint64_t target) +{ + relocations_.push_back({address, target}); + return *this; +} + +} diff --git a/tests/unittests/test_macho.cpp b/tests/unittests/test_macho.cpp index d1601921..94d98db4 100644 --- a/tests/unittests/test_macho.cpp +++ b/tests/unittests/test_macho.cpp @@ -17,14 +17,36 @@ #include #include +#include "LIEF/logging.hpp" +#include "LIEF/MachO/FatBinary.hpp" #include "LIEF/MachO/Binary.hpp" +#include "LIEF/MachO/Relocation.hpp" +#include "LIEF/MachO/RelocationFixup.hpp" +#include "LIEF/MachO/Section.hpp" +#include "LIEF/MachO/Symbol.hpp" +#include "LIEF/MachO/Parser.hpp" +#include "LIEF/MachO/DyldChainedFixupsCreator.hpp" +#include "LIEF/MachO/DylibCommand.hpp" +#include "LIEF/MachO/ChainedBindingInfo.hpp" +#include "LIEF/MachO/DyldChainedFixups.hpp" + #include "LIEF/Abstract/Parser.hpp" +#include "LIEF/BinaryStream/SpanStream.hpp" #include "utils.hpp" using namespace LIEF; using namespace std::string_literals; +std::unique_ptr reload(MachO::Binary& bin) { + std::ostringstream oss; + bin.write(oss); + std::string buffer = std::move(oss.str()); + + auto stream = std::make_unique((const uint8_t*)buffer.data(), buffer.size()); + return MachO::Parser::parse(std::move(stream))->take(0); +} + TEST_CASE("lief.test.macho", "[lief][test][macho]") { SECTION("classof") { { @@ -33,6 +55,68 @@ TEST_CASE("lief.test.macho", "[lief][test][macho]") { REQUIRE(LIEF::MachO::Binary::classof(bin.get())); } } + + SECTION("DyldChainedFixupsCreator") { + { + SECTION("Simple Imports") { + std::string path = test::get_macho_sample("liblog_srp.dylib"); + std::unique_ptr bin = MachO::Parser::parse(path)->take(0); + REQUIRE(bin != nullptr); + + uint64_t __auth_got = bin->get_section("__auth_got")->virtual_address(); + MachO::DyldChainedFixupsCreator() + .add_binding(__auth_got + 0 * sizeof(uint64_t), "_calloc") + .add_binding(__auth_got + 4 * sizeof(uint64_t), "_dlopen") + .create(*bin); + std::unique_ptr new_bin = reload(*bin); + const MachO::DyldChainedFixups* fixups = new_bin->dyld_chained_fixups(); + REQUIRE(fixups != nullptr); + auto bindings = fixups->bindings(); + REQUIRE(bindings.size() == 2); + CHECK(bindings[0].address() == __auth_got); + CHECK(bindings[0].symbol()->name() == "_calloc"); + CHECK(bindings[0].library()->name() == "/usr/lib/libSystem.B.dylib"); + + CHECK(bindings[1].address() == __auth_got + 4 * sizeof(uint64_t)); + CHECK(bindings[1].symbol()->name() == "_dlopen"); + CHECK(bindings[1].library()->name() == "/usr/lib/libSystem.B.dylib"); + } + + SECTION("Imports with relocations") { + std::string path = test::get_macho_sample("liblog_srp.dylib"); + std::unique_ptr bin = MachO::Parser::parse(path)->take(0); + REQUIRE(bin != nullptr); + + uint64_t __auth_got = bin->get_section("__auth_got")->virtual_address(); + { + logging::Scoped Scope(logging::LEVEL::WARN); + MachO::DyldChainedFixupsCreator() + .add_binding (__auth_got + 0 * sizeof(uint64_t), "_calloc") + .add_relocation(__auth_got + 2 * sizeof(uint64_t), bin->imagebase() + 0xdeadc0de) + .add_binding (__auth_got + 4 * sizeof(uint64_t), "_dlopen") + .create(*bin); + } + std::unique_ptr new_bin = reload(*bin); + const MachO::DyldChainedFixups* fixups = new_bin->dyld_chained_fixups(); + REQUIRE(fixups != nullptr); + auto bindings = fixups->bindings(); + REQUIRE(bindings.size() == 2); + CHECK(bindings[0].address() == __auth_got); + CHECK(bindings[0].symbol()->name() == "_calloc"); + CHECK(bindings[0].library()->name() == "/usr/lib/libSystem.B.dylib"); + + CHECK(bindings[1].address() == __auth_got + 4 * sizeof(uint64_t)); + CHECK(bindings[1].symbol()->name() == "_dlopen"); + CHECK(bindings[1].library()->name() == "/usr/lib/libSystem.B.dylib"); + auto relocations = new_bin->relocations(); + REQUIRE(relocations.size() == 1); + auto& fixup = static_cast(relocations[0]); + + CHECK(fixup.address() == __auth_got + 2 * sizeof(uint64_t)); + CHECK(fixup.target() - new_bin->imagebase() == 0xdeadc0de); + } + } + } }