Skip to content

Commit 89afd16

Browse files
committed
Add support for __chain_start section parsing (Firmware fixups)
1 parent 6d2f24c commit 89afd16

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed

view/macho/machoview.cpp

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,12 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
17441744
semantics = ReadWriteDataSectionSemantics;
17451745
if (strncmp(header.sections[i].segname, "__DATA_CONST", sizeof(header.sections[i].segname)) == 0)
17461746
semantics = ReadOnlyDataSectionSemantics;
1747+
if (strncmp(header.sections[i].sectname, "__chain_starts", sizeof(header.sections[i].sectname)) == 0
1748+
&& header.sections[i].size < UINT32_MAX)
1749+
{
1750+
header.chainStartsPresent = true;
1751+
header.chainStarts = header.sections[i];
1752+
}
17471753

17481754
AddAutoSection(header.sectionNames[i], header.sections[i].addr, header.sections[i].size, semantics, type, header.sections[i].align);
17491755
}
@@ -2855,6 +2861,11 @@ void MachoView::ParseSymbolTable(BinaryReader& reader, MachOHeader& header, cons
28552861
m_logger->LogDebug("Chained Fixups");
28562862
ParseChainedFixups(header, header.chainedFixups);
28572863
}
2864+
else if (header.chainStartsPresent)
2865+
{
2866+
m_logger->LogDebug("Chained Starts");
2867+
ParseChainedStarts(header, header.chainStarts);
2868+
}
28582869
if (header.exportTriePresent && header.isMainHeader)
28592870
ParseExportTrie(reader, header.exportTrie);
28602871

@@ -3452,6 +3463,195 @@ void MachoView::ParseChainedFixups(MachOHeader& header, linkedit_data_command ch
34523463
}
34533464

34543465

