Struct reading and disassembly script overhaul, various misc. loading fixes, bump to .NET 9 (#13)

* Bump projects to .net 9 and update nugets

* add VersionedSerialization + source generator

* migrate versioning to StructVersion class, add handling/detection for 29.2/31.2

* add new struct definitions

* rename serialization methods and add BinaryObjectStreamReader for interop

* Rework metadata struct loading to use new struct versioning

* move 29/31.1/.2 to use tags (-2022,-2023) instead of minor versions

* fix metadata usage validity checks

* rework code registration offsetting a bit and add second 29/31.1 condition

* tweak .1 condition (again)

* 29/31.2 was a psyop

* also remove 29.2 from the readme

* remove loading of packed dlls - this was a very unsafe feature

* support auto-recovering type indices from type handles
fixes loading of memory-dumped v29+ libraries since those replacee their class indices on load with a pointer to the corresponding type

* support loading PEs without an export table

* also read UnresolvedVirtualCallCount on regular v31

* Disable plugin loading for now

* Overhaul disassembler script + add Binary Ninja target (#12)

* Overhaul diassembler scripts:
- No longer defines top level functions
- Split into three classes: StatusHandler (like before), DisassemblerInterface (for interfacing with the used program API), ScriptContext (for definiting general functions that use the disassembler interface)
- Add type annotations to all class methods and remove 2.7 compatibility stuff (Ghidra now supports Python 3 so this is unnecessary anymore)
- Disassembler backends are now responsible for launching metadata/script processing, to better support disassembler differences
- String handling is back in the base ScriptContext class, disassembler interfaces opt into the fake string segment creation and fall back to the old method if it isn't supported

* Add Binary Ninja disassembler script backend
This uses the new backend-controlled execution to launch metadata processing on a background thread to keep the ui responsive

* make binary ninja script use own _BINARYNINJA_ define and add define helpers to header

* Update README to account for new script and binary ninja backend

* implement fake string segment functions for binary ninja but don't advertise support

* also cache API function types in binary ninja backend

* fix ida script and disable folders again

* Fix metadata usage issues caused by it being a value type now

* make TryMapVATR overrideable and implement it for ELFs

* Make field offset reading use TryMapVATR to reduce exceptions

* Fix NRE in Assembly ctor on < v24.2

* Update actions workflow to produce cross-platform CLI binaries, update readme to reflect .net 9 changes

* workflow: only restore packages for projects that are being built

* workflow: tweak caching and fix gui compilation

* workflow: remove double .zip in CLI artifact name

* 29/31.2 don't actually exist, this logic is not needed
This commit is contained in:
Luke
2024-11-14 14:32:11 +01:00
committed by GitHub
parent 5b0476fcc5
commit b05c03964a
130 changed files with 5117 additions and 4371 deletions

View File

@@ -7,6 +7,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection {
public class Assembly
@@ -43,37 +45,38 @@ namespace Il2CppInspector.Reflection {
public Assembly(TypeModel model, int imageIndex) {
Model = model;
ImageDefinition = Model.Package.Images[imageIndex];
AssemblyDefinition = Model.Package.Assemblies[ImageDefinition.assemblyIndex];
AssemblyDefinition = Model.Package.Assemblies[ImageDefinition.AssemblyIndex];
if (AssemblyDefinition.imageIndex != imageIndex)
if (AssemblyDefinition.ImageIndex != imageIndex)
throw new InvalidOperationException("Assembly/image index mismatch");
MetadataToken = (int) AssemblyDefinition.token;
Index = ImageDefinition.assemblyIndex;
ShortName = Model.Package.Strings[ImageDefinition.nameIndex];
MetadataToken = (int) AssemblyDefinition.Token;
Index = ImageDefinition.AssemblyIndex;
ShortName = Model.Package.Strings[ImageDefinition.NameIndex];
// Get full assembly name
var nameDef = AssemblyDefinition.aname;
var name = Regex.Replace(Model.Package.Strings[nameDef.nameIndex], @"[^A-Za-z0-9_\-\.()]", "");
var culture = Model.Package.Strings[nameDef.cultureIndex];
var nameDef = AssemblyDefinition.Aname;
var name = Regex.Replace(Model.Package.Strings[nameDef.NameIndex], @"[^A-Za-z0-9_\-\.()]", "");
var culture = Model.Package.Strings[nameDef.CultureIndex];
if (string.IsNullOrEmpty(culture))
culture = "neutral";
var pkt = BitConverter.ToString(nameDef.publicKeyToken).Replace("-", "");
var pkt = Convert.ToHexString(nameDef.PublicKeyToken);
if (pkt == "0000000000000000")
pkt = "null";
var version = string.Format($"{nameDef.major}.{nameDef.minor}.{nameDef.build}.{nameDef.revision}");
var version = string.Format($"{nameDef.Major}.{nameDef.Minor}.{nameDef.Build}.{nameDef.Revision}");
FullName = string.Format($"{name}, Version={version}, Culture={culture}, PublicKeyToken={pkt.ToLower()}");
if (ImageDefinition.entryPointIndex != -1) {
if (ImageDefinition.EntryPointIndex != -1) {
// TODO: Generate EntryPoint method from entryPointIndex
}
// Find corresponding module (we'll need this for method pointers)
ModuleDefinition = Model.Package.Modules?[ShortName];
// Find corresponding module (we'll need this for method pointers on V24.2+)
if (Model.Package.Modules != null)
ModuleDefinition = Model.Package.Modules[ShortName];
// Generate types in DefinedTypes from typeStart to typeStart+typeCount-1
for (var t = ImageDefinition.typeStart; t < ImageDefinition.typeStart + ImageDefinition.typeCount; t++) {
for (var t = ImageDefinition.TypeStart; t < ImageDefinition.TypeStart + ImageDefinition.TypeCount; t++) {
var type = new TypeInfo(t, this);
// Don't add empty module definitions

View File

@@ -4,6 +4,7 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -88,13 +89,13 @@ namespace Il2CppInspector.Reflection
var pkg = asm.Model.Package;
// Attribute type ranges weren't included before v21 (customASttributeGenerators was though)
if (pkg.Version < 21)
if (pkg.Version < MetadataVersions.V210)
yield break;
if (pkg.Version < 29)
if (pkg.Version < MetadataVersions.V290)
{
var range = pkg.AttributeTypeRanges[customAttributeIndex];
for (var i = range.start; i < range.start + range.count; i++)
for (var i = range.Start; i < range.Start + range.Count; i++)
{
var typeIndex = pkg.AttributeTypeIndices[i];
@@ -117,8 +118,8 @@ namespace Il2CppInspector.Reflection
var range = pkg.Metadata.AttributeDataRanges[customAttributeIndex];
var next = pkg.Metadata.AttributeDataRanges[customAttributeIndex + 1];
var startOffset = pkg.Metadata.Header.attributeDataOffset + range.startOffset;
var endOffset = pkg.Metadata.Header.attributeDataOffset + next.startOffset;
var startOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + range.StartOffset;
var endOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + next.StartOffset;
var reader = new CustomAttributeDataReader(pkg, asm, pkg.Metadata, startOffset, endOffset);
if (reader.Count == 0)
@@ -142,13 +143,17 @@ namespace Il2CppInspector.Reflection
public static IList<CustomAttributeData> GetCustomAttributes(Assembly asm, int token, int customAttributeIndex) =>
getCustomAttributes(asm, asm.Model.GetCustomAttributeIndex(asm, token, customAttributeIndex)).ToList();
public static IList<CustomAttributeData> GetCustomAttributes(Assembly asm) => GetCustomAttributes(asm, asm.MetadataToken, asm.AssemblyDefinition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(EventInfo evt) => GetCustomAttributes(evt.Assembly, evt.MetadataToken, evt.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(FieldInfo field) => GetCustomAttributes(field.Assembly, field.MetadataToken, field.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(MethodBase method) => GetCustomAttributes(method.Assembly, method.MetadataToken, method.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo param) => GetCustomAttributes(param.DeclaringMethod.Assembly, param.MetadataToken, param.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(Assembly asm) => GetCustomAttributes(asm, asm.MetadataToken, asm.AssemblyDefinition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(EventInfo evt) => GetCustomAttributes(evt.Assembly, evt.MetadataToken, evt.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(FieldInfo field) => GetCustomAttributes(field.Assembly, field.MetadataToken, field.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(MethodBase method) => GetCustomAttributes(method.Assembly, method.MetadataToken, method.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo param) => GetCustomAttributes(param.DeclaringMethod.Assembly, param.MetadataToken, param.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(PropertyInfo prop)
=> prop.Definition != null ? GetCustomAttributes(prop.Assembly, prop.MetadataToken, prop.Definition.customAttributeIndex) : new List<CustomAttributeData>();
public static IList<CustomAttributeData> GetCustomAttributes(TypeInfo type) => type.Definition != null? GetCustomAttributes(type.Assembly, type.MetadataToken, type.Definition.customAttributeIndex) : new List<CustomAttributeData>();
=> prop.Definition.IsValid
? GetCustomAttributes(prop.Assembly, prop.MetadataToken, prop.Definition.CustomAttributeIndex)
: new List<CustomAttributeData>();
public static IList<CustomAttributeData> GetCustomAttributes(TypeInfo type) => type.Definition.IsValid
? GetCustomAttributes(type.Assembly, type.MetadataToken, type.Definition.CustomAttributeIndex)
: new List<CustomAttributeData>();
}
}

View File

@@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -41,25 +42,25 @@ namespace Il2CppInspector.Reflection
public EventInfo(Il2CppInspector pkg, int eventIndex, TypeInfo declaringType) :
base(declaringType) {
Definition = pkg.Events[eventIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = eventIndex;
Name = pkg.Strings[Definition.nameIndex];
Name = pkg.Strings[Definition.NameIndex];
rootDefinition = this;
eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var eventType = pkg.TypeReferences[Definition.typeIndex];
eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.TypeIndex);
var eventType = pkg.TypeReferences[Definition.TypeIndex];
// Copy attributes
Attributes = (EventAttributes) eventType.attrs;
Attributes = (EventAttributes) eventType.Attrs;
// NOTE: This relies on methods being added to TypeInfo.DeclaredMethods in the same order they are defined in the Il2Cpp metadata
// add, remove and raise are method indices from the first method of the declaring type
if (Definition.add >= 0)
AddMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.add);
if (Definition.remove >= 0)
RemoveMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.remove);
if (Definition.raise >= 0)
RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.raise);
if (Definition.Add >= 0)
AddMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Add);
if (Definition.Remove >= 0)
RemoveMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Remove);
if (Definition.Raise >= 0)
RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Raise);
}
public EventInfo(EventInfo eventDef, TypeInfo declaringType) : base(declaringType) {

View File

@@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection {
public class FieldInfo : MemberInfo // L-TODO: Add support for [ThreadLocal] fields
@@ -96,9 +97,9 @@ namespace Il2CppInspector.Reflection {
public FieldInfo(Il2CppInspector pkg, int fieldIndex, TypeInfo declaringType) :
base(declaringType) {
Definition = pkg.Fields[fieldIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = fieldIndex;
Name = pkg.Strings[Definition.nameIndex];
Name = pkg.Strings[Definition.NameIndex];
rawOffset = pkg.FieldOffsets[fieldIndex];
if (0 > rawOffset)
@@ -109,11 +110,11 @@ namespace Il2CppInspector.Reflection {
rootDefinition = this;
fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var fieldType = pkg.TypeReferences[Definition.typeIndex];
fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.TypeIndex);
var fieldType = pkg.TypeReferences[Definition.TypeIndex];
// Copy attributes
Attributes = (FieldAttributes) fieldType.attrs;
Attributes = (FieldAttributes) fieldType.Attrs;
// Default initialization value if present
if (pkg.FieldDefaultValue.TryGetValue(fieldIndex, out (ulong address, object variant) value)) {
@@ -123,7 +124,7 @@ namespace Il2CppInspector.Reflection {
}
public FieldInfo(FieldInfo fieldDef, TypeInfo declaringType) : base(declaringType) {
if (fieldDef.Definition == null)
if (!fieldDef.Definition.IsValid)
throw new ArgumentException("Argument must be a bare field definition");
rootDefinition = fieldDef;

View File

@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -117,9 +118,9 @@ namespace Il2CppInspector.Reflection
// Initialize a method from a method definition (MethodDef)
protected MethodBase(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(declaringType) {
Definition = pkg.Methods[methodIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = methodIndex;
Name = pkg.Strings[Definition.nameIndex];
Name = pkg.Strings[Definition.NameIndex];
// Find method pointer
VirtualAddress = pkg.GetMethodPointer(Assembly.ModuleDefinition, Definition);
@@ -130,28 +131,28 @@ namespace Il2CppInspector.Reflection
rootDefinition = this;
// Generic method definition?
if (Definition.genericContainerIndex >= 0) {
if (Definition.GenericContainerIndex >= 0) {
IsGenericMethod = true;
// Store the generic type parameters for later instantiation
var container = pkg.GenericContainers[Definition.genericContainerIndex];
genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc)
var container = pkg.GenericContainers[Definition.GenericContainerIndex];
genericArguments = Enumerable.Range(container.GenericParameterStart, container.TypeArgc)
.Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray();
genericMethodInstances = new Dictionary<TypeInfo[], MethodBase>(new TypeInfo.TypeArgumentsComparer());
genericMethodInstances[genericArguments] = this;
}
// Copy attributes
Attributes = (MethodAttributes) Definition.flags;
MethodImplementationFlags = (MethodImplAttributes) Definition.iflags;
Attributes = (MethodAttributes) Definition.Flags;
MethodImplementationFlags = (MethodImplAttributes) Definition.ImplFlags;
// Add arguments
for (var p = Definition.parameterStart; p < Definition.parameterStart + Definition.parameterCount; p++)
for (var p = Definition.ParameterStart; p < Definition.ParameterStart + Definition.ParameterCount; p++)
DeclaredParameters.Add(new ParameterInfo(pkg, p, this));
}
protected MethodBase(MethodBase methodDef, TypeInfo declaringType) : base(declaringType) {
if (methodDef.Definition == null)
if (!methodDef.Definition.IsValid)
throw new ArgumentException("Argument must be a bare method definition");
rootDefinition = methodDef;

View File

@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -64,28 +65,28 @@ namespace Il2CppInspector.Reflection
if (paramIndex == -1) {
Position = -1;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.returnType);
MetadataToken = declaringMethod.Definition.returnParameterToken;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.ReturnType);
MetadataToken = (int)declaringMethod.Definition.ReturnParameterToken;
Attributes |= ParameterAttributes.Retval;
return;
}
Definition = pkg.Params[Index];
MetadataToken = (int) Definition.token;
Name = pkg.Strings[Definition.nameIndex];
MetadataToken = (int) Definition.Token;
Name = pkg.Strings[Definition.NameIndex];
rootDefinition = this;
// Handle unnamed/obfuscated parameter names
if (string.IsNullOrEmpty(Name))
Name = string.Format($"param_{Index:x8}");
Position = paramIndex - declaringMethod.Definition.parameterStart;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.typeIndex);
Position = paramIndex - declaringMethod.Definition.ParameterStart;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.TypeIndex);
var paramType = pkg.TypeReferences[Definition.typeIndex];
var paramType = pkg.TypeReferences[Definition.TypeIndex];
// Copy attributes
Attributes = (ParameterAttributes) paramType.attrs;
Attributes = (ParameterAttributes) paramType.Attrs;
// Default initialization value if present
if (pkg.ParameterDefaultValue.TryGetValue(paramIndex, out (ulong address, object variant) value)) {

View File

@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection {
public class PropertyInfo : MemberInfo
@@ -52,25 +53,24 @@ namespace Il2CppInspector.Reflection {
base(declaringType) {
Index = propIndex;
Definition = pkg.Properties[propIndex];
MetadataToken = (int) Definition.token;
Name = pkg.Strings[Definition.nameIndex];
MetadataToken = (int) Definition.Token;
Name = pkg.Strings[Definition.NameIndex];
rootDefinition = this;
// Copy attributes
Attributes = (PropertyAttributes) Definition.attrs;
Attributes = (PropertyAttributes) Definition.Attrs;
// prop.get and prop.set are method indices from the first method of the declaring type
if (Definition.get >= 0)
GetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.get);
if (Definition.set >= 0)
SetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.set);
if (Definition.Get >= 0)
GetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Get);
if (Definition.Set >= 0)
SetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Set);
}
// Create a property based on a get and set method
public PropertyInfo(MethodInfo getter, MethodInfo setter, TypeInfo declaringType) :
base(declaringType) {
Index = -1;
Definition = null;
rootDefinition = this;
Name = (getter ?? setter).Name.Replace(".get_", ".").Replace(".set_", ".");

View File

@@ -13,6 +13,8 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -53,9 +55,9 @@ namespace Il2CppInspector.Reflection
return null;
if (IsArray)
return Assembly.Model.TypesByFullName["System.Array"];
if (Definition != null) {
if (Definition.parentIndex >= 0)
return Assembly.Model.TypesByReferenceIndex[Definition.parentIndex];
if (Definition.IsValid) {
if (Definition.ParentIndex >= 0)
return Assembly.Model.TypesByReferenceIndex[Definition.ParentIndex];
}
if (genericTypeDefinition != null) {
return genericTypeDefinition.BaseType.SubstituteGenericArguments(genericArguments);
@@ -73,15 +75,15 @@ namespace Il2CppInspector.Reflection
public override TypeInfo DeclaringType {
get {
if (Definition != null) {
if (Definition.IsValid) {
/* Type definition */
if (Definition.declaringTypeIndex == -1)
if (Definition.DeclaringTypeIndex == -1)
return null;
var type = Assembly.Model.TypesByReferenceIndex[Definition.declaringTypeIndex];
var type = Assembly.Model.TypesByReferenceIndex[Definition.DeclaringTypeIndex];
if (type == null) {
/* This might happen while initially setting up the types */
var typeRef = Assembly.Model.Package.TypeReferences[Definition.declaringTypeIndex];
type = Assembly.Model.TypesByDefinitionIndex[(int)typeRef.datapoint];
var typeRef = Assembly.Model.Package.TypeReferences[Definition.DeclaringTypeIndex];
type = Assembly.Model.TypesByDefinitionIndex[typeRef.Data.KlassIndex];
}
return type;
}
@@ -253,11 +255,11 @@ namespace Il2CppInspector.Reflection
public PropertyInfo GetProperty(string name) => DeclaredProperties.FirstOrDefault(p => p.Name == name);
public MethodBase[] GetVTable() {
if (Definition != null) {
if (!Definition.IsValid) {
MetadataUsage[] vt = Assembly.Model.Package.GetVTable(Definition);
MethodBase[] res = new MethodBase[vt.Length];
for (int i = 0; i < vt.Length; i++) {
if (vt[i] != null)
if (vt[i].IsValid)
res[i] = Assembly.Model.GetMetadataUsageMethod(vt[i]);
}
return res;
@@ -667,7 +669,7 @@ namespace Il2CppInspector.Reflection
private readonly TypeRef[] implementedInterfaceReferences;
public IEnumerable<TypeInfo> ImplementedInterfaces {
get {
if (Definition != null)
if (Definition.IsValid)
return implementedInterfaceReferences.Select(x => x.Value);
if (genericTypeDefinition != null)
return genericTypeDefinition.ImplementedInterfaces.Select(t => t.SubstituteGenericArguments(genericArguments));
@@ -687,7 +689,7 @@ namespace Il2CppInspector.Reflection
public bool IsEnum { get; }
public bool IsGenericParameter { get; }
public bool IsGenericType { get; }
public bool IsGenericTypeDefinition => (Definition != null) && genericArguments.Any();
public bool IsGenericTypeDefinition => (Definition.IsValid) && genericArguments.Any();
public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import;
public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;
public bool IsNested => (MemberType & MemberTypes.NestedType) == MemberTypes.NestedType;
@@ -746,13 +748,13 @@ namespace Il2CppInspector.Reflection
Definition = pkg.TypeDefinitions[typeIndex];
Sizes = pkg.TypeDefinitionSizes[typeIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = typeIndex;
Namespace = Regex.Replace(pkg.Strings[Definition.namespaceIndex], @"[^A-Za-z0-9_\-\.<>{}]", "");
Name = pkg.Strings[Definition.nameIndex];
Namespace = Regex.Replace(pkg.Strings[Definition.NamespaceIndex], @"[^A-Za-z0-9_\-\.<>{}]", "");
Name = pkg.Strings[Definition.NameIndex];
// Nested type?
if (Definition.declaringTypeIndex >= 0) {
if (Definition.DeclaringTypeIndex >= 0) {
MemberType |= MemberTypes.NestedType;
}
@@ -760,14 +762,14 @@ namespace Il2CppInspector.Reflection
Assembly.Model.TypesByDefinitionIndex[Index] = this;
// Generic type definition?
if (Definition.genericContainerIndex >= 0) {
if (Definition.GenericContainerIndex >= 0) {
IsGenericType = true;
IsGenericParameter = false;
// Store the generic type parameters for later instantiation
var container = pkg.GenericContainers[Definition.genericContainerIndex];
var container = pkg.GenericContainers[Definition.GenericContainerIndex];
genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc)
genericArguments = Enumerable.Range((int)container.GenericParameterStart, container.TypeArgc)
.Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray();
genericTypeInstances = new Dictionary<TypeInfo[], TypeInfo>(new TypeArgumentsComparer());
genericTypeInstances[genericArguments] = this;
@@ -777,12 +779,12 @@ namespace Il2CppInspector.Reflection
Assembly.Model.TypesByFullName[FullName] = this;
// Copy attributes
Attributes = (TypeAttributes) Definition.flags;
Attributes = (TypeAttributes) Definition.Flags;
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference will be System.Enum)
if (((Definition.bitfield >> 1) & 1) == 1) {
if (Definition.Bitfield.EnumType) {
IsEnum = true;
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.elementTypeIndex);
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.ElementTypeIndex);
}
// Pass-by-reference type
@@ -792,24 +794,24 @@ namespace Il2CppInspector.Reflection
IsByRef = false;
// Add all implemented interfaces
implementedInterfaceReferences = new TypeRef[Definition.interfaces_count];
for (var i = 0; i < Definition.interfaces_count; i++)
implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, pkg.InterfaceUsageIndices[Definition.interfacesStart + i]);
implementedInterfaceReferences = new TypeRef[Definition.InterfacesCount];
for (var i = 0; i < Definition.InterfacesCount; i++)
implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, pkg.InterfaceUsageIndices[Definition.InterfacesIndex + i]);
// Add all nested types
declaredNestedTypes = new TypeRef[Definition.nested_type_count];
for (var n = 0; n < Definition.nested_type_count; n++)
declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.nestedTypesStart + n]);
declaredNestedTypes = new TypeRef[Definition.NestedTypeCount];
for (var n = 0; n < Definition.NestedTypeCount; n++)
declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.NestedTypeIndex + n]);
// Add all fields
declaredFields = new List<FieldInfo>();
for (var f = Definition.fieldStart; f < Definition.fieldStart + Definition.field_count; f++)
for (var f = Definition.FieldIndex; f < Definition.FieldIndex + Definition.FieldCount; f++)
declaredFields.Add(new FieldInfo(pkg, f, this));
// Add all methods
declaredConstructors = new List<ConstructorInfo>();
declaredMethods = new List<MethodInfo>();
for (var m = Definition.methodStart; m < Definition.methodStart + Definition.method_count; m++) {
for (var m = Definition.MethodIndex; m < Definition.MethodIndex + Definition.MethodCount; m++) {
var method = new MethodInfo(pkg, m, this);
if (method.Name == ConstructorInfo.ConstructorName || method.Name == ConstructorInfo.TypeConstructorName)
declaredConstructors.Add(new ConstructorInfo(pkg, m, this));
@@ -819,7 +821,7 @@ namespace Il2CppInspector.Reflection
// Add all properties
declaredProperties = new List<PropertyInfo>();
for (var p = Definition.propertyStart; p < Definition.propertyStart + Definition.property_count; p++)
for (var p = Definition.PropertyIndex; p < Definition.PropertyIndex + Definition.PropertyCount; p++)
declaredProperties.Add(new PropertyInfo(pkg, p, this));
// There are rare cases when explicitly implemented interface properties
@@ -856,7 +858,7 @@ namespace Il2CppInspector.Reflection
// Add all events
declaredEvents = new List<EventInfo>();
for (var e = Definition.eventStart; e < Definition.eventStart + Definition.event_count; e++)
for (var e = Definition.EventIndex; e < Definition.EventIndex + Definition.EventCount; e++)
declaredEvents.Add(new EventInfo(pkg, e, this));
// TODO: Events have the same edge case issue as properties above, eg. PoGo 0.35.0
@@ -937,21 +939,21 @@ namespace Il2CppInspector.Reflection
Namespace = declaringType.Namespace;
// Special constraints
GenericParameterAttributes = (GenericParameterAttributes)param.flags;
GenericParameterAttributes = (GenericParameterAttributes)param.Flags;
// Type constraints
genericParameterConstraints = new TypeRef[param.constraintsCount];
for (int c = 0; c < param.constraintsCount; c++)
genericParameterConstraints[c] = TypeRef.FromReferenceIndex(Assembly.Model, Assembly.Model.Package.GenericConstraintIndices[param.constraintsStart + c]);
genericParameterConstraints = new TypeRef[param.ConstraintsCount];
for (int c = 0; c < param.ConstraintsCount; c++)
genericParameterConstraints[c] = TypeRef.FromReferenceIndex(Assembly.Model, Assembly.Model.Package.GenericConstraintIndices[param.ConstraintsStart + c]);
// Base type of object (set by default)
// TODO: ImplementedInterfaces should be set to interface types constraints
// Name of parameter
Name = Assembly.Model.Package.Strings[param.nameIndex];
Name = Assembly.Model.Package.Strings[param.NameIndex];
// Position
GenericParameterPosition = param.num;
GenericParameterPosition = param.Num;
IsGenericParameter = true;
IsGenericType = false;

View File

@@ -5,10 +5,12 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Il2CppInspector.Next.BinaryMetadata;
namespace Il2CppInspector.Reflection
{
@@ -85,7 +87,7 @@ namespace Il2CppInspector.Reflection
public TypeModel(Il2CppInspector package) {
Package = package;
TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length];
TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Count];
TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Length];
GenericParameterTypes = new TypeInfo[package.GenericParameters.Length];
MethodsByDefinitionIndex = new MethodBase[package.Methods.Length];
MethodInvokers = new MethodInvoker[package.MethodInvokePointers.Length];
@@ -97,7 +99,7 @@ namespace Il2CppInspector.Reflection
// Create and reference types from TypeRefs
// Note that you can't resolve any TypeRefs until all the TypeDefs have been processed
for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) {
for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Length; typeRefIndex++) {
if(TypesByReferenceIndex[typeRefIndex] != null) {
/* type already generated - probably by forward reference through GetTypeFromVirtualAddress */
continue;
@@ -111,13 +113,13 @@ namespace Il2CppInspector.Reflection
// Create types and methods from MethodSpec (which incorporates TypeSpec in IL2CPP)
foreach (var spec in Package.MethodSpecs) {
var methodDefinition = MethodsByDefinitionIndex[spec.methodDefinitionIndex];
var methodDefinition = MethodsByDefinitionIndex[spec.MethodDefinitionIndex];
var declaringType = methodDefinition.DeclaringType;
// Concrete instance of a generic class
// If the class index is not specified, we will later create a generic method in a non-generic class
if (spec.classIndexIndex != -1) {
var genericInstance = Package.GenericInstances[spec.classIndexIndex];
if (spec.ClassIndexIndex != -1) {
var genericInstance = Package.GenericInstances[spec.ClassIndexIndex];
var genericArguments = ResolveGenericArguments(genericInstance);
declaringType = declaringType.MakeGenericType(genericArguments);
}
@@ -128,8 +130,8 @@ namespace Il2CppInspector.Reflection
else
method = declaringType.GetMethodByDefinition((MethodInfo)methodDefinition);
if (spec.methodIndexIndex != -1) {
var genericInstance = Package.GenericInstances[spec.methodIndexIndex];
if (spec.MethodIndexIndex != -1) {
var genericInstance = Package.GenericInstances[spec.MethodIndexIndex];
var genericArguments = ResolveGenericArguments(genericInstance);
method = method.MakeGenericMethod(genericArguments);
}
@@ -189,7 +191,7 @@ namespace Il2CppInspector.Reflection
public TypeInfo[] ResolveGenericArguments(Il2CppGenericInst inst) {
// Get list of pointers to type parameters (both unresolved and concrete)
var genericTypeArguments = Package.BinaryImage.ReadMappedArray<ulong>(inst.type_argv, (int)inst.type_argc);
var genericTypeArguments = Package.BinaryImage.ReadMappedUWordArray(inst.TypeArgv, (int)inst.TypeArgc);
return genericTypeArguments.Select(a => GetTypeFromVirtualAddress(a)).ToArray();
}
@@ -200,67 +202,67 @@ namespace Il2CppInspector.Reflection
var image = Package.BinaryImage;
TypeInfo underlyingType;
switch (typeRef.type) {
switch (typeRef.Type) {
// Classes defined in the metadata (reference to a TypeDef)
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
underlyingType = TypesByDefinitionIndex[typeRef.datapoint]; // klassIndex
underlyingType = TypesByDefinitionIndex[typeRef.Data.KlassIndex]; // klassIndex
break;
// Constructed types
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
// TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses
var generic = image.ReadMappedObject<Il2CppGenericClass>(typeRef.datapoint); // Il2CppGenericClass *
var generic = image.ReadMappedVersionedObject<Il2CppGenericClass>(typeRef.Data.GenericClass); // Il2CppGenericClass *
// Get generic type definition
TypeInfo genericTypeDef;
if (Package.Version < 27) {
if (Package.Version < MetadataVersions.V270) {
// It appears that TypeRef can be -1 if the generic depth recursion limit
// (--maximum-recursive-generic-depth=) is reached in Il2Cpp. In this case,
// no generic instance type is generated, so we just produce a null TypeInfo here.
if ((generic.typeDefinitionIndex & 0xffff_ffff) == 0x0000_0000_ffff_ffff)
if ((generic.TypeDefinitionIndex & 0xffff_ffff) == 0x0000_0000_ffff_ffff)
return null;
genericTypeDef = TypesByDefinitionIndex[generic.typeDefinitionIndex];
genericTypeDef = TypesByDefinitionIndex[generic.TypeDefinitionIndex];
} else {
genericTypeDef = GetTypeFromVirtualAddress(generic.type);
genericTypeDef = GetTypeFromVirtualAddress(generic.Type);
}
// Get the instantiation
// TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts
var genericInstance = image.ReadMappedObject<Il2CppGenericInst>(generic.context.class_inst);
var genericInstance = image.ReadMappedVersionedObject<Il2CppGenericInst>(generic.Context.ClassInst);
var genericArguments = ResolveGenericArguments(genericInstance);
underlyingType = genericTypeDef.MakeGenericType(genericArguments);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
var descriptor = image.ReadMappedObject<Il2CppArrayType>(typeRef.datapoint);
var elementType = GetTypeFromVirtualAddress(descriptor.etype);
underlyingType = elementType.MakeArrayType(descriptor.rank);
var descriptor = image.ReadMappedVersionedObject<Il2CppArrayType>(typeRef.Data.ArrayType);
var elementType = GetTypeFromVirtualAddress(descriptor.ElementType);
underlyingType = elementType.MakeArrayType(descriptor.Rank);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
elementType = GetTypeFromVirtualAddress(typeRef.datapoint);
elementType = GetTypeFromVirtualAddress(typeRef.Data.Type);
underlyingType = elementType.MakeArrayType(1);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
elementType = GetTypeFromVirtualAddress(typeRef.datapoint);
elementType = GetTypeFromVirtualAddress(typeRef.Data.Type);
underlyingType = elementType.MakePointerType();
break;
// Generic type and generic method parameters
case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
underlyingType = GetGenericParameterType((int)typeRef.datapoint);
underlyingType = GetGenericParameterType(typeRef.Data.GenericParameterIndex);
break;
// Primitive types
default:
underlyingType = GetTypeDefinitionFromTypeEnum(typeRef.type);
underlyingType = GetTypeDefinitionFromTypeEnum(typeRef.Type);
break;
}
// Create a reference type if necessary
return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType;
return typeRef.ByRef ? underlyingType.MakeByRefType() : underlyingType;
}
// Basic primitive types are specified via a flag value
@@ -301,14 +303,14 @@ namespace Il2CppInspector.Reflection
return GenericParameterTypes[index];
var paramType = Package.GenericParameters[index]; // genericParameterIndex
var container = Package.GenericContainers[paramType.ownerIndex];
var container = Package.GenericContainers[paramType.OwnerIndex];
TypeInfo result;
if (container.is_method == 1) {
var owner = MethodsByDefinitionIndex[container.ownerIndex];
if (container.IsMethod == 1) {
var owner = MethodsByDefinitionIndex[container.OwnerIndex];
result = new TypeInfo(owner, paramType);
} else {
var owner = TypesByDefinitionIndex[container.ownerIndex];
var owner = TypesByDefinitionIndex[container.OwnerIndex];
result = new TypeInfo(owner, paramType);
}
GenericParameterTypes[index] = result;
@@ -318,12 +320,12 @@ namespace Il2CppInspector.Reflection
// The attribute index is an index into AttributeTypeRanges, each of which is a start-end range index into AttributeTypeIndices, each of which is a TypeIndex
public int GetCustomAttributeIndex(Assembly asm, int token, int customAttributeIndex) {
// Prior to v24.1, Type, Field, Parameter, Method, Event, Property, Assembly definitions had their own customAttributeIndex field
if (Package.Version <= 24.0)
if (Package.Version <= MetadataVersions.V240)
return customAttributeIndex;
// From v24.1 onwards, token was added to Il2CppCustomAttributeTypeRange and each Il2CppImageDefinition noted the CustomAttributeTypeRanges for the image
// v29 uses this same system but with CustomAttributeDataRanges instead
if (!Package.AttributeIndicesByToken.TryGetValue(asm.ImageDefinition.customAttributeStart, out var indices)
if (!Package.AttributeIndicesByToken.TryGetValue(asm.ImageDefinition.CustomAttributeStart, out var indices)
|| !indices.TryGetValue((uint)token, out var index))
return -1;
@@ -344,7 +346,7 @@ namespace Il2CppInspector.Reflection
case MetadataUsageType.FieldInfo:
var fieldRef = Package.FieldRefs[usage.SourceIndex];
var type = GetMetadataUsageType(usage);
var field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex);
var field = type.DeclaredFields.First(f => f.Index == type.Definition.FieldIndex + fieldRef.FieldIndex);
return $"{type.Name}.{field.Name}";
case MetadataUsageType.StringLiteral:
@@ -358,7 +360,7 @@ namespace Il2CppInspector.Reflection
case MetadataUsageType.FieldRva:
fieldRef = Package.FieldRefs[usage.SourceIndex];
type = GetMetadataUsageType(usage);
field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex);
field = type.DeclaredFields.First(f => f.Index == type.Definition.FieldIndex + fieldRef.FieldIndex);
return $"{type.Name}.{field.Name}_Default"; // TODO: Find out if this is really needed for anything
}
throw new NotImplementedException("Unknown metadata usage type: " + usage.Type);
@@ -368,7 +370,7 @@ namespace Il2CppInspector.Reflection
public TypeInfo GetMetadataUsageType(MetadataUsage usage) => usage.Type switch {
MetadataUsageType.Type or MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex],
MetadataUsageType.MethodDef or MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType,
MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex],
MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].TypeIndex],
_ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type")
};