From fe56b2fe2f2de69190794ef1d9286cdefd176e40 Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Fri, 14 Feb 2025 19:53:18 +0100 Subject: [PATCH] add basic support for processing LC_DYLD_CHAINED_FIXUPS --- .../FileFormatStreams/FormatLayouts/MachO.cs | 45 ++++++++++ .../FileFormatStreams/MachOReader.cs | 87 ++++++++++++++++++- 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/MachO.cs b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/MachO.cs index a2c57ee..93f0292 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/MachO.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/MachO.cs @@ -6,6 +6,7 @@ using System; using NoisyCowStudios.Bin2Object; +using VersionedSerialization.Attributes; namespace Il2CppInspector { @@ -31,6 +32,7 @@ namespace Il2CppInspector LC_DYLD_INFO_ONLY = 0x80000022, LC_FUNCTION_STARTS = 0x26, LC_ENCRYPTION_INFO_64 = 0x2C, + LC_DYLD_CHAINED_FIXUPS = 0x80000034, CPU_TYPE_X86 = 7, CPU_TYPE_X86_64 = 0x01000000 + CPU_TYPE_X86, @@ -172,4 +174,47 @@ namespace Il2CppInspector public bool r_extern => ((r_data >> 27) & 1) == 1; public uint r_type => r_data >> 28; } + + [VersionedStruct] + public partial struct MachODyldChainedFixupsHeader + { + public uint FixupsVersion; + public uint StartsOffset; + public uint ImportsOffset; + public uint SymbolsOffset; + public uint ImportsCount; + public uint ImportsFormat; + public uint SymbolsFormat; + } + + [VersionedStruct] + public partial struct MachODyldChainedStartsInSegment + { + public const ushort DYLD_CHAINED_PTR_START_NONE = 0xffff; + + public uint StructSize; + public ushort PageSize; + public ushort PointerFormat; + public ulong SegmentOffset; + public uint MaxValidPointer; + public ushort PageCount; + } + + public enum MachODyldChainedPtr + { + DYLD_CHAINED_PTR_64 = 2, + DYLD_CHAINED_PTR_64_OFFSET = 6, + } + + [VersionedStruct] + public partial struct MachODyldChainedPtr64Rebase + { + private ulong _value; + + public ulong Target => _value & 0xfffffffff; + public ulong High8 => (_value >> 36) & 0xff; + public ulong Reserved => (_value >> (36 + 8)) & 0x7f; + public ulong Next => (_value >> (36 + 8 + 7)) & 0xfff; + public bool Bind => ((_value >> (36 + 8 + 7 + 12)) & 0x1) == 0x1; + } } diff --git a/Il2CppInspector.Common/FileFormatStreams/MachOReader.cs b/Il2CppInspector.Common/FileFormatStreams/MachOReader.cs index e65fc0e..409dd0d 100644 --- a/Il2CppInspector.Common/FileFormatStreams/MachOReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/MachOReader.cs @@ -172,15 +172,21 @@ namespace Il2CppInspector if (encryptionInfo.CryptID != 0) throw new NotImplementedException("This Mach-O executable is encrypted with FairPlay DRM and cannot be processed. Please provide a decrypted version of the executable."); break; + + case MachO.LC_DYLD_CHAINED_FIXUPS: + var chainedFixupsInfo = ReadObject(); + ApplyChainedFixups(chainedFixupsInfo); + break; } // There might be other data after the load command so always use the specified total size to step forwards Position = startPos + loadCommand.Size; } + // Note: Some binaries do not have __mod_init_func, but instead just __init_offset with offsets to the init functions. This check is disabled. // Must find __mod_init_func - if (funcTab == null) - return false; + //if (funcTab == null) + // return false; // Process relocations foreach (var section in machoSections) { @@ -290,7 +296,82 @@ namespace Il2CppInspector } } - public override uint[] GetFunctionTable() => ReadArray(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray(); + private void ApplyChainedFixups(in MachOLinkEditDataCommand info) + { + var chainedFixupsHeader = ReadVersionedObject(info.Offset); + if (chainedFixupsHeader.FixupsVersion != 0) + { + Console.WriteLine($"Unsupported chained fixups version: {chainedFixupsHeader.FixupsVersion}"); + return; + } + + if (chainedFixupsHeader.ImportsFormat != 1 /* DYLD_CHAINED_IMPORT */) + { + Console.WriteLine($"Unsupported chained fixups import format: {chainedFixupsHeader.ImportsFormat}"); + return; + } + + //var importsBase = info.Offset + chainedFixupsHeader.ImportsOffset; + //var imports = ReadPrimitiveArray(importsBase, + // chainedFixupsHeader.ImportsCount); + + //var symbolsBase = info.Offset + chainedFixupsHeader.SymbolsOffset; // todo: apparently this supports zlib + + var startsBase = info.Offset + chainedFixupsHeader.StartsOffset; + var segmentCount = ReadPrimitive(startsBase); + var segmentStartOffsets = ReadPrimitiveArray(startsBase + 4, segmentCount); + + foreach (var startOffset in segmentStartOffsets) + { + if (startOffset == 0) + continue; + + var startsInfo = ReadVersionedObject(startsBase + startOffset); + if (startsInfo.SegmentOffset == 0) + continue; + + var pointerFormat = (MachODyldChainedPtr)startsInfo.PointerFormat; + + var pages = ReadPrimitiveArray( + startsBase + startOffset + MachODyldChainedStartsInSegment.Size(), startsInfo.PageCount); + + for (var i = 0; i < pages.Length; i++) + { + var page = pages[i]; + if (page == MachODyldChainedStartsInSegment.DYLD_CHAINED_PTR_START_NONE) + continue; + + var chainOffset = startsInfo.SegmentOffset + (ulong)(i * startsInfo.PageSize) + page; + + while (true) + { + var currentEntry = ReadVersionedObject((long)chainOffset); + + var fixedValue = 0ul; + + if (!currentEntry.Bind) // todo: bind + { + fixedValue = pointerFormat switch + { + MachODyldChainedPtr.DYLD_CHAINED_PTR_64 + or MachODyldChainedPtr.DYLD_CHAINED_PTR_64_OFFSET + => currentEntry.High8 << 56 | currentEntry.Target, + _ => fixedValue + }; + + Write((long)chainOffset, fixedValue); + } + + if (currentEntry.Next == 0) + break; + + chainOffset += currentEntry.Next * 4; + } + } + } + } + + public override uint[] GetFunctionTable() => funcTab == null ? [] : ReadArray(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray(); public override Dictionary GetSymbolTable() => symbolTable;