3466+
void MachoView::ParseChainedStarts(MachOHeader& header, section_64 chainedStarts)
3467+
{
3468+
if (!chainedStarts.offset)
3469+
return;
3470+
3471+
m_logger->LogDebug("Processing Chained Starts");
3472+
3473+
// Dummy relocation
3474+
BNRelocationInfo reloc;
3475+
memset(&reloc, 0, sizeof(BNRelocationInfo));
3476+
reloc.type = StandardRelocationType;
3477+
reloc.size = m_addressSize;
3478+
reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION;
3479+
3480+
bool processBinds = true;
3481+
3482+
BinaryReader parentReader(GetParentView());
3483+
BinaryReader mappedReader(this);
3484+
3485+
try {
3486+
uint64_t fixupHeaderAddress = m_universalImageOffset + chainedStarts.offset;
3487+
parentReader.Seek(fixupHeaderAddress);
3488+
3489+
uint32_t pointerFormat = parentReader.Read32();
3490+
uint32_t startsCount = parentReader.Read32();
3491+
std::vector<uint32_t> startsOffsets;
3492+
for (size_t i = 0; i < startsCount; i++)
3493+
{
3494+
startsOffsets.push_back(parentReader.Read32());
3495+
}
3496+
3497+
uint8_t strideSize;
3498+
ChainedFixupPointerGeneric format;
3499+
3500+
// Firmware formats will require digging up whatever place they're being used and reversing it.
3501+
// They are not handled by dyld.
3502+
switch (pointerFormat) {
3503+
case DYLD_CHAINED_PTR_ARM64E:
3504+
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
3505+
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
3506+
strideSize = 8;
3507+
format = GenericArm64eFixupFormat;
3508+
break;
3509+
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
3510+
strideSize = 4;
3511+
format = GenericArm64eFixupFormat;
3512+
break;
3513+
// case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: Unsupported.
3514+
case DYLD_CHAINED_PTR_64:
3515+
case DYLD_CHAINED_PTR_64_OFFSET:
3516+
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
3517+
strideSize = 4;
3518+
format = Generic64FixupFormat;
3519+
break;
3520+
case DYLD_CHAINED_PTR_32:
3521+
case DYLD_CHAINED_PTR_32_CACHE:
3522+
strideSize = 4;
3523+
format = Generic32FixupFormat;
3524+
break;
3525+
case DYLD_CHAINED_PTR_32_FIRMWARE:
3526+
strideSize = 4;
3527+
format = Firmware32FixupFormat;
3528+
break;
3529+
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
3530+
strideSize = 1;
3531+
format = Generic64FixupFormat;
3532+
break;
3533+
default:
3534+
{
3535+
m_logger->LogError("Chained Starts: Unknown or unsupported pointer format %d, "
3536+
"unable to process chain starts", pointerFormat);
3537+
return;
3538+
}
3539+
}
3540+
3541+
for (uint32_t offset : startsOffsets)
3542+
{
3543+
uint64_t chainEntryAddress = m_universalImageOffset + offset;
3544+
3545+
bool fixupsDone = false;
3546+
3547+
while (!fixupsDone)
3548+
{
3549+
ChainedFixupPointer pointer;
3550+
parentReader.Seek(chainEntryAddress);
3551+
mappedReader.Seek(chainEntryAddress - m_universalImageOffset + GetStart());
3552+
if (format == Generic32FixupFormat || format == Firmware32FixupFormat)
3553+
pointer.raw32 = (uint32_t)(uintptr_t)mappedReader.Read32();
3554+
else
3555+
pointer.raw64 = (uintptr_t)mappedReader.Read64();
3556+
3557+
bool bind = false;
3558+
uint64_t nextEntryStrideCount;
3559+
3560+
switch (format)
3561+
{
3562+
case Generic32FixupFormat:
3563+
bind = pointer.generic32.bind.bind;
3564+
nextEntryStrideCount = pointer.generic32.rebase.next;
3565+
break;
3566+
case Generic64FixupFormat:
3567+
bind = pointer.generic64.bind.bind;
3568+
nextEntryStrideCount = pointer.generic64.rebase.next;
3569+
break;
3570+
case GenericArm64eFixupFormat:
3571+
bind = pointer.arm64e.bind.bind;
3572+
nextEntryStrideCount = pointer.arm64e.rebase.next;
3573+
break;
3574+
case Firmware32FixupFormat:
3575+
nextEntryStrideCount = pointer.firmware32.next;
3576+
bind = false;
3577+
break;
3578+
}
3579+
3580+
m_logger->LogTrace("Chained Starts: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress,
3581+
GetStart() + (chainEntryAddress - m_universalImageOffset),
3582+
bind, nextEntryStrideCount);
3583+
3584+
if (bind && processBinds)
3585+
{
3586+
m_logger->LogWarn("Chained Starts: Bind pointers not supported in Chained Starts");
3587+
chainEntryAddress += (nextEntryStrideCount * strideSize);
3588+
if (nextEntryStrideCount == 0)
3589+
fixupsDone = true;
3590+
continue;
3591+
}
3592+
else if (!bind)
3593+
{
3594+
uint64_t entryOffset;
3595+
switch (pointerFormat)
3596+
{
3597+
case DYLD_CHAINED_PTR_ARM64E:
3598+
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
3599+
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
3600+
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
3601+
{
3602+
if (pointer.arm64e.bind.auth)
3603+
entryOffset = pointer.arm64e.authRebase.target;
3604+
else
3605+
entryOffset = pointer.arm64e.rebase.target;
3606+
3607+
if ( pointerFormat != DYLD_CHAINED_PTR_ARM64E || pointer.arm64e.bind.auth)
3608+
entryOffset += GetStart();
3609+
3610+
break;
3611+
}
3612+
case DYLD_CHAINED_PTR_64:
3613+
entryOffset = pointer.generic64.rebase.target;
3614+
break;
3615+
case DYLD_CHAINED_PTR_64_OFFSET:
3616+
entryOffset = pointer.generic64.rebase.target + GetStart();
3617+
break;
3618+
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
3619+
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
3620+
entryOffset = pointer.kernel64.target;
3621+
break;
3622+
case DYLD_CHAINED_PTR_32:
3623+
case DYLD_CHAINED_PTR_32_CACHE:
3624+
entryOffset = pointer.generic32.rebase.target;
3625+
break;
3626+
case DYLD_CHAINED_PTR_32_FIRMWARE:
3627+
entryOffset = pointer.firmware32.target;
3628+
break;
3629+
}
3630+
3631+
reloc.address = GetStart() + (chainEntryAddress - m_universalImageOffset);
3632+
DefineRelocation(m_arch, reloc, entryOffset, reloc.address);
3633+
m_logger->LogDebug("Chained Starts: Adding relocated pointer %llx -> %llx", reloc.address, entryOffset);
3634+
3635+
if (m_objcProcessor)
3636+
{
3637+
m_objcProcessor->AddRelocatedPointer(reloc.address, entryOffset);
3638+
}
3639+
}
3640+
3641+
chainEntryAddress += (nextEntryStrideCount * strideSize);
3642+
3643+
if (nextEntryStrideCount == 0)
3644+
fixupsDone = true;
3645+
}
3646+
}
3647+
}
3648+
catch (ReadException&)
3649+
{
3650+
m_logger->LogError("Chained Starts parsing failed");
3651+
}
3652+
}
3653+
3654+
34553655
uint64_t MachoView::PerformGetEntryPoint() const
34563656
{
34573657
if (m_header.m_entryPoints.size() == 0)

view/macho/machoview.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,7 @@ namespace BinaryNinja
12711271
std::vector<section_64> moduleInitSections;
12721272
linkedit_data_command exportTrie;
12731273
linkedit_data_command chainedFixups {};
1274+
section_64 chainStarts {};
12741275

12751276
DataBuffer* stringList;
12761277
size_t stringListSize = 0;
@@ -1293,6 +1294,7 @@ namespace BinaryNinja
12931294
bool dyldInfoPresent = false;
12941295
bool exportTriePresent = false;
12951296
bool chainedFixupsPresent = false;
1297+
bool chainStartsPresent = false;
12961298
bool routinesPresent = false;
12971299
bool functionStartsPresent = false;
12981300
bool relocatable = false;
@@ -1374,6 +1376,7 @@ namespace BinaryNinja
13741376
bool GetSectionPermissions(MachOHeader& header, uint64_t address, uint32_t &flags);
13751377
bool GetSegmentPermissions(MachOHeader& header, uint64_t address, uint32_t &flags);
13761378
void ParseChainedFixups(MachOHeader& header, linkedit_data_command chainedFixups);
1379+
void ParseChainedStarts(MachOHeader& header, section_64 chainedStarts);
13771380

13781381
virtual uint64_t PerformGetEntryPoint() const override;
13791382

0 commit comments

Comments
 (0)