diff --git a/Il2CppInspector/FileFormatReaders/FormatLayouts/MachO.cs b/Il2CppInspector/FileFormatReaders/FormatLayouts/MachO.cs index e45f7c1..1b1ebe5 100644 --- a/Il2CppInspector/FileFormatReaders/FormatLayouts/MachO.cs +++ b/Il2CppInspector/FileFormatReaders/FormatLayouts/MachO.cs @@ -30,7 +30,7 @@ namespace Il2CppInspector CPU_TYPE_ARM64 = 0x01000000 + CPU_TYPE_ARM } - internal class MachOHeader + internal class MachOHeader where TWord : struct { public uint Magic; public uint CPUType; @@ -38,8 +38,7 @@ namespace Il2CppInspector public uint FileType; public uint NumCommands; public uint SizeOfCommands; - public uint Flags; - // 64-bit header has an extra 32-bit Reserved field + public TWord Flags; } internal class MachOLoadCommand @@ -48,69 +47,36 @@ namespace Il2CppInspector public uint Size; } - internal class MachOSegmentCommand + internal class MachOSegmentCommand where TWord : struct { // MachOLoadCommand [String(FixedSize = 16)] public string Name; - public uint VirtualAddress; - public uint VirtualSize; - public uint ImageOffset; - public uint ImageSize; + public TWord VirtualAddress; + public TWord VirtualSize; + public TWord ImageOffset; + public TWord ImageSize; public uint VMMaxProt; public uint VMInitProt; public uint NumSections; public uint Flags; } - internal class MachOSegmentCommand64 - { - // MachOLoadCommand - [String(FixedSize = 16)] - public string Name; - public ulong VirtualAddress; - public ulong VirtualSize; - public ulong ImageOffset; - public ulong ImageSize; - public uint VMMaxProt; - public uint VMInitProt; - public uint NumSections; - public uint Flags; - } - - internal class MachOSection + internal class MachOSection where TWord : struct { [String(FixedSize = 16)] public string Name; [String(FixedSize = 16)] public string SegmentName; - public uint Address; - public uint Size; + public TWord Address; + public TWord Size; public uint ImageOffset; public uint Align; public uint ImageRelocOffset; public uint NumRelocEntries; public uint Flags; public uint Reserved1; - public uint Reserved2; - } - - internal class MachOSection64 - { - [String(FixedSize = 16)] - public string Name; - [String(FixedSize = 16)] - public string SegmentName; - public ulong Address; - public ulong Size; - public uint ImageOffset; - public uint Align; - public uint ImageRelocOffset; - public uint NumRelocEntries; - public uint Flags; - public uint Reserved1; - public uint Reserved2; - public uint Reserved3; + public TWord Reserved2; } internal class MachOLinkEditDataCommand diff --git a/Il2CppInspector/FileFormatReaders/MachOReader.cs b/Il2CppInspector/FileFormatReaders/MachOReader.cs index a04d5b2..c32dad5 100644 --- a/Il2CppInspector/FileFormatReaders/MachOReader.cs +++ b/Il2CppInspector/FileFormatReaders/MachOReader.cs @@ -13,20 +13,45 @@ using NoisyCowStudios.Bin2Object; namespace Il2CppInspector { - internal class MachOReader : FileFormatReader + internal class MachOReader32 : MachOReader { - private MachOHeader header; + public MachOReader32(Stream stream) : base(stream) { } + + public override int Bits => 32; + + protected override bool checkMagicLE(MachO magic) => magic == MachO.MH_MAGIC; + protected override bool checkMagicBE(MachO magic) => magic == MachO.MH_CIGAM; + + protected override MachO lc_Segment => MachO.LC_SEGMENT; + } + + internal class MachOReader64 : MachOReader + { + public MachOReader64(Stream stream) : base(stream) { } + + public override int Bits => 64; + + protected override bool checkMagicLE(MachO magic) => magic == MachO.MH_MAGIC_64; + protected override bool checkMagicBE(MachO magic) => magic == MachO.MH_CIGAM_64; + + protected override MachO lc_Segment => MachO.LC_SEGMENT_64; + } + + // We need this convoluted generic TReader declaration so that "static T FileFormatReader.Load(Stream)" + // is inherited to MachOReader32/64 with a correct definition of T + internal abstract class MachOReader : FileFormatReader where TWord : struct where TReader : FileFormatReader + { + private MachOHeader header; + private readonly List> sections = new List>(); private uint pFuncTable; private uint sFuncTable; - private bool is64; - private List sections = new List(); - private List sections64 = new List(); - public MachOReader(Stream stream) : base(stream) { } + protected MachOReader(Stream stream) : base(stream) { } public override string Format => "Mach-O"; - public override string Arch => (MachO)header.CPUType switch { + public override string Arch => (MachO)header.CPUType switch + { MachO.CPU_TYPE_ARM => "ARM", MachO.CPU_TYPE_ARM64 => "ARM64", MachO.CPU_TYPE_X86 => "x86", @@ -34,57 +59,42 @@ namespace Il2CppInspector _ => "Unsupported" }; - public override int Bits => is64 ? 64 : 32; + protected abstract bool checkMagicLE(MachO magic); + protected abstract bool checkMagicBE(MachO magic); + protected abstract MachO lc_Segment { get; } protected override bool Init() { // Detect endianness - default is little-endianness MachO magic = (MachO)ReadUInt32(); - if (magic == MachO.MH_CIGAM || magic == MachO.MH_CIGAM_64) { + + if (checkMagicBE(magic)) Endianness = Endianness.Big; - } - else if (magic != MachO.MH_MAGIC && magic != MachO.MH_MAGIC_64) { + + if (!checkMagicBE(magic) && !checkMagicLE(magic)) return false; - } - Position -= sizeof(uint); - header = ReadObject(); - - // 64-bit files have an extra 4 bytes after the header - is64 = false; - if (magic == MachO.MH_MAGIC_64) { - is64 = true; - ReadUInt32(); - } + header = ReadObject>(0); // Must be executable file - if ((MachO) header.FileType != MachO.MH_EXECUTE) + if ((MachO)header.FileType != MachO.MH_EXECUTE) return false; MachOLinkEditDataCommand functionStarts = null; + // Process load commands for (var c = 0; c < header.NumCommands; c++) { var startPos = Position; var loadCommand = ReadObject(); - if ((MachO)loadCommand.Command == MachO.LC_SEGMENT) { - var segment = ReadObject(); + if ((MachO)loadCommand.Command == lc_Segment) { + var segment = ReadObject>(); if (segment.Name == "__TEXT" || segment.Name == "__DATA") { for (int s = 0; s < segment.NumSections; s++) { - var section = ReadObject(); + var section = ReadObject>(); sections.Add(section); - if (section.Name == "__text") - GlobalOffset = section.Address - section.ImageOffset; - } - } - } - else if ((MachO)loadCommand.Command == MachO.LC_SEGMENT_64) { - var segment = ReadObject(); - if (segment.Name == "__TEXT" || segment.Name == "__DATA") { - for (int s = 0; s < segment.NumSections; s++) { - var section64 = ReadObject(); - sections64.Add(section64); - if (section64.Name == "__text") - GlobalOffset = (uint)section64.Address - section64.ImageOffset; + if (section.Name == "__text") { + GlobalOffset = (ulong)Convert.ChangeType(section.Address, typeof(ulong)) - section.ImageOffset; + } } } } @@ -133,12 +143,8 @@ namespace Il2CppInspector } public override uint MapVATR(ulong uiAddr) { - if (!is64) { - var section = sections.First(x => uiAddr >= x.Address && uiAddr <= (x.Address + x.Size)); - return (uint) (uiAddr - (section.Address - section.ImageOffset)); - } - var section64 = sections64.First(x => uiAddr >= x.Address && uiAddr <= (x.Address + x.Size)); - return (uint) (uiAddr - ((uint)section64.Address - section64.ImageOffset)); + var section = sections.First(x => uiAddr >= (uint)(object)x.Address && uiAddr <= (uint)(object)x.Address + (uint)(object)x.Size); + return (uint) (uiAddr - ((ulong) Convert.ChangeType(section.Address, typeof(ulong)) - section.ImageOffset)); } } } diff --git a/Il2CppInspector/FileFormatReaders/UBReader.cs b/Il2CppInspector/FileFormatReaders/UBReader.cs index 52e6aa2..c2db743 100644 --- a/Il2CppInspector/FileFormatReaders/UBReader.cs +++ b/Il2CppInspector/FileFormatReaders/UBReader.cs @@ -1,5 +1,5 @@ /* - Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com + Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com All rights reserved. */ @@ -37,7 +37,8 @@ namespace Il2CppInspector Position = arch.Offset; Endianness = Endianness.Little; - return MachOReader.Load(new MemoryStream(ReadBytes((int)arch.Size))); + var s = new MemoryStream(ReadBytes((int) arch.Size)); + return (IFileFormatReader) MachOReader32.Load(s) ?? MachOReader64.Load(s); } } } diff --git a/README.md b/README.md index 87ba308..3e37c50 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Extract types, methods, properties and fields from Unity IL2CPP binaries. * Supports ELF (Android .so), PE (Windows .exe), Mach-O (Apple iOS/Mac) and Universal Binary (Fat Mach-O) file formats +* 64-bit support for Mach-O files * Supports ARMv7, ARMv7 Thumb T1 and x86 architectures regardless of file format * Supports metadata versions 21, 22, 23, 24, 24.1 (Unity 2018.3+) and 24.2 (Unity 2019+) * No manual reverse-engineering required; all data is calculated automatically