Add Fat Mach-O (Universal Binary) support
Fix fieldOffsets bug in some metadata versions Add support for generic multi-architecture binaries Add Mach-O section RVA mapping
This commit is contained in:
@@ -43,7 +43,7 @@ namespace Il2CppInspector
|
|||||||
var fieldEnd = typeDef.fieldStart + typeDef.field_count;
|
var fieldEnd = typeDef.fieldStart + typeDef.field_count;
|
||||||
for (int i = typeDef.fieldStart; i < fieldEnd; ++i) {
|
for (int i = typeDef.fieldStart; i < fieldEnd; ++i) {
|
||||||
var pField = metadata.Fields[i];
|
var pField = metadata.Fields[i];
|
||||||
var pType = il2cpp.Code.GetTypeFromTypeIndex(pField.typeIndex);
|
var pType = il2cpp.GetTypeFromTypeIndex(pField.typeIndex);
|
||||||
var pDefault = metadata.GetFieldDefaultFromIndex(i);
|
var pDefault = metadata.GetFieldDefaultFromIndex(i);
|
||||||
writer.Write("\t");
|
writer.Write("\t");
|
||||||
if ((pType.attrs & DefineConstants.FIELD_ATTRIBUTE_PRIVATE) ==
|
if ((pType.attrs & DefineConstants.FIELD_ATTRIBUTE_PRIVATE) ==
|
||||||
@@ -59,7 +59,7 @@ namespace Il2CppInspector
|
|||||||
writer.Write($"{il2cpp.GetTypeName(pType)} {metadata.GetString(pField.nameIndex)}");
|
writer.Write($"{il2cpp.GetTypeName(pType)} {metadata.GetString(pField.nameIndex)}");
|
||||||
if (pDefault != null && pDefault.dataIndex != -1) {
|
if (pDefault != null && pDefault.dataIndex != -1) {
|
||||||
var pointer = metadata.GetDefaultValueFromIndex(pDefault.dataIndex);
|
var pointer = metadata.GetDefaultValueFromIndex(pDefault.dataIndex);
|
||||||
Il2CppType pTypeToUse = il2cpp.Code.GetTypeFromTypeIndex(pDefault.typeIndex);
|
Il2CppType pTypeToUse = il2cpp.GetTypeFromTypeIndex(pDefault.typeIndex);
|
||||||
if (pointer > 0) {
|
if (pointer > 0) {
|
||||||
metadata.Position = pointer;
|
metadata.Position = pointer;
|
||||||
object multi = null;
|
object multi = null;
|
||||||
@@ -110,14 +110,14 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
writer.Write("; // 0x{0:x}\n",
|
writer.Write("; // 0x{0:x}\n",
|
||||||
il2cpp.Code.GetFieldOffsetFromIndex(idx, i - typeDef.fieldStart));
|
il2cpp.GetFieldOffsetFromIndex(idx, i - typeDef.fieldStart));
|
||||||
}
|
}
|
||||||
writer.Write("\t// Methods\n");
|
writer.Write("\t// Methods\n");
|
||||||
var methodEnd = typeDef.methodStart + typeDef.method_count;
|
var methodEnd = typeDef.methodStart + typeDef.method_count;
|
||||||
for (int i = typeDef.methodStart; i < methodEnd; ++i) {
|
for (int i = typeDef.methodStart; i < methodEnd; ++i) {
|
||||||
var methodDef = metadata.Methods[i];
|
var methodDef = metadata.Methods[i];
|
||||||
writer.Write("\t");
|
writer.Write("\t");
|
||||||
Il2CppType pReturnType = il2cpp.Code.GetTypeFromTypeIndex(methodDef.returnType);
|
Il2CppType pReturnType = il2cpp.GetTypeFromTypeIndex(methodDef.returnType);
|
||||||
if ((methodDef.flags & DefineConstants.METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) ==
|
if ((methodDef.flags & DefineConstants.METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) ==
|
||||||
DefineConstants.METHOD_ATTRIBUTE_PRIVATE)
|
DefineConstants.METHOD_ATTRIBUTE_PRIVATE)
|
||||||
writer.Write("private ");
|
writer.Write("private ");
|
||||||
@@ -133,7 +133,7 @@ namespace Il2CppInspector
|
|||||||
for (int j = 0; j < methodDef.parameterCount; ++j) {
|
for (int j = 0; j < methodDef.parameterCount; ++j) {
|
||||||
Il2CppParameterDefinition pParam = metadata.parameterDefs[methodDef.parameterStart + j];
|
Il2CppParameterDefinition pParam = metadata.parameterDefs[methodDef.parameterStart + j];
|
||||||
string szParamName = metadata.GetString(pParam.nameIndex);
|
string szParamName = metadata.GetString(pParam.nameIndex);
|
||||||
Il2CppType pType = il2cpp.Code.GetTypeFromTypeIndex(pParam.typeIndex);
|
Il2CppType pType = il2cpp.GetTypeFromTypeIndex(pParam.typeIndex);
|
||||||
string szTypeName = il2cpp.GetTypeName(pType);
|
string szTypeName = il2cpp.GetTypeName(pType);
|
||||||
if ((pType.attrs & DefineConstants.PARAM_ATTRIBUTE_OPTIONAL) != 0)
|
if ((pType.attrs & DefineConstants.PARAM_ATTRIBUTE_OPTIONAL) != 0)
|
||||||
writer.Write("optional ");
|
writer.Write("optional ");
|
||||||
|
|||||||
@@ -40,13 +40,14 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Analyze data
|
// Analyze data
|
||||||
var il2cpp = Il2CppProcessor.LoadFromFile(imageFile, metaFile);
|
var il2cppProcessors = Il2CppProcessor.LoadFromFile(imageFile, metaFile);
|
||||||
if (il2cpp == null)
|
if (il2cppProcessors == null)
|
||||||
Environment.Exit(1);
|
Environment.Exit(1);
|
||||||
|
|
||||||
// Write output file
|
// Write output file
|
||||||
var dumper = new Il2CppDumper(il2cpp);
|
int i = 0;
|
||||||
dumper.WriteFile(outFile);
|
foreach (var il2cpp in il2cppProcessors)
|
||||||
|
new Il2CppDumper(il2cpp).WriteFile(outFile + "-" + (i++));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using NoisyCowStudios.Bin2Object;
|
using NoisyCowStudios.Bin2Object;
|
||||||
|
|
||||||
namespace Il2CppInspector
|
namespace Il2CppInspector
|
||||||
@@ -13,6 +15,9 @@ namespace Il2CppInspector
|
|||||||
public interface IFileFormatReader
|
public interface IFileFormatReader
|
||||||
{
|
{
|
||||||
BinaryObjectReader Stream { get; }
|
BinaryObjectReader Stream { get; }
|
||||||
|
uint NumImages { get; }
|
||||||
|
IEnumerable<IFileFormatReader> Images { get; }
|
||||||
|
IFileFormatReader this[uint index] { get; }
|
||||||
long Position { get; set; }
|
long Position { get; set; }
|
||||||
string Arch { get; }
|
string Arch { get; }
|
||||||
uint GlobalOffset { get; }
|
uint GlobalOffset { get; }
|
||||||
@@ -35,17 +40,26 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
public BinaryObjectReader Stream => this;
|
public BinaryObjectReader Stream => this;
|
||||||
|
|
||||||
|
public uint NumImages { get; protected set; } = 1;
|
||||||
|
|
||||||
public uint GlobalOffset { get; protected set; }
|
public uint GlobalOffset { get; protected set; }
|
||||||
|
|
||||||
public virtual string Arch => throw new NotImplementedException();
|
public virtual string Arch => throw new NotImplementedException();
|
||||||
|
|
||||||
public static T Load(string filename, uint offset = 0) {
|
public IEnumerable<IFileFormatReader> Images {
|
||||||
using (var stream = new FileStream(filename, FileMode.Open))
|
get {
|
||||||
return Load(stream, offset);
|
for (uint i = 0; i < NumImages; i++)
|
||||||
|
yield return this[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Load(Stream stream, uint offset = 0) {
|
public static T Load(string filename) {
|
||||||
stream.Position = offset;
|
using (var stream = new FileStream(filename, FileMode.Open))
|
||||||
|
return Load(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Load(Stream stream) {
|
||||||
|
stream.Position = 0;
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -53,11 +67,21 @@ namespace Il2CppInspector
|
|||||||
// Confirm file is valid and set up RVA mappings
|
// Confirm file is valid and set up RVA mappings
|
||||||
protected virtual bool Init() => throw new NotImplementedException();
|
protected virtual bool Init() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
// Choose a sub-binary within the image for multi-architecture binaries
|
||||||
|
public virtual IFileFormatReader this[uint index] {
|
||||||
|
get {
|
||||||
|
if (index == 0)
|
||||||
|
return this;
|
||||||
|
throw new IndexOutOfRangeException("Binary image index out of bounds");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find search locations in the machine code for Il2Cpp data
|
// Find search locations in the machine code for Il2Cpp data
|
||||||
public virtual uint[] GetSearchLocations() => throw new NotImplementedException();
|
public virtual uint[] GetSearchLocations() => throw new NotImplementedException();
|
||||||
|
|
||||||
// Map an RVA to an offset into the file image
|
// Map an RVA to an offset into the file image
|
||||||
public virtual uint MapVATR(uint uiAddr) => throw new NotImplementedException();
|
// No mapping by default
|
||||||
|
public virtual uint MapVATR(uint uiAddr) => uiAddr;
|
||||||
|
|
||||||
// Retrieve object(s) from specified RVA(s)
|
// Retrieve object(s) from specified RVA(s)
|
||||||
public U ReadMappedObject<U>(uint uiAddr) where U : new() {
|
public U ReadMappedObject<U>(uint uiAddr) where U : new() {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Il2CppInspector
|
|||||||
Metadata = metadata;
|
Metadata = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Il2CppProcessor LoadFromFile(string codeFile, string metadataFile) {
|
public static List<Il2CppProcessor> LoadFromFile(string codeFile, string metadataFile) {
|
||||||
// Load the metadata file
|
// Load the metadata file
|
||||||
Metadata metadata;
|
Metadata metadata;
|
||||||
try {
|
try {
|
||||||
@@ -34,36 +34,41 @@ 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 stream =
|
||||||
((IFileFormatReader) ElfReader.Load(memoryStream) ??
|
(((IFileFormatReader) ElfReader.Load(memoryStream) ??
|
||||||
PEReader.Load(memoryStream)) ??
|
PEReader.Load(memoryStream)) ??
|
||||||
MachOReader.Load(memoryStream);
|
MachOReader.Load(memoryStream)) ??
|
||||||
|
UBReader.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
Il2CppReader il2cpp;
|
var processors = new List<Il2CppProcessor>();
|
||||||
|
foreach (var image in stream.Images) {
|
||||||
|
Il2CppReader il2cpp;
|
||||||
|
|
||||||
// We are currently supporting x86 and ARM architectures
|
// We are currently supporting x86 and ARM architectures
|
||||||
switch (stream.Arch) {
|
switch (image.Arch) {
|
||||||
case "x86":
|
case "x86":
|
||||||
il2cpp = new Il2CppReaderX86(stream);
|
il2cpp = new Il2CppReaderX86(image);
|
||||||
break;
|
break;
|
||||||
case "ARM":
|
case "ARM":
|
||||||
il2cpp = new Il2CppReaderARM(stream);
|
il2cpp = new Il2CppReaderARM(image);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Console.Error.WriteLine("Unsupported architecture");
|
Console.Error.WriteLine("Unsupported architecture");
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find code and metadata regions
|
||||||
|
if (!il2cpp.Load(metadata.Version)) {
|
||||||
|
Console.Error.WriteLine("Could not process IL2CPP image");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
processors.Add(new Il2CppProcessor(il2cpp, metadata));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return processors;
|
||||||
// Find code and metadata regions
|
|
||||||
if (!il2cpp.Load(metadata.Version)) {
|
|
||||||
Console.Error.WriteLine("Could not process IL2CPP image");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Il2CppProcessor(il2cpp, metadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetTypeName(Il2CppType pType) {
|
public string GetTypeName(Il2CppType pType) {
|
||||||
@@ -106,6 +111,31 @@ namespace Il2CppInspector
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Il2CppType GetTypeFromTypeIndex(int idx) {
|
||||||
|
return Code.PtrMetadataRegistration.types[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType) {
|
||||||
|
// Versions from 22 onwards use an array of pointers in fieldOffsets
|
||||||
|
bool fieldOffsetsArePointers = (Metadata.Version >= 22);
|
||||||
|
|
||||||
|
// Some variants of 21 also use an array of pointers
|
||||||
|
if (Metadata.Version == 21) {
|
||||||
|
var f = Code.PtrMetadataRegistration.fieldOffsets;
|
||||||
|
fieldOffsetsArePointers = (f[0] == 0 && f[1] == 0 && f[2] == 0 && f[3] == 0 && f[4] == 0 && f[5] > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All older versions use values directly in the array
|
||||||
|
if (!fieldOffsetsArePointers) {
|
||||||
|
var typeDef = Metadata.Types[typeIndex];
|
||||||
|
return Code.PtrMetadataRegistration.fieldOffsets[typeDef.fieldStart + fieldIndexInType];
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = Code.PtrMetadataRegistration.fieldOffsets[typeIndex];
|
||||||
|
Code.Image.Stream.Position = Code.Image.MapVATR((uint)ptr) + 4 * fieldIndexInType;
|
||||||
|
return Code.Image.Stream.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
private readonly string[] szTypeString =
|
private readonly string[] szTypeString =
|
||||||
{
|
{
|
||||||
"END",
|
"END",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
protected Il2CppReader(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) {
|
protected Il2CppReader(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) {
|
||||||
Image = stream;
|
Image = stream;
|
||||||
Configure(codeRegistration, metadataRegistration);
|
Configure(Image, codeRegistration, metadataRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Il2CppCodeRegistration PtrCodeRegistration { get; protected set; }
|
public Il2CppCodeRegistration PtrCodeRegistration { get; protected set; }
|
||||||
@@ -27,44 +27,35 @@ namespace Il2CppInspector
|
|||||||
protected abstract (uint, uint) Search(uint loc, uint globalOffset);
|
protected abstract (uint, uint) Search(uint loc, uint globalOffset);
|
||||||
|
|
||||||
// Check all search locations
|
// Check all search locations
|
||||||
public bool Load(int version) {
|
public bool Load(int version, uint imageIndex = 0) {
|
||||||
|
var subImage = Image[imageIndex];
|
||||||
Image.Stream.Version = version;
|
Image.Stream.Version = version;
|
||||||
var addrs = Image.GetSearchLocations();
|
var addrs = subImage.GetSearchLocations();
|
||||||
foreach (var loc in addrs)
|
foreach (var loc in addrs)
|
||||||
if (loc != 0) {
|
if (loc != 0) {
|
||||||
var (code, metadata) = Search(loc, Image.GlobalOffset);
|
var (code, metadata) = Search(loc, Image.GlobalOffset);
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
Configure(code, metadata);
|
Configure(subImage, code, metadata);
|
||||||
Image.FinalizeInit(this);
|
subImage.FinalizeInit(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Configure(uint codeRegistration, uint metadataRegistration) {
|
private void Configure(IFileFormatReader image, uint codeRegistration, uint metadataRegistration) {
|
||||||
PtrCodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
|
PtrCodeRegistration = image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
|
||||||
PtrMetadataRegistration = Image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
|
PtrMetadataRegistration = image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
|
||||||
PtrCodeRegistration.methodPointers = Image.ReadMappedArray<uint>(PtrCodeRegistration.pmethodPointers,
|
PtrCodeRegistration.methodPointers = image.ReadMappedArray<uint>(PtrCodeRegistration.pmethodPointers,
|
||||||
(int) PtrCodeRegistration.methodPointersCount);
|
(int) PtrCodeRegistration.methodPointersCount);
|
||||||
PtrMetadataRegistration.fieldOffsets = Image.ReadMappedArray<int>(PtrMetadataRegistration.pfieldOffsets,
|
PtrMetadataRegistration.fieldOffsets = image.ReadMappedArray<int>(PtrMetadataRegistration.pfieldOffsets,
|
||||||
PtrMetadataRegistration.fieldOffsetsCount);
|
PtrMetadataRegistration.fieldOffsetsCount);
|
||||||
var types = Image.ReadMappedArray<uint>(PtrMetadataRegistration.ptypes, PtrMetadataRegistration.typesCount);
|
var types = image.ReadMappedArray<uint>(PtrMetadataRegistration.ptypes, PtrMetadataRegistration.typesCount);
|
||||||
PtrMetadataRegistration.types = new Il2CppType[PtrMetadataRegistration.typesCount];
|
PtrMetadataRegistration.types = new Il2CppType[PtrMetadataRegistration.typesCount];
|
||||||
for (int i = 0; i < PtrMetadataRegistration.typesCount; ++i) {
|
for (int i = 0; i < PtrMetadataRegistration.typesCount; ++i) {
|
||||||
PtrMetadataRegistration.types[i] = Image.ReadMappedObject<Il2CppType>(types[i]);
|
PtrMetadataRegistration.types[i] = image.ReadMappedObject<Il2CppType>(types[i]);
|
||||||
PtrMetadataRegistration.types[i].Init();
|
PtrMetadataRegistration.types[i].Init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Il2CppType GetTypeFromTypeIndex(int idx) {
|
|
||||||
return PtrMetadataRegistration.types[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType) {
|
|
||||||
var ptr = PtrMetadataRegistration.fieldOffsets[typeIndex];
|
|
||||||
Image.Stream.Position = Image.MapVATR((uint) ptr) + 4 * fieldIndexInType;
|
|
||||||
return Image.Stream.ReadInt32();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Il2CppInspector
|
|||||||
// Assembly bytes to search for at start of each function
|
// Assembly bytes to search for at start of each function
|
||||||
uint metadataRegistration, codeRegistration;
|
uint metadataRegistration, codeRegistration;
|
||||||
|
|
||||||
// ARM
|
// ARMv7
|
||||||
var bytes = new byte[] { 0x1c, 0x0, 0x9f, 0xe5, 0x1c, 0x10, 0x9f, 0xe5, 0x1c, 0x20, 0x9f, 0xe5 };
|
var bytes = new byte[] { 0x1c, 0x0, 0x9f, 0xe5, 0x1c, 0x10, 0x9f, 0xe5, 0x1c, 0x20, 0x9f, 0xe5 };
|
||||||
Image.Position = loc;
|
Image.Position = loc;
|
||||||
var buff = Image.ReadBytes(12);
|
var buff = Image.ReadBytes(12);
|
||||||
@@ -35,7 +35,7 @@ namespace Il2CppInspector
|
|||||||
return (codeRegistration, metadataRegistration);
|
return (codeRegistration, metadataRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARM metadata v23
|
// ARMv7 metadata v23
|
||||||
Image.Position = loc;
|
Image.Position = loc;
|
||||||
|
|
||||||
// Check for ADD Rx, PC in relevant parts of function
|
// Check for ADD Rx, PC in relevant parts of function
|
||||||
@@ -51,7 +51,7 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
// Follow path to code pointer
|
// Follow path to code pointer
|
||||||
var pCode = decodeMovImm32(func.Skip(8).Take(4).Concat(func.Skip(14).Take(4)).ToArray());
|
var pCode = decodeMovImm32(func.Skip(8).Take(4).Concat(func.Skip(14).Take(4)).ToArray());
|
||||||
codeRegistration = pCode + loc + 0x1A - globalOffset;
|
codeRegistration = pCode + loc + 0x1A + globalOffset;
|
||||||
|
|
||||||
return (codeRegistration, metadataRegistration);
|
return (codeRegistration, metadataRegistration);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ namespace Il2CppInspector
|
|||||||
private MachOHeader header;
|
private MachOHeader header;
|
||||||
private uint pFuncTable;
|
private uint pFuncTable;
|
||||||
private uint sFuncTable;
|
private uint sFuncTable;
|
||||||
private uint fatIndex;
|
private bool is64;
|
||||||
|
private List<MachOSection> sections = new List<MachOSection>();
|
||||||
|
private List<MachOSection64> sections64 = new List<MachOSection64>();
|
||||||
|
|
||||||
public MachOReader(Stream stream) : base(stream) { }
|
public MachOReader(Stream stream) : base(stream) { }
|
||||||
|
|
||||||
@@ -37,8 +39,6 @@ namespace Il2CppInspector
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override bool Init() {
|
protected override bool Init() {
|
||||||
fatIndex = (uint)Position;
|
|
||||||
|
|
||||||
// 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 (magic == MachO.MH_CIGAM || magic == MachO.MH_CIGAM_64) {
|
||||||
@@ -50,11 +50,11 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
Console.WriteLine("Endianness: {0}", Endianness);
|
Console.WriteLine("Endianness: {0}", Endianness);
|
||||||
|
|
||||||
Position = fatIndex;
|
Position -= sizeof(uint);
|
||||||
header = ReadObject<MachOHeader>();
|
header = ReadObject<MachOHeader>();
|
||||||
|
|
||||||
// 64-bit files have an extra 4 bytes after the header
|
// 64-bit files have an extra 4 bytes after the header
|
||||||
bool is64 = false;
|
is64 = false;
|
||||||
if (magic == MachO.MH_MAGIC_64) {
|
if (magic == MachO.MH_MAGIC_64) {
|
||||||
is64 = true;
|
is64 = true;
|
||||||
ReadUInt32();
|
ReadUInt32();
|
||||||
@@ -75,21 +75,23 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
if ((MachO)loadCommand.Command == MachO.LC_SEGMENT) {
|
if ((MachO)loadCommand.Command == MachO.LC_SEGMENT) {
|
||||||
var segment = ReadObject<MachOSegmentCommand>();
|
var segment = ReadObject<MachOSegmentCommand>();
|
||||||
if (segment.Name == "__TEXT") {
|
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>();
|
||||||
|
sections.Add(section);
|
||||||
if (section.Name == "__text")
|
if (section.Name == "__text")
|
||||||
GlobalOffset = section.ImageOffset - section.Address + fatIndex;
|
GlobalOffset = section.Address - section.ImageOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((MachO)loadCommand.Command == MachO.LC_SEGMENT_64) {
|
else if ((MachO)loadCommand.Command == MachO.LC_SEGMENT_64) {
|
||||||
var segment = ReadObject<MachOSegmentCommand64>();
|
var segment = ReadObject<MachOSegmentCommand64>();
|
||||||
if (segment.Name == "__TEXT") {
|
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<MachOSection64>();
|
var section64 = ReadObject<MachOSection64>();
|
||||||
if (section.Name == "__text")
|
sections64.Add(section64);
|
||||||
GlobalOffset = section.ImageOffset - (uint)section.Address + fatIndex;
|
if (section64.Name == "__text")
|
||||||
|
GlobalOffset = (uint)section64.Address - section64.ImageOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +108,7 @@ namespace Il2CppInspector
|
|||||||
if (functionStarts == null)
|
if (functionStarts == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
pFuncTable = functionStarts.Offset + fatIndex;
|
pFuncTable = functionStarts.Offset;
|
||||||
sFuncTable = functionStarts.Size;
|
sFuncTable = functionStarts.Size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -131,20 +133,25 @@ namespace Il2CppInspector
|
|||||||
if (previous == 0)
|
if (previous == 0)
|
||||||
result &= 0xffffffc;
|
result &= 0xffffffc;
|
||||||
previous += result;
|
previous += result;
|
||||||
functionPointers.Add(previous + fatIndex);
|
functionPointers.Add(previous);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return functionPointers.ToArray();
|
return functionPointers.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override uint MapVATR(uint uiAddr) {
|
|
||||||
return uiAddr + GlobalOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void FinalizeInit(Il2CppReader il2cpp) {
|
public override void FinalizeInit(Il2CppReader il2cpp) {
|
||||||
// Mach-O function pointers have an annoying habit of being 1-off
|
// Mach-O function pointers have an annoying habit of being 1-off
|
||||||
il2cpp.PtrCodeRegistration.methodPointers =
|
il2cpp.PtrCodeRegistration.methodPointers =
|
||||||
il2cpp.PtrCodeRegistration.methodPointers.Select(x => x - 1).ToArray();
|
il2cpp.PtrCodeRegistration.methodPointers.Select(x => x - 1).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override uint MapVATR(uint uiAddr) {
|
||||||
|
if (!is64) {
|
||||||
|
var section = sections.First(x => uiAddr >= x.Address && uiAddr <= (x.Address + x.Size));
|
||||||
|
return uiAddr - (section.Address - section.ImageOffset);
|
||||||
|
}
|
||||||
|
var section64 = sections64.First(x => uiAddr >= x.Address && uiAddr <= (x.Address + x.Size));
|
||||||
|
return uiAddr - ((uint)section64.Address - section64.ImageOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
Il2CppInspector/UBHeaders.cs
Normal file
36
Il2CppInspector/UBHeaders.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
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://cocoaintheshell.whine.fr/2009/07/universal-binary-mach-o-format/
|
||||||
|
|
||||||
|
public enum UB : uint
|
||||||
|
{
|
||||||
|
FAT_MAGIC = 0xcafebabe
|
||||||
|
}
|
||||||
|
|
||||||
|
// Big-endian
|
||||||
|
internal class FatHeader
|
||||||
|
{
|
||||||
|
public uint Magic;
|
||||||
|
public uint NumArch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Big-endian
|
||||||
|
internal class FatArch
|
||||||
|
{
|
||||||
|
public uint CPUType;
|
||||||
|
public uint CPUSubType;
|
||||||
|
public uint Offset;
|
||||||
|
public uint Size;
|
||||||
|
public uint Align;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
Il2CppInspector/UBReader.cs
Normal file
47
Il2CppInspector/UBReader.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
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 UBReader : FileFormatReader<UBReader>
|
||||||
|
{
|
||||||
|
private FatHeader header;
|
||||||
|
|
||||||
|
public UBReader(Stream stream) : base(stream) { }
|
||||||
|
|
||||||
|
protected override bool Init() {
|
||||||
|
// Fat headers are always big-endian regardless of architectures
|
||||||
|
Endianness = Endianness.Big;
|
||||||
|
|
||||||
|
header = ReadObject<FatHeader>();
|
||||||
|
|
||||||
|
if ((UB) header.Magic != UB.FAT_MAGIC)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NumImages = header.NumArch;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IFileFormatReader this[uint index] {
|
||||||
|
get {
|
||||||
|
Position = 0x8 + 0x14 * index; // sizeof(FatHeader), sizeof(FatArch)
|
||||||
|
Endianness = Endianness.Big;
|
||||||
|
|
||||||
|
var arch = ReadObject<FatArch>();
|
||||||
|
|
||||||
|
Position = arch.Offset;
|
||||||
|
Endianness = Endianness.Little;
|
||||||
|
return MachOReader.Load(new MemoryStream(ReadBytes((int)arch.Size)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user