Additional refactoring

This commit is contained in:
Katy Coe
2017-10-23 16:12:40 +02:00
parent 474faa009c
commit 0ab1b75ef4
12 changed files with 117 additions and 190 deletions

View File

@@ -8,9 +8,9 @@ namespace Il2CppInspector
{ {
public class Il2CppDumper public class Il2CppDumper
{ {
private readonly Il2CppProcessor il2cpp; private readonly Il2CppInspector il2cpp;
public Il2CppDumper(Il2CppProcessor proc) { public Il2CppDumper(Il2CppInspector proc) {
il2cpp = proc; il2cpp = proc;
} }
@@ -100,7 +100,7 @@ namespace Il2CppInspector
} }
if (methodDef.methodIndex >= 0) if (methodDef.methodIndex >= 0)
writer.Write("); // {0:x} - {1}\n", writer.Write("); // {0:x} - {1}\n",
il2cpp.Code.PtrCodeRegistration.methodPointers[methodDef.methodIndex], il2cpp.Binary.MethodPointers[methodDef.methodIndex],
methodDef.methodIndex); methodDef.methodIndex);
else else
writer.Write("); // 0 - -1\n"); writer.Write("); // 0 - -1\n");

View File

@@ -40,13 +40,13 @@ namespace Il2CppInspector
} }
// Analyze data // Analyze data
var il2cppProcessors = Il2CppProcessor.LoadFromFile(imageFile, metaFile); var il2cppInspectors = Il2CppInspector.LoadFromFile(imageFile, metaFile);
if (il2cppProcessors == null) if (il2cppInspectors == null)
Environment.Exit(1); Environment.Exit(1);
// Write output file // Write output file
int i = 0; int i = 0;
foreach (var il2cpp in il2cppProcessors) foreach (var il2cpp in il2cppInspectors)
new Il2CppDumper(il2cpp).WriteFile(outFile + (i++ > 0 ? "-" + (i-1) : "")); new Il2CppDumper(il2cpp).WriteFile(outFile + (i++ > 0 ? "-" + (i-1) : ""));
} }
} }

View File

@@ -21,11 +21,11 @@ namespace Il2CppInspector
long Position { get; set; } long Position { get; set; }
string Arch { get; } string Arch { get; }
uint GlobalOffset { get; } uint GlobalOffset { get; }
uint[] GetSearchLocations(); uint[] GetFunctionTable();
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); void FinalizeInit(Il2CppBinary il2cpp);
byte[] ReadBytes(int count); byte[] ReadBytes(int count);
ulong ReadUInt64(); ulong ReadUInt64();
@@ -77,7 +77,7 @@ namespace Il2CppInspector
} }
// 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[] GetFunctionTable() => throw new NotImplementedException();
// Map an RVA to an offset into the file image // Map an RVA to an offset into the file image
// No mapping by default // No mapping by default
@@ -93,6 +93,6 @@ namespace Il2CppInspector
} }
// Perform file format-based post-load manipulations to the IL2Cpp data // Perform file format-based post-load manipulations to the IL2Cpp data
public virtual void FinalizeInit(Il2CppReader il2cpp) { } public virtual void FinalizeInit(Il2CppBinary il2cpp) { }
} }
} }

View File

