Add support for Mach-O files
This commit is contained in:
@@ -20,6 +20,7 @@ namespace Il2CppInspector
|
|||||||
U ReadMappedObject<U>(uint uiAddr) where U : new();
|
U ReadMappedObject<U>(uint uiAddr) where U : new();
|
||||||
U[] ReadMappedArray<U>(uint uiAddr, int count) where U : new();
|
U[] ReadMappedArray<U>(uint uiAddr, int count) where U : new();
|
||||||
uint MapVATR(uint uiAddr);
|
uint MapVATR(uint uiAddr);
|
||||||
|
void FinalizeInit(Il2CppReader il2cpp);
|
||||||
|
|
||||||
byte[] ReadBytes(int count);
|
byte[] ReadBytes(int count);
|
||||||
ulong ReadUInt64();
|
ulong ReadUInt64();
|
||||||
@@ -38,13 +39,13 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
public virtual string Arch => throw new NotImplementedException();
|
public virtual string Arch => throw new NotImplementedException();
|
||||||
|
|
||||||
public static T Load(string filename) {
|
public static T Load(string filename, uint offset = 0) {
|
||||||
using (var stream = new FileStream(filename, FileMode.Open))
|
using (var stream = new FileStream(filename, FileMode.Open))
|
||||||
return Load(stream);
|
return Load(stream, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Load(Stream stream) {
|
public static T Load(Stream stream, uint offset = 0) {
|
||||||
stream.Position = 0;
|
stream.Position = offset;
|
||||||
var pe = (T) Activator.CreateInstance(typeof(T), stream);
|
var pe = (T) Activator.CreateInstance(typeof(T), stream);
|
||||||
return pe.Init() ? pe : null;
|
return pe.Init() ? pe : null;
|
||||||
}
|
}
|
||||||
@@ -66,5 +67,8 @@ namespace Il2CppInspector
|
|||||||
public U[] ReadMappedArray<U>(uint uiAddr, int count) where U : new() {
|
public U[] ReadMappedArray<U>(uint uiAddr, int count) where U : new() {
|
||||||
return ReadArray<U>(MapVATR(uiAddr), count);
|
return ReadArray<U>(MapVATR(uiAddr), count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Perform file format-based post-load manipulations to the IL2Cpp data
|
||||||
|
public virtual void FinalizeInit(Il2CppReader il2cpp) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,10 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
// Load the il2cpp code file (try ELF and PE)
|
// Load the il2cpp code file (try ELF and PE)
|
||||||
var memoryStream = new MemoryStream(File.ReadAllBytes(codeFile));
|
var memoryStream = new MemoryStream(File.ReadAllBytes(codeFile));
|
||||||
IFileFormatReader stream = (IFileFormatReader) ElfReader.Load(memoryStream) ?? PEReader.Load(memoryStream);
|
IFileFormatReader stream =
|
||||||
|
((IFileFormatReader) ElfReader.Load(memoryStream) ??
|
||||||
|
PEReader.Load(memoryStream)) ??
|
||||||
|
MachOReader.Load(memoryStream);
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
Console.Error.WriteLine("Unsupported executable file format");
|
Console.Error.WriteLine("Unsupported executable file format");
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Il2CppInspector
|
|||||||
var (code, metadata) = Search(loc, Image.GlobalOffset);
|
var (code, metadata) = Search(loc, Image.GlobalOffset);
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
Configure(code, metadata);
|
Configure(code, metadata);
|
||||||
|
Image.FinalizeInit(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
123
Il2CppInspector/MachOHeaders.cs
Normal file
123
Il2CppInspector/MachOHeaders.cs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
|
||||||
|
namespace Il2CppInspector
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
// Structures and enums: https://opensource.apple.com/source/cctools/cctools-870/include/mach-o/loader.h
|
||||||
|
|
||||||
|
public enum MachO : uint
|
||||||
|
{
|
||||||
|
MH_MAGIC = 0xfeedface,
|
||||||
|
MH_CIGAM = 0xcefaedfe,
|
||||||
|
MH_MAGIC_64 = 0xfeedfacf,
|
||||||
|
MH_CIGAM_64 = 0xcffaedfe,
|
||||||
|
|
||||||
|
MH_EXECUTE = 0x2,
|
||||||
|
|
||||||
|
LC_SEGMENT = 0x1,
|
||||||
|
LC_SEGMENT_64 = 0x19,
|
||||||
|
LC_FUNCTION_STARTS = 0x26,
|
||||||
|
|
||||||
|
CPU_TYPE_X86 = 7,
|
||||||
|
CPU_TYPE_X86_64 = 0x01000000 + CPU_TYPE_X86,
|
||||||
|
CPU_TYPE_ARM = 12,
|
||||||
|
CPU_TYPE_ARM64 = 0x01000000 + CPU_TYPE_ARM
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MachOHeader
|
||||||
|
{
|
||||||
|
public uint Magic;
|
||||||
|
public uint CPUType;
|
||||||
|
public uint CPUSubType;
|
||||||
|
public uint FileType;
|
||||||
|
public uint NumCommands;
|
||||||
|
public uint SizeOfCommands;
|
||||||
|
public uint Flags;
|
||||||
|
// 64-bit header has an extra 32-bit Reserved field
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MachOLoadCommand
|
||||||
|
{
|
||||||
|
public uint Command;
|
||||||
|
public uint Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MachOSegmentCommand
|
||||||
|
{
|
||||||
|
// MachOLoadCommand
|
||||||
|
[String(FixedSize = 16)]
|
||||||
|
public string Name;
|
||||||
|
public uint VirtualAddress;
|
||||||
|
public uint VirtualSize;
|
||||||
|
public uint ImageOffset;
|
||||||
|
public uint ImageSize;
|
||||||
|
public uint VMMaxProt;
|
||||||
|
public uint VMInitProt;
|
||||||
|
public uint NumSections;
|
||||||
|
public uint Flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MachOSection
|
||||||
|
{
|
||||||
|
[String(FixedSize = 16)]
|
||||||
|
public string Name;
|
||||||
|
[String(FixedSize = 16)]
|
||||||
|
public string SegmentName;
|
||||||
|
public uint Address;
|
||||||
|
public uint Size;
|
||||||
|
public uint ImageOffset;
|
||||||
|
public uint Align;
|
||||||
|
public uint ImageRelocOffset;
|
||||||
|
public uint NumRelocEntries;
|
||||||
|
public uint Flags;
|
||||||
|
public uint Reserved1;
|
||||||
|
public uint Reserved2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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 class MachOLinkEditDataCommand
|
||||||
|
{
|
||||||
|
// MachOLoadCommand
|
||||||
|
public uint Offset;
|
||||||
|
public uint Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
150
Il2CppInspector/MachOReader.cs
Normal file
150
Il2CppInspector/MachOReader.cs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
|
||||||
|
namespace Il2CppInspector
|
||||||
|
{
|
||||||
|
internal class MachOReader : FileFormatReader<MachOReader>
|
||||||
|
{
|
||||||
|
private MachOHeader header;
|
||||||
|
private uint pFuncTable;
|
||||||
|
private uint sFuncTable;
|
||||||
|
private uint fatIndex;
|
||||||
|
|
||||||
|
public MachOReader(Stream stream) : base(stream) { }
|
||||||
|
|
||||||
|
public override string Arch {
|
||||||
|
get {
|
||||||
|
switch ((MachO)header.CPUType) {
|
||||||
|
case MachO.CPU_TYPE_ARM:
|
||||||
|
case MachO.CPU_TYPE_ARM64:
|
||||||
|
return "ARM";
|
||||||
|
case MachO.CPU_TYPE_X86:
|
||||||
|
case MachO.CPU_TYPE_X86_64:
|
||||||
|
return "x86";
|
||||||
|
default:
|
||||||
|
return "Unsupported";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool Init() {
|
||||||
|
fatIndex = (uint)Position;
|
||||||
|
|
||||||
|
// Detect endianness - default is little-endianness
|
||||||
|
MachO magic = (MachO)ReadUInt32();
|
||||||
|
if (magic == MachO.MH_CIGAM || magic == MachO.MH_CIGAM_64) {
|
||||||
|
Endianness = Endianness.Big;
|
||||||
|
}
|
||||||
|
else if (magic != MachO.MH_MAGIC && magic != MachO.MH_MAGIC_64) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Endianness: {0}", Endianness);
|
||||||
|
|
||||||
|
Position = fatIndex;
|
||||||
|
header = ReadObject<MachOHeader>();
|
||||||
|
|
||||||
|
// 64-bit files have an extra 4 bytes after the header
|
||||||
|
bool is64 = false;
|
||||||
|
if (magic == MachO.MH_MAGIC_64) {
|
||||||
|
is64 = true;
|
||||||
|
ReadUInt32();
|
||||||
|
}
|
||||||
|
Console.WriteLine("Architecture: {0}-bit", is64 ? 64 : 32);
|
||||||
|
|
||||||
|
// Must be executable file
|
||||||
|
if ((MachO) header.FileType != MachO.MH_EXECUTE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Console.WriteLine("CPU Type: " + (MachO)header.CPUType);
|
||||||
|
|
||||||
|
MachOLinkEditDataCommand functionStarts = null;
|
||||||
|
|
||||||
|
for (var c = 0; c < header.NumCommands; c++) {
|
||||||
|
var startPos = Position;
|
||||||
|
var loadCommand = ReadObject<MachOLoadCommand>();
|
||||||
|
|
||||||
|
if ((MachO)loadCommand.Command == MachO.LC_SEGMENT) {
|
||||||
|
var segment = ReadObject<MachOSegmentCommand>();
|
||||||
|
if (segment.Name == "__TEXT") {
|
||||||
|
for (int s = 0; s < segment.NumSections; s++) {
|
||||||
|
var section = ReadObject<MachOSection>();
|
||||||
|
if (section.Name == "__text")
|
||||||
|
GlobalOffset = section.ImageOffset - section.Address + fatIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((MachO)loadCommand.Command == MachO.LC_SEGMENT_64) {
|
||||||
|
var segment = ReadObject<MachOSegmentCommand64>();
|
||||||
|
if (segment.Name == "__TEXT") {
|
||||||
|
for (int s = 0; s < segment.NumSections; s++) {
|
||||||
|
var section = ReadObject<MachOSection64>();
|
||||||
|
if (section.Name == "__text")
|
||||||
|
GlobalOffset = section.ImageOffset - (uint)section.Address + fatIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((MachO)loadCommand.Command == MachO.LC_FUNCTION_STARTS) {
|
||||||
|
functionStarts = ReadObject<MachOLinkEditDataCommand>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// There might be other data after the load command so always use the specified total size to step forwards
|
||||||
|
Position = startPos + loadCommand.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must find LC_FUNCTION_STARTS load command
|
||||||
|
if (functionStarts == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pFuncTable = functionStarts.Offset + fatIndex;
|
||||||
|
sFuncTable = functionStarts.Size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint[] GetSearchLocations() {
|
||||||
|
Position = pFuncTable;
|
||||||
|
var functionPointers = new List<uint>();
|
||||||
|
|
||||||
|
// Decompress ELB128 list of function offsets
|
||||||
|
// https://en.wikipedia.org/wiki/LEB128
|
||||||
|
uint previous = 0;
|
||||||
|
while (Position < pFuncTable + sFuncTable) {
|
||||||
|
uint result = 0;
|
||||||
|
int shift = 0;
|
||||||
|
byte b;
|
||||||
|
do {
|
||||||
|
b = ReadByte();
|
||||||
|
result |= (uint)((b & 0x7f) << shift);
|
||||||
|
shift += 7;
|
||||||
|
} while ((b & 0x80) != 0);
|
||||||
|
if (result > 0) {
|
||||||
|
if (previous == 0)
|
||||||
|
result &= 0xffffffc;
|
||||||
|
previous += result;
|
||||||
|
functionPointers.Add(previous + fatIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return functionPointers.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint MapVATR(uint uiAddr) {
|
||||||
|
return uiAddr + GlobalOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void FinalizeInit(Il2CppReader il2cpp) {
|
||||||
|
// Mach-O function pointers have an annoying habit of being 1-off
|
||||||
|
il2cpp.PtrCodeRegistration.methodPointers =
|
||||||
|
il2cpp.PtrCodeRegistration.methodPointers.Select(x => x - 1).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user