Add 64-bit Mach-O support

This commit is contained in:
Katy Coe
2019-10-22 15:42:11 +02:00
parent e036151f4d
commit 298db65a71
4 changed files with 65 additions and 91 deletions

View File

@@ -30,7 +30,7 @@ namespace Il2CppInspector
CPU_TYPE_ARM64 = 0x01000000 + CPU_TYPE_ARM CPU_TYPE_ARM64 = 0x01000000 + CPU_TYPE_ARM
} }
internal class MachOHeader internal class MachOHeader<TWord> where TWord : struct
{ {
public uint Magic; public uint Magic;
public uint CPUType; public uint CPUType;
@@ -38,8 +38,7 @@ namespace Il2CppInspector
public uint FileType; public uint FileType;
public uint NumCommands; public uint NumCommands;
public uint SizeOfCommands; public uint SizeOfCommands;
public uint Flags; public TWord Flags;
// 64-bit header has an extra 32-bit Reserved field
} }
internal class MachOLoadCommand internal class MachOLoadCommand
@@ -48,69 +47,36 @@ namespace Il2CppInspector
public uint Size; public uint Size;
} }
internal class MachOSegmentCommand internal class MachOSegmentCommand<TWord> where TWord : struct
{ {
// MachOLoadCommand // MachOLoadCommand
[String(FixedSize = 16)] [String(FixedSize = 16)]
public string Name; public string Name;
public uint VirtualAddress; public TWord VirtualAddress;
public uint VirtualSize; public TWord VirtualSize;
public uint ImageOffset; public TWord ImageOffset;
public uint ImageSize; public TWord ImageSize;
public uint VMMaxProt; public uint VMMaxProt;
public uint VMInitProt; public uint VMInitProt;
public uint NumSections; public uint NumSections;
public uint Flags; public uint Flags;
} }
internal class MachOSegmentCommand64 internal class MachOSection<TWord> where TWord : struct
{
// 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
{ {
[String(FixedSize = 16)] [String(FixedSize = 16)]
public string Name; public string Name;
[String(FixedSize = 16)] [String(FixedSize = 16)]
public string SegmentName; public string SegmentName;
public uint Address; public TWord Address;
public uint Size; public TWord Size;
public uint ImageOffset; public uint ImageOffset;
public uint Align; public uint Align;
public uint ImageRelocOffset; public uint ImageRelocOffset;
public uint NumRelocEntries; public uint NumRelocEntries;
public uint Flags; public uint Flags;
public uint Reserved1; public uint Reserved1;
public uint Reserved2; public TWord 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;
} }
internal class MachOLinkEditDataCommand internal class MachOLinkEditDataCommand

View File

@@ -13,20 +13,45 @@ using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector namespace Il2CppInspector
{ {
internal class MachOReader : FileFormatReader<MachOReader> internal class MachOReader32 : MachOReader<uint, MachOReader32>
{ {
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<ulong, MachOReader64>
{
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<TWord, TReader> : FileFormatReader<TReader> where TWord : struct where TReader : FileFormatReader<TReader>
{
private MachOHeader<TWord> header;
private readonly List<MachOSection<TWord>> sections = new List<MachOSection<TWord>>();
private uint pFuncTable; private uint pFuncTable;
private uint sFuncTable; private uint sFuncTable;
private bool is64;
private List<MachOSection> sections = new List<MachOSection>();
private List<MachOSection64> sections64 = new List<MachOSection64>();
public MachOReader(Stream stream) : base(stream) { } protected MachOReader(Stream stream) : base(stream) { }
public override string Format => "Mach-O"; 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_ARM => "ARM",
MachO.CPU_TYPE_ARM64 => "ARM64", MachO.CPU_TYPE_ARM64 => "ARM64",
MachO.CPU_TYPE_X86 => "x86", MachO.CPU_TYPE_X86 => "x86",
@@ -34,59 +59,44 @@ namespace Il2CppInspector
_ => "Unsupported" _ => "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() { protected override bool Init() {
// Detect endianness - default is little-endianness // Detect endianness - default is little-endianness
MachO magic = (MachO)ReadUInt32(); MachO magic = (MachO)ReadUInt32();
if (magic == MachO.MH_CIGAM || magic == MachO.MH_CIGAM_64) {
if (checkMagicBE(magic))
Endianness = Endianness.Big; Endianness = Endianness.Big;
}
else if (magic != MachO.MH_MAGIC && magic != MachO.MH_MAGIC_64) { if (!checkMagicBE(magic) && !checkMagicLE(magic))
return false; return false;
}
Position -= sizeof(uint); header = ReadObject<MachOHeader<TWord>>(0);
header = ReadObject<MachOHeader>();
// 64-bit files have an extra 4 bytes after the header
is64 = false;
if (magic == MachO.MH_MAGIC_64) {
is64 = true;
ReadUInt32();
}
// Must be executable file // Must be executable file
if ((MachO) header.FileType != MachO.MH_EXECUTE) if ((MachO)header.FileType != MachO.MH_EXECUTE)
return false; return false;
MachOLinkEditDataCommand functionStarts = null; MachOLinkEditDataCommand functionStarts = null;
// Process load commands
for (var c = 0; c < header.NumCommands; c++) { for (var c = 0; c < header.NumCommands; c++) {
var startPos = Position; var startPos = Position;
var loadCommand = ReadObject<MachOLoadCommand>(); var loadCommand = ReadObject<MachOLoadCommand>();
if ((MachO)loadCommand.Command == MachO.LC_SEGMENT) { if ((MachO)loadCommand.Command == lc_Segment) {
var segment = ReadObject<MachOSegmentCommand>(); var segment = ReadObject<MachOSegmentCommand<TWord>>();
if (segment.Name == "__TEXT" || segment.Name == "__DATA") { if (segment.Name == "__TEXT" || segment.Name == "__DATA") {
for (int s = 0; s < segment.NumSections; s++) { for (int s = 0; s < segment.NumSections; s++) {
var section = ReadObject<MachOSection>(); var section = ReadObject<MachOSection<TWord>>();
sections.Add(section); sections.Add(section);
if (section.Name == "__text") if (section.Name == "__text") {
GlobalOffset = section.Address - section.ImageOffset; GlobalOffset = (ulong)Convert.ChangeType(section.Address, typeof(ulong)) - section.ImageOffset;
} }
} }
} }
else if ((MachO)loadCommand.Command == MachO.LC_SEGMENT_64) {
var segment = ReadObject<MachOSegmentCommand64>();
if (segment.Name == "__TEXT" || segment.Name == "__DATA") {
for (int s = 0; s < segment.NumSections; s++) {
var section64 = ReadObject<MachOSection64>();
sections64.Add(section64);
if (section64.Name == "__text")
GlobalOffset = (uint)section64.Address - section64.ImageOffset;
}
}
} }
if ((MachO)loadCommand.Command == MachO.LC_FUNCTION_STARTS) { if ((MachO)loadCommand.Command == MachO.LC_FUNCTION_STARTS) {
@@ -133,12 +143,8 @@ namespace Il2CppInspector
} }
public override uint MapVATR(ulong uiAddr) { public override uint MapVATR(ulong uiAddr) {
if (!is64) { var section = sections.First(x => uiAddr >= (uint)(object)x.Address && uiAddr <= (uint)(object)x.Address + (uint)(object)x.Size);
var section = sections.First(x => uiAddr >= x.Address && uiAddr <= (x.Address + x.Size)); return (uint) (uiAddr - ((ulong) Convert.ChangeType(section.Address, typeof(ulong)) - section.ImageOffset));
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));
} }
} }
} }

View File

@@ -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. All rights reserved.
*/ */
@@ -37,7 +37,8 @@ namespace Il2CppInspector
Position = arch.Offset; Position = arch.Offset;
Endianness = Endianness.Little; 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);
} }
} }
} }

View File

@@ -2,6 +2,7 @@
Extract types, methods, properties and fields from Unity IL2CPP binaries. 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 * 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 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+) * 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 * No manual reverse-engineering required; all data is calculated automatically