@@ -47,7 +47,7 @@ namespace Il2CppInspector
return true; return true;
} }
public override uint[] GetSearchLocations() { public override uint[] GetFunctionTable() {
// Find dynamic section // Find dynamic section
var dynamic = new elf_32_shdr(); var dynamic = new elf_32_shdr();
var PT_DYNAMIC = program_table_element.First(x => x.p_type == 2u); var PT_DYNAMIC = program_table_element.First(x => x.p_type == 2u);

View File

@@ -113,7 +113,7 @@ namespace Il2CppInspector
return true; return true;
} }
public override uint[] GetSearchLocations() { public override uint[] GetFunctionTable() {
Position = pFuncTable; Position = pFuncTable;
var functionPointers = new List<uint>(); var functionPointers = new List<uint>();
@@ -139,10 +139,9 @@ namespace Il2CppInspector
return functionPointers.ToArray(); return functionPointers.ToArray();
} }
public override void FinalizeInit(Il2CppReader il2cpp) { public override void FinalizeInit(Il2CppBinary 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.MethodPointers = il2cpp.MethodPointers.Select(x => x - 1).ToArray();
il2cpp.PtrCodeRegistration.methodPointers.Select(x => x - 1).ToArray();
} }
public override uint MapVATR(uint uiAddr) { public override uint MapVATR(uint uiAddr) {

View File

@@ -80,7 +80,7 @@ namespace Il2CppInspector
return true; return true;
} }
public override uint[] GetSearchLocations() { public override uint[] GetFunctionTable() {
Position = pFuncTable; Position = pFuncTable;
var addrs = new List<uint>(); var addrs = new List<uint>();
uint addr; uint addr;

View File

@@ -0,0 +1,62 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using System.Collections.Generic;
namespace Il2CppInspector
{
public abstract class Il2CppBinary
{
public IFileFormatReader Image { get; }
public Il2CppCodeRegistration CodeRegistration { get; protected set; }
public Il2CppMetadataRegistration MetadataRegistration { get; protected set; }
public uint[] MethodPointers { get; set; }
public int[] FieldOffsets { get; private set; }
public List<Il2CppType> Types { get; } = new List<Il2CppType>();
protected Il2CppBinary(IFileFormatReader stream) {
Image = stream;
}
protected Il2CppBinary(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) {
Image = stream;
Configure(Image, codeRegistration, metadataRegistration);
}
// Architecture-specific search function
protected abstract (uint, uint) ConsiderCode(uint loc, uint globalOffset);
// Check all search locations
public bool Initialize(int version, uint imageIndex = 0) {
var subImage = Image[imageIndex];
Image.Stream.Version = version;
var addrs = subImage.GetFunctionTable();
foreach (var loc in addrs)
if (loc != 0) {
var (code, metadata) = ConsiderCode(loc, Image.GlobalOffset);
if (code != 0) {
Configure(subImage, code, metadata);
subImage.FinalizeInit(this);
return true;
}
}
return false;
}
private void Configure(IFileFormatReader image, uint codeRegistration, uint metadataRegistration) {
CodeRegistration = image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
MetadataRegistration = image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
MethodPointers = image.ReadMappedArray<uint>(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount);
FieldOffsets = image.ReadMappedArray<int>(MetadataRegistration.pfieldOffsets, MetadataRegistration.fieldOffsetsCount);
var types = image.ReadMappedArray<uint>(MetadataRegistration.ptypes, MetadataRegistration.typesCount);
for (int i = 0; i < MetadataRegistration.typesCount; i++)
Types.Add(image.ReadMappedObject<Il2CppType>(types[i]));
}
}
}

View File

@@ -9,13 +9,13 @@ using System.Linq;
namespace Il2CppInspector namespace Il2CppInspector
{ {
internal class Il2CppReaderARM : Il2CppReader internal class Il2CppBinaryARM : Il2CppBinary
{ {
public Il2CppReaderARM(IFileFormatReader stream) : base(stream) { } public Il2CppBinaryARM(IFileFormatReader stream) : base(stream) { }
public Il2CppReaderARM(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { } public Il2CppBinaryARM(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { }
protected override (uint, uint) Search(uint loc, uint globalOffset) { protected override (uint, uint) ConsiderCode(uint loc, uint globalOffset) {
// 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;

View File

@@ -49,12 +49,6 @@ namespace Il2CppInspector
public uint interopDataCount; public uint interopDataCount;
[Version(Min = 23)] [Version(Min = 23)]
public uint interopData; public uint interopData;
// Properties
public uint[] methodPointers
{
get; set;
}
} }
#pragma warning disable CS0649 #pragma warning disable CS0649
@@ -78,16 +72,6 @@ namespace Il2CppInspector
public uint typeDefinitionsSizes; public uint typeDefinitionsSizes;
public uint metadataUsagesCount; public uint metadataUsagesCount;
public uint metadataUsages; public uint metadataUsages;
public int[] fieldOffsets
{
get; set;
}
public Il2CppType[] types
{
get; set;
}
} }
#pragma warning restore CS0649 #pragma warning restore CS0649
@@ -136,68 +120,13 @@ namespace Il2CppInspector
public class Il2CppType public class Il2CppType
{ {
public uint datapoint; public uint datapoint;
public Anonymous data { get; set; }
public uint bits; public uint bits;
public uint attrs { get; set; }
public Il2CppTypeEnum type { get; set; }
public uint num_mods { get; set; }
public uint byref { get; set; }
public uint pinned { get; set; }
public void Init() public uint attrs => bits & 0xffff;
{ public Il2CppTypeEnum type => (Il2CppTypeEnum)((bits >> 16) & 0xff);
var str = Convert.ToString(bits, 2); public uint num_mods => (bits >> 24) & 0x3f;
if (str.Length != 32) public bool byref => ((bits >> 30) & 1) == 1;
{ public bool pinned => (bits >> 31) == 1;
str = new string(Enumerable.Repeat('0', 32 - str.Length).Concat(str.ToCharArray()).ToArray());
}
attrs = Convert.ToUInt32(str.Substring(16, 16), 2);
type = (Il2CppTypeEnum)Convert.ToInt32(str.Substring(8, 8), 2);
num_mods = Convert.ToUInt32(str.Substring(2, 6), 2);
byref = Convert.ToUInt32(str.Substring(1, 1), 2);
pinned = Convert.ToUInt32(str.Substring(0, 1), 2);
data = new Anonymous() { dummy = datapoint };
}
public class Anonymous
{
public uint dummy;
public int klassIndex
{
get
{
return (int)dummy;
}
}
public uint type
{
get
{
return dummy;
}
}
public uint array
{
get
{
return dummy;
}
}
public int genericParameterIndex
{
get
{
return (int)dummy;
}
}
public uint generic_class
{
get
{
return dummy;
}
}
}
} }
public class Il2CppGenericClass public class Il2CppGenericClass

View File

@@ -8,11 +8,11 @@ using System.Linq;
namespace Il2CppInspector namespace Il2CppInspector
{ {
internal class Il2CppReaderX86 : Il2CppReader internal class Il2CppBinaryX86 : Il2CppBinary
{ {
public Il2CppReaderX86(IFileFormatReader stream) : base(stream) { } public Il2CppBinaryX86(IFileFormatReader stream) : base(stream) { }
public Il2CppReaderX86(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { } public Il2CppBinaryX86(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { }
protected override (uint, uint) Search(uint loc, uint globalOffset) { protected override (uint, uint) ConsiderCode(uint loc, uint globalOffset) {
uint funcPtr, metadata, code; uint funcPtr, metadata, code;
ushort opcode; ushort opcode;

View File

@@ -12,17 +12,17 @@ using System.Text;
namespace Il2CppInspector namespace Il2CppInspector
{ {
public class Il2CppProcessor public class Il2CppInspector
{ {
public Il2CppReader Code { get; } public Il2CppBinary Binary { get; }
public Metadata Metadata { get; } public Metadata Metadata { get; }
public Il2CppProcessor(Il2CppReader code, Metadata metadata) { public Il2CppInspector(Il2CppBinary binary, Metadata metadata) {
Code = code; Binary = binary;
Metadata = metadata; Metadata = metadata;
} }
public static List<Il2CppProcessor> LoadFromFile(string codeFile, string metadataFile) { public static List<Il2CppInspector> LoadFromFile(string codeFile, string metadataFile) {
// Load the metadata file // Load the metadata file
Metadata metadata; Metadata metadata;
try { try {
@@ -33,7 +33,7 @@ namespace Il2CppInspector
return null; return null;
} }
// Load the il2cpp code file (try ELF and PE) // Load the il2cpp code file (try ELF, PE, Mach-O and Universal Binary)
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) ??
@@ -45,17 +45,18 @@ namespace Il2CppInspector
return null; return null;
} }
var processors = new List<Il2CppProcessor>(); // Multi-image binaries may contain more than one Il2Cpp image
var processors = new List<Il2CppInspector>();
foreach (var image in stream.Images) { foreach (var image in stream.Images) {
Il2CppReader il2cpp; Il2CppBinary binary;
// We are currently supporting x86 and ARM architectures // We are currently supporting x86 and ARM architectures
switch (image.Arch) { switch (image.Arch) {
case "x86": case "x86":
il2cpp = new Il2CppReaderX86(image); binary = new Il2CppBinaryX86(image);
break; break;
case "ARM": case "ARM":
il2cpp = new Il2CppReaderARM(image); binary = new Il2CppBinaryARM(image);
break; break;
default: default:
Console.Error.WriteLine("Unsupported architecture"); Console.Error.WriteLine("Unsupported architecture");
@@ -63,11 +64,11 @@ namespace Il2CppInspector
} }
// Find code and metadata regions // Find code and metadata regions
if (!il2cpp.Load(metadata.Version)) { if (!binary.Initialize(metadata.Version)) {
Console.Error.WriteLine("Could not process IL2CPP image"); Console.Error.WriteLine("Could not process IL2CPP image");
} }
else { else {
processors.Add(new Il2CppProcessor(il2cpp, metadata)); processors.Add(new Il2CppInspector(binary, metadata));
} }
} }
return processors; return processors;
@@ -76,32 +77,29 @@ namespace Il2CppInspector
public string GetTypeName(Il2CppType pType) { public string GetTypeName(Il2CppType pType) {
string ret; string ret;
if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_CLASS || pType.type == Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) { if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_CLASS || pType.type == Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) {
Il2CppTypeDefinition klass = Metadata.Types[pType.data.klassIndex]; Il2CppTypeDefinition klass = Metadata.Types[pType.datapoint];
ret = Metadata.Strings[klass.nameIndex]; ret = Metadata.Strings[klass.nameIndex];
} }
else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
Il2CppGenericClass generic_class = Code.Image.ReadMappedObject<Il2CppGenericClass>(pType.data.generic_class); Il2CppGenericClass generic_class = Binary.Image.ReadMappedObject<Il2CppGenericClass>(pType.datapoint);
Il2CppTypeDefinition pMainDef = Metadata.Types[generic_class.typeDefinitionIndex]; Il2CppTypeDefinition pMainDef = Metadata.Types[generic_class.typeDefinitionIndex];
ret = Metadata.Strings[pMainDef.nameIndex]; ret = Metadata.Strings[pMainDef.nameIndex];
var typeNames = new List<string>(); var typeNames = new List<string>();
Il2CppGenericInst pInst = Code.Image.ReadMappedObject<Il2CppGenericInst>(generic_class.context.class_inst); Il2CppGenericInst pInst = Binary.Image.ReadMappedObject<Il2CppGenericInst>(generic_class.context.class_inst);
var pointers = Code.Image.ReadMappedArray<uint>(pInst.type_argv, (int)pInst.type_argc); var pointers = Binary.Image.ReadMappedArray<uint>(pInst.type_argv, (int)pInst.type_argc);
for (int i = 0; i < pInst.type_argc; ++i) { for (int i = 0; i < pInst.type_argc; ++i) {
var pOriType = Code.Image.ReadMappedObject<Il2CppType>(pointers[i]); var pOriType = Binary.Image.ReadMappedObject<Il2CppType>(pointers[i]);
pOriType.Init();
typeNames.Add(GetTypeName(pOriType)); typeNames.Add(GetTypeName(pOriType));
} }
ret += $"<{string.Join(", ", typeNames)}>"; ret += $"<{string.Join(", ", typeNames)}>";
} }
else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) { else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) {
Il2CppArrayType arrayType = Code.Image.ReadMappedObject<Il2CppArrayType>(pType.data.array); Il2CppArrayType arrayType = Binary.Image.ReadMappedObject<Il2CppArrayType>(pType.datapoint);
var type = Code.Image.ReadMappedObject<Il2CppType>(arrayType.etype); var type = Binary.Image.ReadMappedObject<Il2CppType>(arrayType.etype);
type.Init();
ret = $"{GetTypeName(type)}[]"; ret = $"{GetTypeName(type)}[]";
} }
else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) { else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) {
var type = Code.Image.ReadMappedObject<Il2CppType>(pType.data.type); var type = Binary.Image.ReadMappedObject<Il2CppType>(pType.datapoint);
type.Init();
ret = $"{GetTypeName(type)}[]"; ret = $"{GetTypeName(type)}[]";
} }
else { else {
@@ -114,7 +112,7 @@ namespace Il2CppInspector
} }
public Il2CppType GetTypeFromTypeIndex(int idx) { public Il2CppType GetTypeFromTypeIndex(int idx) {
return Code.PtrMetadataRegistration.types[idx]; return Binary.Types[idx];
} }
public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType) { public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType) {
@@ -123,19 +121,19 @@ namespace Il2CppInspector
// Some variants of 21 also use an array of pointers // Some variants of 21 also use an array of pointers
if (Metadata.Version == 21) { if (Metadata.Version == 21) {
var f = Code.PtrMetadataRegistration.fieldOffsets; var f = Binary.FieldOffsets;
fieldOffsetsArePointers = (f[0] == 0 && f[1] == 0 && f[2] == 0 && f[3] == 0 && f[4] == 0 && f[5] > 0); 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 // All older versions use values directly in the array
if (!fieldOffsetsArePointers) { if (!fieldOffsetsArePointers) {
var typeDef = Metadata.Types[typeIndex]; var typeDef = Metadata.Types[typeIndex];
return Code.PtrMetadataRegistration.fieldOffsets[typeDef.fieldStart + fieldIndexInType]; return Binary.FieldOffsets[typeDef.fieldStart + fieldIndexInType];
} }
var ptr = Code.PtrMetadataRegistration.fieldOffsets[typeIndex]; var ptr = Binary.FieldOffsets[typeIndex];
Code.Image.Stream.Position = Code.Image.MapVATR((uint)ptr) + 4 * fieldIndexInType; Binary.Image.Stream.Position = Binary.Image.MapVATR((uint)ptr) + 4 * fieldIndexInType;
return Code.Image.Stream.ReadInt32(); return Binary.Image.Stream.ReadInt32();
} }

View File

@@ -1,61 +0,0 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
namespace Il2CppInspector
{
public abstract class Il2CppReader
{
public IFileFormatReader Image { get; }
protected Il2CppReader(IFileFormatReader stream) {
Image = stream;
}
protected Il2CppReader(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) {
Image = stream;
Configure(Image, codeRegistration, metadataRegistration);
}
public Il2CppCodeRegistration PtrCodeRegistration { get; protected set; }
public Il2CppMetadataRegistration PtrMetadataRegistration { get; protected set; }
// Architecture-specific search function
protected abstract (uint, uint) Search(uint loc, uint globalOffset);
// Check all search locations
public bool Load(int version, uint imageIndex = 0) {
var subImage = Image[imageIndex];
Image.Stream.Version = version;
var addrs = subImage.GetSearchLocations();
foreach (var loc in addrs)
if (loc != 0) {
var (code, metadata) = Search(loc, Image.GlobalOffset);
if (code != 0) {
Configure(subImage, code, metadata);
subImage.FinalizeInit(this);
return true;
}
}
return false;
}
private void Configure(IFileFormatReader image, uint codeRegistration, uint metadataRegistration) {
PtrCodeRegistration = image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
PtrMetadataRegistration = image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
PtrCodeRegistration.methodPointers = image.ReadMappedArray<uint>(PtrCodeRegistration.pmethodPointers,
(int) PtrCodeRegistration.methodPointersCount);
PtrMetadataRegistration.fieldOffsets = image.ReadMappedArray<int>(PtrMetadataRegistration.pfieldOffsets,
PtrMetadataRegistration.fieldOffsetsCount);
var types = image.ReadMappedArray<uint>(PtrMetadataRegistration.ptypes, PtrMetadataRegistration.typesCount);
PtrMetadataRegistration.types = new Il2CppType[PtrMetadataRegistration.typesCount];
for (int i = 0; i < PtrMetadataRegistration.typesCount; ++i) {
PtrMetadataRegistration.types[i] = image.ReadMappedObject<Il2CppType>(types[i]);
PtrMetadataRegistration.types[i].Init();
}
}
}
}