diff options
| author | Mark Rowe <mark@vector35.com> | 2025-09-21 17:33:20 -0700 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2025-10-22 08:57:04 -0700 |
| commit | 2f531f7279a01810494cdde6342434077456edf0 (patch) | |
| tree | a127b7c537b5e9992bb87b275f0ddc13a1e85436 /view/macho/chained_fixups.h | |
| parent | 4c8fa3972ad50c07631418620eb9be44d629b2ae (diff) | |
[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.
Diffstat (limited to 'view/macho/chained_fixups.h')
| -rw-r--r-- | view/macho/chained_fixups.h | 115 |
1 files changed, 115 insertions, 0 deletions
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 <functional> +#include <stdint.h> +#include <string_view> +#include <vector> + +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<BinaryView> raw, Ref<Logger> logger, uint64_t machOStartOffset, uint64_t preferredLoadAddress, + const linkedit_data_command& chainedFixupCommand, std::unordered_map<uint64_t, uint64_t> 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<ImportEntry> 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<void(uint64_t offset, const FixupInfo&)> 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<std::pair<uint64_t, uint16_t>> 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<BinaryView> m_raw; + Ref<Logger> 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<uint64_t, uint64_t> m_segmentVMAddrToFileOffset; + + mutable std::function<void(uint64_t offset, const FixupInfo&)> m_fixupHandler; + mutable std::vector<char> m_symbolData; +}; + +} // namespace BinaryNinja |
