diff options
| author | kat <kat@vector35.com> | 2024-06-18 10:58:00 -0400 |
|---|---|---|
| committer | kat <kat@vector35.com> | 2024-06-18 12:12:54 -0400 |
| commit | 89afd16f243065bdff26ada8d5aa090c781131d9 (patch) | |
| tree | 4c9a0b001afac8c64f438a8497541cf2c181cc20 /view/macho | |
| parent | 6d2f24c84947f188f4056d39b2cb22ee1d870022 (diff) | |
Add support for __chain_start section parsing (Firmware fixups)
Diffstat (limited to 'view/macho')
| -rw-r--r-- | view/macho/machoview.cpp | 200 | ||||
| -rw-r--r-- | view/macho/machoview.h | 3 |
2 files changed, 203 insertions, 0 deletions
diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp index 17165aa0..257f57a6 100644 --- a/view/macho/machoview.cpp +++ b/view/macho/machoview.cpp @@ -1744,6 +1744,12 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_ semantics = ReadWriteDataSectionSemantics; if (strncmp(header.sections[i].segname, "__DATA_CONST", sizeof(header.sections[i].segname)) == 0) semantics = ReadOnlyDataSectionSemantics; + if (strncmp(header.sections[i].sectname, "__chain_starts", sizeof(header.sections[i].sectname)) == 0 + && header.sections[i].size < UINT32_MAX) + { + header.chainStartsPresent = true; + header.chainStarts = header.sections[i]; + } AddAutoSection(header.sectionNames[i], header.sections[i].addr, header.sections[i].size, semantics, type, header.sections[i].align); } @@ -2855,6 +2861,11 @@ void MachoView::ParseSymbolTable(BinaryReader& reader, MachOHeader& header, cons m_logger->LogDebug("Chained Fixups"); ParseChainedFixups(header, header.chainedFixups); } + else if (header.chainStartsPresent) + { + m_logger->LogDebug("Chained Starts"); + ParseChainedStarts(header, header.chainStarts); + } if (header.exportTriePresent && header.isMainHeader) ParseExportTrie(reader, header.exportTrie); @@ -3452,6 +3463,195 @@ void MachoView::ParseChainedFixups(MachOHeader& header, linkedit_data_command ch } +void MachoView::ParseChainedStarts(MachOHeader& header, section_64 chainedStarts) +{ + if (!chainedStarts.offset) + return; + + m_logger->LogDebug("Processing Chained Starts"); + + // Dummy relocation + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(BNRelocationInfo)); + reloc.type = StandardRelocationType; + reloc.size = m_addressSize; + reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION; + + bool processBinds = true; + + BinaryReader parentReader(GetParentView()); + BinaryReader mappedReader(this); + + try { + uint64_t fixupHeaderAddress = m_universalImageOffset + chainedStarts.offset; + parentReader.Seek(fixupHeaderAddress); + + uint32_t pointerFormat = parentReader.Read32(); + uint32_t startsCount = parentReader.Read32(); + std::vector<uint32_t> startsOffsets; + for (size_t i = 0; i < startsCount; i++) + { + startsOffsets.push_back(parentReader.Read32()); + } + + uint8_t strideSize; + ChainedFixupPointerGeneric format; + + // Firmware formats will require digging up whatever place they're being used and reversing it. + // They are not handled by dyld. + switch (pointerFormat) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + strideSize = 8; + format = GenericArm64eFixupFormat; + break; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + strideSize = 4; + format = GenericArm64eFixupFormat; + break; + // case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: Unsupported. + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + strideSize = 4; + format = Generic64FixupFormat; + break; + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + strideSize = 4; + format = Generic32FixupFormat; + break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + strideSize = 4; + format = Firmware32FixupFormat; + break; + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + strideSize = 1; + format = Generic64FixupFormat; + break; + default: + { + m_logger->LogError("Chained Starts: Unknown or unsupported pointer format %d, " + "unable to process chain starts", pointerFormat); + return; + } + } + + for (uint32_t offset : startsOffsets) + { + uint64_t chainEntryAddress = m_universalImageOffset + offset; + + bool fixupsDone = false; + + while (!fixupsDone) + { + ChainedFixupPointer pointer; + parentReader.Seek(chainEntryAddress); + mappedReader.Seek(chainEntryAddress - m_universalImageOffset + GetStart()); + if (format == Generic32FixupFormat || format == Firmware32FixupFormat) + pointer.raw32 = (uint32_t)(uintptr_t)mappedReader.Read32(); + else + pointer.raw64 = (uintptr_t)mappedReader.Read64(); + + bool bind = false; + uint64_t nextEntryStrideCount; + + switch (format) + { + case Generic32FixupFormat: + bind = pointer.generic32.bind.bind; + nextEntryStrideCount = pointer.generic32.rebase.next; + break; + case Generic64FixupFormat: + bind = pointer.generic64.bind.bind; + nextEntryStrideCount = pointer.generic64.rebase.next; + break; + case GenericArm64eFixupFormat: + bind = pointer.arm64e.bind.bind; + nextEntryStrideCount = pointer.arm64e.rebase.next; + break; + case Firmware32FixupFormat: + nextEntryStrideCount = pointer.firmware32.next; + bind = false; + break; + } + + m_logger->LogTrace("Chained Starts: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress, + GetStart() + (chainEntryAddress - m_universalImageOffset), + bind, nextEntryStrideCount); + + if (bind && processBinds) + { + m_logger->LogWarn("Chained Starts: Bind pointers not supported in Chained Starts"); + chainEntryAddress += (nextEntryStrideCount * strideSize); + if (nextEntryStrideCount == 0) + fixupsDone = true; + continue; + } + else if (!bind) + { + uint64_t entryOffset; + switch (pointerFormat) + { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + { + if (pointer.arm64e.bind.auth) + entryOffset = pointer.arm64e.authRebase.target; + else + entryOffset = pointer.arm64e.rebase.target; + + if ( pointerFormat != DYLD_CHAINED_PTR_ARM64E || pointer.arm64e.bind.auth) + entryOffset += GetStart(); + + break; + } + case DYLD_CHAINED_PTR_64: + entryOffset = pointer.generic64.rebase.target; + break; + case DYLD_CHAINED_PTR_64_OFFSET: + entryOffset = pointer.generic64.rebase.target + GetStart(); + break; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + entryOffset = pointer.kernel64.target; + break; + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + entryOffset = pointer.generic32.rebase.target; + break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + entryOffset = pointer.firmware32.target; + break; + } + + reloc.address = GetStart() + (chainEntryAddress - m_universalImageOffset); + DefineRelocation(m_arch, reloc, entryOffset, reloc.address); + m_logger->LogDebug("Chained Starts: Adding relocated pointer %llx -> %llx", reloc.address, entryOffset); + + if (m_objcProcessor) + { + m_objcProcessor->AddRelocatedPointer(reloc.address, entryOffset); + } + } + + chainEntryAddress += (nextEntryStrideCount * strideSize); + + if (nextEntryStrideCount == 0) + fixupsDone = true; + } + } + } + catch (ReadException&) + { + m_logger->LogError("Chained Starts parsing failed"); + } +} + + uint64_t MachoView::PerformGetEntryPoint() const { if (m_header.m_entryPoints.size() == 0) diff --git a/view/macho/machoview.h b/view/macho/machoview.h index dbc0f172..4bf37ef0 100644 --- a/view/macho/machoview.h +++ b/view/macho/machoview.h @@ -1271,6 +1271,7 @@ namespace BinaryNinja std::vector<section_64> moduleInitSections; linkedit_data_command exportTrie; linkedit_data_command chainedFixups {}; + section_64 chainStarts {}; DataBuffer* stringList; size_t stringListSize = 0; @@ -1293,6 +1294,7 @@ namespace BinaryNinja bool dyldInfoPresent = false; bool exportTriePresent = false; bool chainedFixupsPresent = false; + bool chainStartsPresent = false; bool routinesPresent = false; bool functionStartsPresent = false; bool relocatable = false; @@ -1374,6 +1376,7 @@ namespace BinaryNinja bool GetSectionPermissions(MachOHeader& header, uint64_t address, uint32_t &flags); bool GetSegmentPermissions(MachOHeader& header, uint64_t address, uint32_t &flags); void ParseChainedFixups(MachOHeader& header, linkedit_data_command chainedFixups); + void ParseChainedStarts(MachOHeader& header, section_64 chainedStarts); virtual uint64_t PerformGetEntryPoint() const override; |
