From 8045f2cfd7eef699918186bb66f41eae7f49dde8 Mon Sep 17 00:00:00 2001 From: Carter Bush Date: Mon, 27 Jan 2020 07:31:21 +1100 Subject: [PATCH] Output: IDA Python script generation IL2CPP: Implement MetadataUsages --- Il2CppDumper/Il2CppIDAScriptDumper.cs | 125 ++++++++++++++++++++++++++ Il2CppDumper/Program.cs | 7 +- Il2CppInspector/Il2CppBinary.cs | 6 ++ Il2CppInspector/Il2CppInspector.cs | 26 ++++++ Il2CppInspector/Metadata.cs | 30 +++++++ Il2CppInspector/MetadataClasses.cs | 12 +++ 6 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 Il2CppDumper/Il2CppIDAScriptDumper.cs diff --git a/Il2CppDumper/Il2CppIDAScriptDumper.cs b/Il2CppDumper/Il2CppIDAScriptDumper.cs new file mode 100644 index 0000000..613ae0d --- /dev/null +++ b/Il2CppDumper/Il2CppIDAScriptDumper.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; +using Il2CppInspector.Reflection; + +namespace Il2CppInspector +{ + public class Il2CppIDAScriptDumper + { + private Il2CppModel model; + + public Il2CppIDAScriptDumper(Il2CppModel model) => this.model = model; + + #region Writing + + public void WriteScriptToFile(string outputFile) { + using (var fs = new FileStream(outputFile, FileMode.Create)) + using (var sw = new StreamWriter(fs, Encoding.UTF8)) { + writeSectionHeader(sw, "Preamble"); + writePreamble(sw); + + writeSectionHeader(sw, "Methods"); + writeMethods(sw, this.model.Types); + + writeSectionHeader(sw, "Usages"); + writeUsages(sw, this.model); + } + } + + private static void writePreamble(StreamWriter writer) { + writeLines(writer, +@"#encoding: utf-8 +import idaapi + +def SetString(addr, comm): + global index + name = 'StringLiteral_' + str(index) + ret = idc.set_name(addr, name, SN_NOWARN) + idc.set_cmt(addr, comm, 1) + +def SetName(addr, name): + ret = idc.set_name(addr, name, SN_NOWARN | SN_NOCHECK) + if ret == 0: + new_name = name + '_' + str(addr) + ret = idc.set_name(addr, new_name, SN_NOWARN | SN_NOCHECK) + +index = 1 +" + ); + } + + private static void writeMethods(StreamWriter writer, IEnumerable types) { + foreach (var type in types.Where(t => t != null)) { + writeMethods(writer, type.Name, type.DeclaredConstructors); + writeMethods(writer, type.Name, type.DeclaredMethods); + } + } + + private static void writeMethods(StreamWriter writer, string typeName, IEnumerable methods) { + foreach (var method in methods.Where(m => m.VirtualAddress.HasValue)) { + writeLines(writer, + $"SetName({method.VirtualAddress.Value.Start.ToAddressString()}, '{typeName}$${method.Name}')" + ); + } + } + + private static void writeUsages(StreamWriter writer, Il2CppModel model) { + foreach (var usage in model.Package.MetadataUsages) { + switch (usage.Type) { + case MetadataUsageType.TypeInfo: + case MetadataUsageType.Type: + var type = model.GetTypeFromUsage(usage.SourceIndex); + writeLines(writer, + $"SetName({model.Package.BinaryMetadataUsages[usage.DestinationIndex].ToAddressString()}, 'Class${type.Name}')" + ); + break; + case MetadataUsageType.MethodDef: + var method = model.MethodsByDefinitionIndex[usage.SourceIndex]; + writeLines(writer, + $"SetName({model.Package.BinaryMetadataUsages[usage.DestinationIndex].ToAddressString()}, 'Method${method.DeclaringType.Name}.{method.Name}')" + ); + break; + case MetadataUsageType.FieldInfo: + var field = model.Package.Fields[usage.SourceIndex]; + type = model.GetTypeFromUsage(field.typeIndex); + var fieldName = model.Package.Strings[field.nameIndex]; + writeLines(writer, + $"SetName({model.Package.BinaryMetadataUsages[usage.DestinationIndex].ToAddressString()}, 'Field${type.Name}.{fieldName}')" + ); + break; + case MetadataUsageType.StringLiteral: + // TODO: String literals + break; + case MetadataUsageType.MethodRef: + var methodSpec = model.Package.MethodSpecs[usage.SourceIndex]; + method = model.MethodsByDefinitionIndex[methodSpec.methodDefinitionIndex]; + type = method.DeclaringType; + writeLines(writer, + $"SetName({model.Package.BinaryMetadataUsages[usage.DestinationIndex].ToAddressString()}, 'Method${type.Name}.{method.Name}')" + ); + break; + default: + break; + } + } + } + + private static void writeSectionHeader(StreamWriter writer, string sectionName) { + writeLines(writer, + $"# SECTION: {sectionName}", + $"# -----------------------------" + ); + } + + private static void writeLines(StreamWriter writer, params string[] lines) { + foreach (var line in lines) { + writer.WriteLine(line); + } + } + + #endregion + } +} diff --git a/Il2CppDumper/Program.cs b/Il2CppDumper/Program.cs index ae1fc1e..3f800a0 100644 --- a/Il2CppDumper/Program.cs +++ b/Il2CppDumper/Program.cs @@ -25,7 +25,7 @@ namespace Il2CppInspector [Option('c', "cs-out", Required = false, HelpText = "C# output file (when using single-file layout) or path (when using per namespace, assembly or class layout)", Default = "types.cs")] public string CSharpOutPath { get; set; } - [Option('p', "py-out", Required = false, Hidden = true, HelpText = "IDA Python script output file", Default = "ida.py")] + [Option('p', "py-out", Required = false, HelpText = "IDA Python script output file", Default = "ida.py")] public string PythonOutFile { get; set; } [Option('e', "exclude-namespaces", Required = false, Separator = ',', HelpText = "Comma-separated list of namespaces to suppress in C# output, or 'none' to include all namespaces", @@ -208,7 +208,10 @@ namespace Il2CppInspector } // IDA Python script output - // TODO: IDA Python script output + using (var scriptDumperTimer = new Benchmark("IDA Python Script Dumper")) { + var idaWriter = new Il2CppIDAScriptDumper(model); + idaWriter.WriteScriptToFile(options.PythonOutFile); + } } // Success exit code diff --git a/Il2CppInspector/Il2CppBinary.cs b/Il2CppInspector/Il2CppBinary.cs index fb6c910..b0606b9 100644 --- a/Il2CppInspector/Il2CppBinary.cs +++ b/Il2CppInspector/Il2CppBinary.cs @@ -43,6 +43,9 @@ namespace Il2CppInspector // Generic method specs for vtables public Il2CppMethodSpec[] MethodSpecs { get; private set; } + // Addresses where metadata is used + public ulong[] MetadataUsages { get; private set; } + // Every defined type public List Types { get; private set; } @@ -194,6 +197,9 @@ namespace Il2CppInspector // Generic method specs MethodSpecs = image.ReadMappedArray(MetadataRegistration.methodSpecs, (int) MetadataRegistration.methodSpecsCount); + + // Metadata usages (addresses) + MetadataUsages = image.ReadMappedArray(MetadataRegistration.metadataUsages, (int)MetadataRegistration.metadataUsagesCount); } } } diff --git a/Il2CppInspector/Il2CppInspector.cs b/Il2CppInspector/Il2CppInspector.cs index e4ce6a6..7f65229 100644 --- a/Il2CppInspector/Il2CppInspector.cs +++ b/Il2CppInspector/Il2CppInspector.cs @@ -42,6 +42,7 @@ namespace Il2CppInspector public int[] GenericConstraintIndices => Metadata.GenericConstraintIndices; public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges => Metadata.AttributeTypeRanges; public Il2CppInterfaceOffsetPair[] InterfaceOffsets => Metadata.InterfaceOffsets; + public List MetadataUsages => Metadata.MetadataUsages; public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices; public int[] NestedTypeIndices => Metadata.NestedTypeIndices; public int[] AttributeTypeIndices => Metadata.AttributeTypeIndices; @@ -53,6 +54,7 @@ namespace Il2CppInspector public Dictionary Modules => Binary.Modules; public ulong[] CustomAttributeGenerators => Binary.CustomAttributeGenerators; public Il2CppMethodSpec[] MethodSpecs => Binary.MethodSpecs; + public ulong[] BinaryMetadataUsages => Binary.MetadataUsages; // TODO: Finish all file access in the constructor and eliminate the need for this public IFileFormatReader BinaryImage => Binary.Image; @@ -274,4 +276,28 @@ namespace Il2CppInspector return processors; } } + + public enum MetadataUsageType + { + TypeInfo = 1, + Type = 2, + MethodDef = 3, + FieldInfo = 4, + StringLiteral = 5, + MethodRef = 6, + } + + public class MetadataUsage + { + public MetadataUsageType Type { get; } + public int SourceIndex { get; } + public int DestinationIndex { get; } + + public MetadataUsage(MetadataUsageType type, int sourceIndex, int destinationIndex) + { + Type = type; + SourceIndex = sourceIndex; + DestinationIndex = destinationIndex; + } + } } diff --git a/Il2CppInspector/Metadata.cs b/Il2CppInspector/Metadata.cs index cbd1c60..514a6f0 100644 --- a/Il2CppInspector/Metadata.cs +++ b/Il2CppInspector/Metadata.cs @@ -32,6 +32,8 @@ namespace Il2CppInspector public Il2CppGenericParameter[] GenericParameters { get; } public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges { get; } public Il2CppInterfaceOffsetPair[] InterfaceOffsets { get; } + public Il2CppMetadataUsageList[] MetadataUsageLists { get; } + public Il2CppMetadataUsagePair[] MetadataUsagePairs { get; } public int[] InterfaceUsageIndices { get; } public int[] NestedTypeIndices { get; } @@ -40,6 +42,7 @@ namespace Il2CppInspector public uint[] VTableMethodIndices { get; } public Dictionary Strings { get; } = new Dictionary(); + public List MetadataUsages { get; } = new List(); public Metadata(Stream stream) : base(stream) { @@ -118,6 +121,11 @@ namespace Il2CppInspector Assemblies = ReadArray(Header.assembliesOffset, Header.assembliesCount / Sizeof(typeof(Il2CppAssemblyDefinition))); ParameterDefaultValues = ReadArray(Header.parameterDefaultValuesOffset, Header.parameterDefaultValuesCount / Sizeof(typeof(Il2CppParameterDefaultValue))); } + if (Version >= 19) { + MetadataUsageLists = ReadArray(Header.metadataUsageListsOffset, Header.metadataUsageListsCount / Sizeof(typeof(Il2CppMetadataUsageList))); + MetadataUsagePairs = ReadArray(Header.metadataUsagePairsOffset, Header.metadataUsagePairsCount / Sizeof(typeof(Il2CppMetadataUsagePair))); + MetadataUsages = buildMetadataUsages(); + } if (Version >= 21) { AttributeTypeIndices = ReadArray(Header.attributeTypesOffset, Header.attributeTypesCount / sizeof(int)); AttributeTypeRanges = ReadArray(Header.attributesInfoOffset, Header.attributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange))); @@ -129,6 +137,28 @@ namespace Il2CppInspector Strings.Add((int)Position - Header.stringOffset, ReadNullTerminatedString()); } + private List buildMetadataUsages() + { + var usages = new Dictionary(); + foreach (var metadataUsageList in MetadataUsageLists) + { + for (var i = 0; i < metadataUsageList.count; i++) + { + var metadataUsagePair = MetadataUsagePairs[metadataUsageList.start + i]; + + var encodedType = metadataUsagePair.encodedSourceIndex & 0xE0000000; + var usageType = (MetadataUsageType)(encodedType >> 29); + + var sourceIndex = metadataUsagePair.encodedSourceIndex & 0x1FFFFFFF; + var destinationIndex = metadataUsagePair.destinationindex; + + usages.TryAdd(destinationIndex, new MetadataUsage(usageType, (int)sourceIndex, (int)destinationIndex)); + } + } + + return usages.Values.ToList(); + } + private int Sizeof(Type type) { int size = 0; diff --git a/Il2CppInspector/MetadataClasses.cs b/Il2CppInspector/MetadataClasses.cs index 2073a1a..c73fe8b 100644 --- a/Il2CppInspector/MetadataClasses.cs +++ b/Il2CppInspector/MetadataClasses.cs @@ -421,4 +421,16 @@ namespace Il2CppInspector public int interfaceTypeIndex; public int offset; } + + public class Il2CppMetadataUsageList + { + public uint start; + public uint count; + } + + public class Il2CppMetadataUsagePair + { + public uint destinationindex; + public uint encodedSourceIndex; + } }