From 6e796a200f2a0d3f44c2af1b4ca9119f76ba4f39 Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Mon, 13 Jul 2020 19:52:04 +0200 Subject: [PATCH] AppModel: Generate composite type mappings --- .../Cpp/CppDeclarationGenerator.cs | 12 ++-- Il2CppInspector.Common/Model/AppModel.cs | 72 ++++++++++--------- Il2CppInspector.Common/Model/AppType.cs | 42 +++++++++++ .../Outputs/IDAPythonScript.cs | 5 +- 4 files changed, 90 insertions(+), 41 deletions(-) create mode 100644 Il2CppInspector.Common/Model/AppType.cs diff --git a/Il2CppInspector.Common/Cpp/CppDeclarationGenerator.cs b/Il2CppInspector.Common/Cpp/CppDeclarationGenerator.cs index 10f92ed..dc694c3 100644 --- a/Il2CppInspector.Common/Cpp/CppDeclarationGenerator.cs +++ b/Il2CppInspector.Common/Cpp/CppDeclarationGenerator.cs @@ -21,7 +21,7 @@ namespace Il2CppInspector.Cpp private readonly AppModel appModel; private TypeModel model => appModel.ILModel; - private CppTypeCollection types => appModel.TypeCollection; + private CppTypeCollection types => appModel.CppTypeCollection; // Version number and header file to generate structures for public UnityVersion UnityVersion => appModel.UnityVersion; @@ -245,8 +245,8 @@ namespace Il2CppInspector.Cpp } // "Flush" the list of visited types, generating C structures for each one - private List<(TypeInfo ilType, CppType valueType, CppType referenceType, CppType fieldsType)> GenerateVisitedFieldStructs() { - var structs = new List<(TypeInfo ilType, CppType valueType, CppType referenceType, CppType fieldsType)>(TodoTypeStructs.Count); + private List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, CppComplexType fieldsType)> GenerateVisitedFieldStructs() { + var structs = new List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, CppComplexType fieldsType)>(TodoTypeStructs.Count); foreach (var ti in TodoFieldStructs) { if (ti.IsEnum || ti.IsValueType) { var (valueType, boxedType) = GenerateValueFieldStruct(ti); @@ -432,8 +432,10 @@ namespace Il2CppInspector.Cpp /// Type declarations that have previously been generated by this instance of CppDeclarationGenerator will not be generated again. /// /// A string containing C type declarations - public List<(TypeInfo ilType, CppType valueType, CppType referenceType, CppType fieldsType, CppType vtableType, CppType staticsType)> GenerateRemainingTypeDeclarations() { - var decl = GenerateVisitedFieldStructs().Select(s => (s.ilType, s.valueType, s.referenceType, s.fieldsType, (CppType) null, (CppType) null)).ToList(); + public List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, CppComplexType fieldsType, + CppComplexType vtableType, CppComplexType staticsType)> GenerateRemainingTypeDeclarations() { + var decl = GenerateVisitedFieldStructs().Select(s => + (s.ilType, s.valueType, s.referenceType, s.fieldsType, (CppComplexType) null, (CppComplexType) null)).ToList(); foreach (var ti in TodoTypeStructs) { var (cls, statics, vtable) = GenerateTypeStruct(ti); diff --git a/Il2CppInspector.Common/Model/AppModel.cs b/Il2CppInspector.Common/Model/AppModel.cs index 803b5ec..c0b933c 100644 --- a/Il2CppInspector.Common/Model/AppModel.cs +++ b/Il2CppInspector.Common/Model/AppModel.cs @@ -16,22 +16,6 @@ using Il2CppInspector.Reflection; namespace Il2CppInspector.Model { // Class that represents a composite IL/C++ type - public class AppType - { - // The corresponding C++ type definition which represents an instance of the object - // If a .NET type, this is derived from Il2CppObject, otherwise it can be any type - // If the underlying .NET type is a struct (value type), this will return the boxed version - public CppType CppType { get; internal set; } - - // For an underlying .NET type which is a struct (value type), the unboxed type, otherwise null - public CppType CppValueType { get; internal set; } - - // The type in the model this object represents (for .NET types, otherwise null) - public TypeInfo ILType { get; internal set; } - - // The VA of the Il2CppClass object which defines this type (for .NET types, otherwise zero) - public ulong VirtualAddress { get; internal set; } - } // Class that represents the entire structure of the IL2CPP binary realized as C++ types and code, // correlated with .NET types where applicable. Primarily designed to enable automated static analysis of disassembly code. @@ -49,16 +33,19 @@ namespace Il2CppInspector.Model // All of the C++ types used in the application including Unity internal types // NOTE: This is for querying individual types for static analysis - // To generate code output, use DependencyOrderedTypes - public CppTypeCollection TypeCollection { get; set; } // TODO: Change to private set after integrating IDA output + // To generate code output, use DependencyOrderedCppTypes + public CppTypeCollection CppTypeCollection { get; set; } // TODO: Change to private set after integrating IDA output // All of the C++ types used in the application (.NET type translations only) // The types are ordered to enable the production of code output without forward dependencies - public List DependencyOrderedTypes { get; private set; } + public List DependencyOrderedCppTypes { get; private set; } // Composite mapping of all the .NET methods in the IL2CPP binary public MultiKeyDictionary Methods = new MultiKeyDictionary(); + // Composite mapping of all the .NET types in the IL2CPP binary + public MultiKeyDictionary Types = new MultiKeyDictionary(); + // The .NET type model for the application public TypeModel ILModel { get; } @@ -66,8 +53,8 @@ namespace Il2CppInspector.Model public List Exports { get; } // Delegated C++ types iterator - public IEnumerator GetEnumerator() => TypeCollection.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) TypeCollection).GetEnumerator(); + public IEnumerator GetEnumerator() => CppTypeCollection.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) CppTypeCollection).GetEnumerator(); // The C++ declaration generator for this binary // TODO: Make this private once IDA output integration is completed @@ -117,16 +104,16 @@ namespace Il2CppInspector.Model // Start creation of type model by parsing all of the Unity IL2CPP headers // Calling declarationGenerator.GenerateRemainingTypeDeclarations() below will automatically add to this collection - TypeCollection = CppTypeCollection.FromUnityHeaders(UnityHeader, WordSize); + CppTypeCollection = CppTypeCollection.FromUnityHeaders(UnityHeader, WordSize); // Initialize declaration generator to process every type in the binary declarationGenerator = new CppDeclarationGenerator(this); // Initialize ordered type list for code output - DependencyOrderedTypes = new List(); + DependencyOrderedCppTypes = new List(); // Add method definitions to C++ type model - TypeCollection.SetGroup("type_definitions"); + CppTypeCollection.SetGroup("type_definitions"); foreach (var method in ILModel.MethodsByDefinitionIndex.Where(m => m.VirtualAddress.HasValue)) { declarationGenerator.IncludeMethod(method); @@ -137,7 +124,7 @@ namespace Il2CppInspector.Model } // Add generic methods to C++ type model - TypeCollection.SetGroup("types_from_generics"); + CppTypeCollection.SetGroup("types_from_generics"); foreach (var method in ILModel.GenericMethods.Values.Where(m => m.VirtualAddress.HasValue)) { declarationGenerator.IncludeMethod(method); @@ -149,7 +136,7 @@ namespace Il2CppInspector.Model // Add metadata usage types to C++ type model // Not supported in il2cpp <19 - TypeCollection.SetGroup("types_from_usages"); + CppTypeCollection.SetGroup("types_from_usages"); if (Package.MetadataUsages != null) foreach (var usage in Package.MetadataUsages) { @@ -161,6 +148,15 @@ namespace Il2CppInspector.Model var type = ILModel.GetMetadataUsageType(usage); declarationGenerator.IncludeType(type); AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); + + if (usage.Type == MetadataUsageType.TypeInfo) + Types[type].TypeClassAddress = address; + else if (!Types.ContainsKey(type)) + // Generic type definition has no associated C++ type, therefore no dictionary subkey + Types.Add(type, new AppType(type, null, cppTypeRefPtr: address)); + else + // Regular type reference + Types[type].TypeRefPtrAddress = address; break; case MetadataUsageType.MethodDef: case MetadataUsageType.MethodRef: @@ -179,24 +175,32 @@ namespace Il2CppInspector.Model return this; } - private void AddTypes(List<(TypeInfo ilType, CppType valueType, CppType referenceType, CppType fieldsType, CppType vtableType, CppType staticsType)> types) { + private void AddTypes(List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, + CppComplexType fieldsType, CppComplexType vtableType, CppComplexType staticsType)> types) { + + // Add types to dependency-ordered list foreach (var type in types) { if (type.vtableType != null) - DependencyOrderedTypes.Add(type.vtableType); + DependencyOrderedCppTypes.Add(type.vtableType); if (type.staticsType != null) - DependencyOrderedTypes.Add(type.staticsType); + DependencyOrderedCppTypes.Add(type.staticsType); if (type.fieldsType != null) - DependencyOrderedTypes.Add(type.fieldsType); + DependencyOrderedCppTypes.Add(type.fieldsType); if (type.valueType != null) - DependencyOrderedTypes.Add(type.valueType); + DependencyOrderedCppTypes.Add(type.valueType); - DependencyOrderedTypes.Add(type.referenceType); + DependencyOrderedCppTypes.Add(type.referenceType); } + + // Create composite types + foreach (var type in types) + if (!Types.ContainsKey(type.ilType)) + Types.Add(type.ilType, type.referenceType, new AppType(type.ilType, type.referenceType, type.valueType)); } // Get all the types for a group - public IEnumerable GetTypeGroup(string groupName) => TypeCollection.GetTypeGroup(groupName); - public IEnumerable GetDependencyOrderedTypeGroup(string groupName) => DependencyOrderedTypes.Where(t => t.Group == groupName); + public IEnumerable GetTypeGroup(string groupName) => CppTypeCollection.GetTypeGroup(groupName); + public IEnumerable GetDependencyOrderedTypeGroup(string groupName) => DependencyOrderedCppTypes.Where(t => t.Group == groupName); } } diff --git a/Il2CppInspector.Common/Model/AppType.cs b/Il2CppInspector.Common/Model/AppType.cs new file mode 100644 index 0000000..da6f520 --- /dev/null +++ b/Il2CppInspector.Common/Model/AppType.cs @@ -0,0 +1,42 @@ +/* + Copyright 2020 Katy Coe - http://www.djkaty.com - https://github.com/djkaty + + All rights reserved. +*/ + +using Il2CppInspector.Cpp; +using Il2CppInspector.Reflection; + +namespace Il2CppInspector.Model +{ + public class AppType + { + // The corresponding C++ type definition which represents an instance of the object + // This is derived from Il2CppObject + // If the underlying .NET type is a struct (value type), this will return the boxed version + public CppComplexType CppType { get; internal set; } + + // For an underlying .NET type which is a struct (value type), the unboxed type, otherwise null + public CppComplexType CppValueType { get; internal set; } + + // The type in the .NET type model this object maps to + public TypeInfo ILType { get; internal set; } + + // The VA of the Il2CppClass object which defines this type (ClassName__TypeInfo) + public ulong TypeClassAddress { get; internal set; } + + // The VA of the Il2CppType* (VA of the pointer to the Il2CppType) object which references this type + public ulong TypeRefPtrAddress { get; internal set; } + + public AppType(TypeInfo ilType, CppComplexType cppType, CppComplexType valueType = null, + ulong cppClassPtr = 0xffffffff_ffffffff, ulong cppTypeRefPtr = 0xffffffff_ffffffff) { + CppType = cppType; + ILType = ilType; + CppValueType = valueType; + TypeClassAddress = cppClassPtr; + TypeRefPtrAddress = cppTypeRefPtr; + } + + public override string ToString() => ILType.FullName + " -> " + CppType.Name; + } +} \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/IDAPythonScript.cs b/Il2CppInspector.Common/Outputs/IDAPythonScript.cs index 6aae6f0..232bec7 100755 --- a/Il2CppInspector.Common/Outputs/IDAPythonScript.cs +++ b/Il2CppInspector.Common/Outputs/IDAPythonScript.cs @@ -29,7 +29,7 @@ namespace Il2CppInspector.Outputs var internalModel = new AppModel(model.ILModel); internalModel.UnityVersion = model.UnityVersion; internalModel.UnityHeader = model.UnityHeader; - internalModel.TypeCollection = CppTypeCollection.FromUnityHeaders(model.UnityHeader, model.WordSize); + internalModel.CppTypeCollection = CppTypeCollection.FromUnityHeaders(model.UnityHeader, model.WordSize); model = internalModel; declGenerator = new CppDeclarationGenerator(model); @@ -226,7 +226,8 @@ typedef __int64 int64_t; } // TODO: Temporary compatibility function, remove when integrated with ApplicationModel - private void writeDecls(List<(TypeInfo ilType, CppType valueType, CppType referenceType, CppType fieldsType, CppType vtableType, CppType staticsType)> types) + private void writeDecls(List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, + CppComplexType fieldsType, CppComplexType vtableType, CppComplexType staticsType)> types) => writeDecls(string.Join("\n", types.SelectMany(t => new List {t.vtableType, t.staticsType, t.fieldsType, t.valueType, t.referenceType}) .Where(t => t != null)