From 2f531f7279a01810494cdde6342434077456edf0 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Sun, 21 Sep 2025 17:33:20 -0700 Subject: [Mach-O] Refactor fixup chain parsing The parsing of fix-up chains is moved out of `MachoView` and into its own class. It deals purely in terms of offsets into the Mach-O slice. `MachoView` translates those offsets to mapped addresses when needed. This is primarily aimed at fixing incorrect handling of pointer formats that use offsets where in some cases the relocations would be applied at incorrect addresses due to confusion between file offsets, Mach-O slice offsets, and VM offsets. It incidentally fixes addends from bind operations not being respected. These show up most frequently in C++ RTTI information. --- view/macho/chained_fixups.h | 115 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 view/macho/chained_fixups.h (limited to 'view/macho/chained_fixups.h') diff --git a/view/macho/chained_fixups.h b/view/macho/chained_fixups.h new file mode 100644 index 00000000..9a26a1f5 --- /dev/null +++ b/view/macho/chained_fixups.h @@ -0,0 +1,115 @@ +#pragma once + +#include "machoview.h" + +#include +#include +#include +#include + +namespace BinaryNinja +{ + +enum class FixupType : uint8_t +{ + Bind, + Rebase, +}; + +enum class AuthKeyType : uint8_t +{ + IA, + IB, + DA, + DB, + None, +}; + +struct FixupInfo +{ + union + { + struct + { + uint32_t ordinal = 0; + int32_t addend = 0; + } bind; + struct + { + uint64_t target = 0; + } rebase; + }; + FixupType type : 1; + bool isAuthenticated : 1 = false; + AuthKeyType authKeyType : 3 = AuthKeyType::None; + bool usesAddressDiversity : 1 = false; + uint16_t addressDiversity = 0; + uint16_t next = 0; +}; + +struct ImportEntry +{ + std::string_view name; + uint64_t addend; + int32_t libraryOrdinal; + bool weakImport = false; +}; + +class ChainedFixupProcessor +{ +public: + ChainedFixupProcessor(Ref raw, Ref logger, uint64_t machOStartOffset, uint64_t preferredLoadAddress, + const linkedit_data_command& chainedFixupCommand, std::unordered_map segmentVMAddrToFileOffset) + : m_raw(std::move(raw)) + , m_logger(std::move(logger)) + , m_machOStartOffset(machOStartOffset) + , m_fixupsStartOffset(OffsetInRaw(chainedFixupCommand.dataoff)) + , m_fixupsSize(chainedFixupCommand.datasize) + , m_preferredLoadAddress(preferredLoadAddress) + , m_segmentVMAddrToFileOffset(std::move(segmentVMAddrToFileOffset)) + {} + + std::vector ProcessImports() const; + + // Calls the provided handler for each fixup found. `offset` is relative to `machOStartOffset`. + // + // Note that `FixupInfo` references data owned by this object and so should not be used outside + // of the handler function. + void ProcessFixups(std::function fixupHandler) const; + +private: + dyld_chained_fixups_header ReadHeader(BinaryReader&) const; + + void ProcessChainedFixups(const dyld_chained_fixups_header&, BinaryReader&) const; + void ProcessChainsInSegment(const dyld_chained_starts_in_segment&, BinaryReader&) const; + + // Returns a vector of pairs of (page index, offset in page) representing the start of each fixup chain. + std::vector> ReadChainStartsInSegment(const dyld_chained_starts_in_segment&, BinaryReader&) const; + + uint64_t OffsetInRaw(uint64_t offset) const { return m_machOStartOffset + offset; } + uint64_t OffsetInFixups(uint64_t offset) const { return m_fixupsStartOffset + offset; } + + Ref m_raw; + Ref m_logger; + + // Offset to the start of the Mach-O file within the BinaryView. + // This will be non-zero for Mach-O files within a universal binary. + uint64_t m_machOStartOffset; + + // Offset to the start of the chained fixups data within the BinaryView. + uint64_t m_fixupsStartOffset; + + // Total size of the chained fixups data. + uint64_t m_fixupsSize; + + // The preferred load address of the __TEXT segment. Used for translating + // address-based fixups into offset-based fixups. + uint64_t m_preferredLoadAddress; + + std::unordered_map m_segmentVMAddrToFileOffset; + + mutable std::function m_fixupHandler; + mutable std::vector m_symbolData; +}; + +} // namespace BinaryNinja -- cgit v1.3.1