From 2b3ce702bdaa825af2dbff45f7899b9ffd82a257 Mon Sep 17 00:00:00 2001 From: kat Date: Fri, 11 Jul 2025 09:44:37 -0400 Subject: [PATCH] [Mach-O] Handling for special library ordinals used in binding information --- view/macho/machoview.cpp | 100 +++++++++++++++++++++++++++++++-------- view/macho/machoview.h | 16 ++++++- 2 files changed, 96 insertions(+), 20 deletions(-) diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp index 77d8d8fe5..4e228a33d 100644 --- a/view/macho/machoview.cpp +++ b/view/macho/machoview.cpp @@ -2093,10 +2093,74 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_ if (objcProcessor) objcProcessor->AddRelocatedPointer(relocationLocation, slidTarget); } - for (auto& [relocation, name] : header.externalRelocations) + for (auto& [relocation, name, ordinal] : header.bindingRelocations) { - if (auto symbol = GetSymbolByRawName(name, GetExternalNameSpace()); symbol) - DefineRelocation(m_arch, relocation, symbol, relocation.address); + bool handled = false; + + switch (ordinal) + { + case BindSpecialDylibSelf: + if (auto symbol = GetSymbolByRawName(name, GetInternalNameSpace()); symbol) + { + DefineRelocation(m_arch, relocation, symbol, relocation.address); + if (objcProcessor) + objcProcessor->AddRelocatedPointer(relocation.address, symbol->GetAddress()); + handled = true; + } + break; + + case BindSpecialDylibMainExecutable: + case BindSpecialDylibFlatLookup: + case BindSpecialDylibWeakLookup: + // In cases where we are the primary executable, flat lookup should find us first, + // it seems like our best course of action is to try and find internally first on + // executables, and externally on libraries. + if (header.ident.filetype == MH_EXECUTE) + { + if (auto symbol = GetSymbolByRawName(name, GetInternalNameSpace()); symbol) + { + DefineRelocation(m_arch, relocation, symbol, relocation.address); + if (objcProcessor) + objcProcessor->AddRelocatedPointer(relocation.address, symbol->GetAddress()); + handled = true; + } + else if (auto symbol = GetSymbolByRawName(name, GetExternalNameSpace()); symbol) + { + DefineRelocation(m_arch, relocation, symbol, relocation.address); + handled = true; + } + } + else + { + if (auto symbol = GetSymbolByRawName(name, GetExternalNameSpace()); symbol) + { + DefineRelocation(m_arch, relocation, symbol, relocation.address); + handled = true; + } + else if (auto symbol = GetSymbolByRawName(name, GetInternalNameSpace()); symbol) + { + DefineRelocation(m_arch, relocation, symbol, relocation.address); + if (objcProcessor) + objcProcessor->AddRelocatedPointer(relocation.address, symbol->GetAddress()); + handled = true; + } + } + break; + + default: + if (ordinal > 0) + { + if (auto symbol = GetSymbolByRawName(name, GetExternalNameSpace()); symbol) + { + DefineRelocation(m_arch, relocation, symbol, relocation.address); + handled = true; + } + } + break; + } + + if (!handled) + m_logger->LogError("Failed to find external symbol '%s', couldn't bind symbol at 0x%llx", name.c_str(), relocation.address); } auto relocationHandler = m_arch->GetRelocationHandler("Mach-O"); @@ -2848,7 +2912,7 @@ void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNS BNRelocationInfo externReloc; BNSymbolType symtype = incomingType; - // uint64_t ordinal = 0; + uint64_t ordinal = 0; // int64_t addend = 0; uint64_t segmentIndex = 0; uint64_t address = 0; @@ -2866,7 +2930,7 @@ void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNS switch (opcode) { case BindOpcodeDone: - // ordinal = 0; + ordinal = 0; // addend = 0; segmentIndex = 0; address = 0; @@ -2876,9 +2940,9 @@ void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNS type = 0; symtype = incomingType; break; - case BindOpcodeSetDylibOrdinalImmediate: /* ordinal = imm; */ break; - case BindOpcodeSetDylibOrdinalULEB: /* ordinal = */ readLEB128(table, tableSize, i); break; - case BindOpcodeSetDylibSpecialImmediate: /* ordinal = -imm; */ break; + case BindOpcodeSetDylibOrdinalImmediate: ordinal = imm;break; + case BindOpcodeSetDylibOrdinalULEB: ordinal = readLEB128(table, tableSize, i); break; + case BindOpcodeSetDylibSpecialImmediate: ordinal = -imm; break; case BindOpcodeSetSymbolTrailingFlagsImmediate: /* flags = imm; */ name = (char*)&table[i]; @@ -2911,8 +2975,7 @@ void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNS externReloc.size = m_addressSize; externReloc.pcRelative = false; externReloc.external = true; - header.externalRelocations.emplace_back(externReloc, string(name)); - + header.bindingRelocations.emplace_back(externReloc, string(name), ordinal); address += m_addressSize; break; case BindOpcodeDoBindAddAddressULEB: @@ -2925,7 +2988,7 @@ void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNS externReloc.size = m_addressSize; externReloc.pcRelative = false; externReloc.external = true; - header.externalRelocations.emplace_back(externReloc, string(name)); + header.bindingRelocations.emplace_back(externReloc, string(name), ordinal); address += m_addressSize; address += readLEB128(table, tableSize, i); @@ -2940,7 +3003,7 @@ void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNS externReloc.size = m_addressSize; externReloc.pcRelative = false; externReloc.external = true; - header.externalRelocations.emplace_back(externReloc, string(name)); + header.bindingRelocations.emplace_back(externReloc, string(name), ordinal); address += m_addressSize; address += (imm * m_addressSize); break; @@ -2959,7 +3022,7 @@ void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNS externReloc.size = m_addressSize; externReloc.pcRelative = false; externReloc.external = true; - header.externalRelocations.emplace_back(externReloc, string(name)); + header.bindingRelocations.emplace_back(externReloc, string(name), ordinal); address += skip + m_addressSize; } @@ -3288,7 +3351,7 @@ void MachoView::ParseChainedFixups( { uint32_t importEntry = parentReader.Read32(); dyld_chained_import import = *(reinterpret_cast(&importEntry)); - processChainedImport(import.lib_ordinal, 0, import.name_offset, import.weak_import, parentReader); + processChainedImport(static_cast(import.lib_ordinal), 0, import.name_offset, import.weak_import, parentReader); } break; } @@ -3298,7 +3361,7 @@ void MachoView::ParseChainedFixups( { dyld_chained_import_addend import; parentReader.Read(&import, sizeof(import)); - processChainedImport(import.lib_ordinal, import.addend, import.name_offset, import.weak_import, parentReader); + processChainedImport(static_cast(import.lib_ordinal), import.addend, import.name_offset, import.weak_import, parentReader); } break; } @@ -3308,7 +3371,7 @@ void MachoView::ParseChainedFixups( { dyld_chained_import_addend64 import; parentReader.Read(&import, sizeof(import)); - processChainedImport(import.lib_ordinal, import.addend, import.name_offset, import.weak_import, parentReader); + processChainedImport(static_cast(import.lib_ordinal), import.addend, import.name_offset, import.weak_import, parentReader); } break; } @@ -3521,7 +3584,7 @@ void MachoView::ParseChainedFixups( chainEntryAddress += (nextEntryStrideCount * strideSize); if (chainEntryAddress > pageAddress + starts.page_size) { - m_logger->LogDebug("Chained Fixups: Pointer at %llx left page", + m_logger->LogError("Chained Fixups: Pointer at %llx left page", GetStart() + ((chainEntryAddress - (nextEntryStrideCount * strideSize))) - m_universalImageOffset); fixupsDone = true; } @@ -3546,9 +3609,8 @@ void MachoView::ParseChainedFixups( externReloc.address = targetAddress; externReloc.size = m_addressSize; externReloc.pcRelative = false; - externReloc.external = true; externReloc.addend = entry.addend; - header.externalRelocations.emplace_back(externReloc, entry.name); + header.bindingRelocations.emplace_back(externReloc, entry.name, entry.lib_ordinal); } else { diff --git a/view/macho/machoview.h b/view/macho/machoview.h index 253af9d13..c05d03a80 100644 --- a/view/macho/machoview.h +++ b/view/macho/machoview.h @@ -818,6 +818,13 @@ namespace BinaryNinja RebaseOpcodeDoRebaseUlebTimesSkippingUleb = 0x80u, // REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB }; + enum BindSpecial { + BindSpecialDylibSelf = 0, // BIND_SPECIAL_DYLIB_SELF + BindSpecialDylibMainExecutable = -1, // BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE + BindSpecialDylibFlatLookup = -2, // BIND_SPECIAL_DYLIB_FLAT_LOOKUP + BindSpecialDylibWeakLookup = -3 // BIND_SPECIAL_DYLIB_WEAK_LOOKUP + }; + enum BindOpcode { BindOpcodeMask = 0xF0u, // BIND_OPCODE_MASK BindImmediateMask = 0x0Fu, // BIND_IMMEDIATE_MASK @@ -1375,6 +1382,13 @@ namespace BinaryNinja uint32_t reserved; }; + struct BoundRelocation + { + BNRelocationInfo info; + std::string name; + int64_t ordinal; + }; + struct MachOHeader { bool isMainHeader = false; @@ -1386,7 +1400,7 @@ namespace BinaryNinja std::vector> entryPoints; std::vector m_entryPoints; //list of entrypoints - std::vector> externalRelocations; + std::vector bindingRelocations; std::vector rebaseRelocations; symtab_command symtab;