/* Copyright 2017 - 2026 R. Thomas
* Copyright 2017 - 2026 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 <cassert>
#include "LIEF/BinaryStream/BinaryStream.hpp"
#include "LIEF/MachO/ChainedPointerAnalysis.hpp"
#include <spdlog/fmt/fmt.h>
namespace LIEF::MachO {
std::ostream& operator<<(
std::ostream& os,
const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t& chain
) {
os << fmt::format("target: {:#012x} high8: {:#04x}, next: {:#05x}, "
"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: {:#06x} zero: {:#06x}, addend: {:#07x}, "
"next: {:#05x} 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: {:#010x} diversity: {:#06x}, addr_div: {}, "
"key: {:#x} next: {:#05x} 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: {:#06x} zero: {:#06x}, diversity: {:#06x}, "
"addr_div: {} key: {:#x} next: {:#05x} 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: {:#012x} high8: {:#04x}, reserved: {:#04x}, "
"next: {:#06x} 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: {:#08x} zero: {:#04x}, addend: {:#07x}, "
"next: {:#05x} 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: {:#08x} zero: {:#04x}, diversity: {:#06x}, "
"addr_div: {}, key: {:#x}, next: {:#05x} 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: {:#08x} addend: {:#06x}, reserved: {:#07x}, "
"next: {:#06x} 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: {:#010x} cache_level: {}, diversity: {:#06x}, "
"addr_div: {} key: {} next: {:#05x}, 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: {:#010x} next: {:#04x}, 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: {:#07x} addend: {:#04x}, next: {:#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: {:#08x}, next: {:#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: {:#08x}, next: {:#x}", chain.target, chain.next);
return os;
}
std::ostream& operator<<(
std::ostream& os,
const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_segmented_rebase_t& chain
) {
os << fmt::format("segment offset: {:#08x}, segment index: {}, next: {:#x}",
chain.target_seg_offset, chain.target_seg_index, chain.next);
return os;
}
std::ostream& operator<<(
std::ostream& os,
const ChainedPointerAnalysis::dyld_chained_ptr_arm64e_auth_segmented_rebase_t&
chain
) {
os << fmt::format("segment offset: {:#08x}, segment index: {}, next: {:#x}, "
"addr_div: {} key: {} diversity: {}, auth: {}",
chain.target_seg_offset, chain.target_seg_index, chain.next,
chain.addr_div, chain.key, chain.diversity, chain.auth);
return os;
}
template<class T>
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<
T, ChainedPointerAnalysis::dyld_chained_ptr_arm64e_rebase_t
>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_REBASE;
out.arm64e_rebase = value;
} else if constexpr (std::is_same_v<T, ChainedPointerAnalysis::
dyld_chained_ptr_arm64e_bind_t>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND;
out.arm64e_bind = value;
} else if constexpr (std::is_same_v<T,
ChainedPointerAnalysis::
dyld_chained_ptr_arm64e_auth_rebase_t>)
{
out.type =
ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_REBASE;
out.arm64e_auth_rebase = value;
} else if constexpr (std::is_same_v<T, ChainedPointerAnalysis::
dyld_chained_ptr_arm64e_auth_bind_t>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND;
out.arm64e_auth_bind = value;
} else if constexpr (std::is_same_v<
T, ChainedPointerAnalysis::dyld_chained_ptr_64_rebase_t
>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE;
out.ptr_64_rebase = value;
} else if constexpr (std::is_same_v<T, ChainedPointerAnalysis::
dyld_chained_ptr_arm64e_bind24_t>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_BIND24;
out.arm64e_bind24 = value;
} else if constexpr (std::is_same_v<T,
ChainedPointerAnalysis::
dyld_chained_ptr_arm64e_auth_bind24_t>)
{
out.type =
ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24;
out.arm64e_auth_bind24 = value;
} else if constexpr (std::is_same_v<
T, ChainedPointerAnalysis::dyld_chained_ptr_64_bind_t
>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_64_BIND;
out.ptr_64_bind = value;
} else if constexpr (std::is_same_v<
T, ChainedPointerAnalysis::
dyld_chained_ptr_64_kernel_cache_rebase_t
>)
{
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<
T, ChainedPointerAnalysis::dyld_chained_ptr_32_rebase_t
>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_REBASE;
out.ptr_32_rebase = value;
} else if constexpr (std::is_same_v<
T, ChainedPointerAnalysis::dyld_chained_ptr_32_bind_t
>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_BIND;
out.ptr_32_bind = value;
} else if constexpr (std::is_same_v<T, ChainedPointerAnalysis::
dyld_chained_ptr_32_cache_rebase_t>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_CACHE_REBASE;
out.ptr_32_cache_rebase = value;
} else if constexpr (std::is_same_v<T,
ChainedPointerAnalysis::
dyld_chained_ptr_32_firmware_rebase_t>)
{
out.type =
ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_32_FIRMWARE_REBASE;
out.ptr_32_firmware_rebase = value;
} else if constexpr (std::is_same_v<
T, ChainedPointerAnalysis::
dyld_chained_ptr_arm64e_segmented_rebase_t
>)
{
out.type =
ChainedPointerAnalysis::PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE;
out.ptr_arm64e_segmented_rebase = value;
} else if constexpr (std::is_same_v<
T, ChainedPointerAnalysis::
dyld_chained_ptr_arm64e_auth_segmented_rebase_t
>)
{
out.type = ChainedPointerAnalysis::PTR_TYPE::
DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE;
out.ptr_arm64e_auth_segmented_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());
case DYLD_CHAINED_PTR_FORMAT::PTR_ARM64E_SEGMENTED:
{
if (dyld_chained_ptr_arm64e_segmented_rebase().auth) {
return create_impl(dyld_chained_ptr_arm64e_auth_segmented_rebase());
}
return create_impl(dyld_chained_ptr_arm64e_segmented_rebase());
}
default: return {};
}
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::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE:
return ptr_arm64e_segmented_rebase.next;
case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE:
return ptr_arm64e_auth_segmented_rebase.next;
case PTR_TYPE::UNKNOWN: return 0;
}
return 0;
}
result<uint32_t> 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::DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE:
case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE:
case PTR_TYPE::UNKNOWN: return make_error_code(lief_errors::not_found);
}
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_ARM64E_SEGMENTED_REBASE:
return ptr_arm64e_segmented_rebase.auth;
case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE:
return ptr_arm64e_auth_segmented_rebase.auth;
case PTR_TYPE::DYLD_CHAINED_PTR_64_REBASE:
case PTR_TYPE::DYLD_CHAINED_PTR_64_BIND:
case PTR_TYPE::DYLD_CHAINED_PTR_64_KERNEL_CACHE_REBASE:
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<uint64_t> 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_ARM64E_SEGMENTED_REBASE:
return ptr_arm64e_segmented_rebase.target_seg_offset;
case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE:
return ptr_arm64e_auth_segmented_rebase.target_seg_offset;
case PTR_TYPE::DYLD_CHAINED_PTR_32_BIND:
case PTR_TYPE::DYLD_CHAINED_PTR_64_BIND:
case PTR_TYPE::DYLD_CHAINED_PTR_ARM64E_AUTH_BIND24:
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::
DYLD_CHAINED_PTR_ARM64E_SEGMENTED_REBASE:
os << ptr.ptr_arm64e_segmented_rebase;
break;
case ChainedPointerAnalysis::PTR_TYPE::
DYLD_CHAINED_PTR_ARM64E_AUTH_SEGMENTED_REBASE:
os << ptr.ptr_arm64e_auth_segmented_rebase;
break;
case ChainedPointerAnalysis::PTR_TYPE::UNKNOWN: break;
}
return os;
}
uint64_t ChainedPointerAnalysis::walk_chain(
BinaryStream& stream, DYLD_CHAINED_PTR_FORMAT format,
const std::function<int(uint64_t, const union_pointer_t& ptr)>& 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 (true) {
uint64_t value = ptr_sizeof == sizeof(uint64_t) ?
stream.peek<uint64_t>().value_or(0) :
stream.peek<uint32_t>().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;
}
}