From 09fd023a9f0f021a0a2a71610c69a057bb4436fa Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Tue, 28 Nov 2017 13:57:16 +0100 Subject: [PATCH] Implement and output events --- Il2CppDumper/Il2CppDumper.cs | 20 ++++++++- Il2CppInspector/Metadata.cs | 4 +- Il2CppInspector/MetadataClasses.cs | 11 +++++ Il2CppInspector/Reflection/EventInfo.cs | 54 +++++++++++++++++++++++++ Il2CppInspector/Reflection/TypeInfo.cs | 8 ++-- README.md | 1 + 6 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 Il2CppInspector/Reflection/EventInfo.cs diff --git a/Il2CppDumper/Il2CppDumper.cs b/Il2CppDumper/Il2CppDumper.cs index 06bbbc7..d57206e 100644 --- a/Il2CppDumper/Il2CppDumper.cs +++ b/Il2CppDumper/Il2CppDumper.cs @@ -1,6 +1,7 @@ // Copyright (c) 2017 Katy Coe - https://www.djkaty.com - https://github.com/djkaty // All rights reserved +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -129,6 +130,23 @@ namespace Il2CppInspector if (type.DeclaredProperties.Count > 0) writer.Write("\n"); + // Events + if (type.DeclaredEvents.Count > 0) + writer.Write("\t// Events\n"); + + foreach (var evt in type.DeclaredEvents) { + string modifiers = evt.AddMethod?.GetModifierString(); + writer.Write($"\t{modifiers} event {evt.EventHandlerType.CSharpName} {evt.Name} {{\n"); + var m = new Dictionary(); + if (evt.AddMethod != null) m.Add("add", evt.AddMethod.VirtualAddress); + if (evt.RemoveMethod != null) m.Add("remove", evt.RemoveMethod.VirtualAddress); + if (evt.RaiseMethod != null) m.Add("raise", evt.RaiseMethod.VirtualAddress); + writer.Write(string.Join("\n", m.Select(x => $"\t\t{x.Key}; // 0x{x.Value:X8}")) + "\n\t}\n"); + } + + if (type.DeclaredEvents.Count > 0) + writer.Write("\n"); + // Methods if (type.DeclaredMethods.Count > 0) writer.Write("\t// Methods\n"); @@ -147,7 +165,7 @@ namespace Il2CppInspector writer.Write("out "); writer.Write($"{param.ParameterType.CSharpName} {param.Name}"); } - writer.Write("); // 0x{0:X}\n", + writer.Write("); // 0x{0:X8}\n", method.VirtualAddress); } writer.Write("}\n"); diff --git a/Il2CppInspector/Metadata.cs b/Il2CppInspector/Metadata.cs index f86acb7..445a497 100644 --- a/Il2CppInspector/Metadata.cs +++ b/Il2CppInspector/Metadata.cs @@ -24,6 +24,7 @@ namespace Il2CppInspector public Il2CppFieldDefinition[] Fields { get; } public Il2CppFieldDefaultValue[] FieldDefaultValues { get; } public Il2CppPropertyDefinition[] Properties { get; } + public Il2CppEventDefinition[] Events { get; } public int[] InterfaceUsageIndices { get; } @@ -55,8 +56,9 @@ namespace Il2CppInspector Fields = ReadArray(Header.fieldsOffset, Header.fieldsCount / Sizeof(typeof(Il2CppFieldDefinition))); FieldDefaultValues = ReadArray(Header.fieldDefaultValuesOffset, Header.fieldDefaultValuesCount / Sizeof(typeof(Il2CppFieldDefaultValue))); Properties = ReadArray(Header.propertiesOffset, Header.propertiesOffset / Sizeof(typeof(Il2CppPropertyDefinition))); + Events = ReadArray(Header.eventsOffset, Header.eventsOffset / Sizeof(typeof(Il2CppEventDefinition))); InterfaceUsageIndices = ReadArray(Header.interfacesOffset, Header.interfacesCount / sizeof(int)); - // TODO: Events, ParameterDefaultValue, GenericParameters, ParameterConstraints, GenericContainers, MetadataUsage, CustomAttributes + // TODO: ParameterDefaultValue, GenericParameters, ParameterConstraints, GenericContainers, MetadataUsage, CustomAttributes // Get all string literals Position = Header.stringOffset; diff --git a/Il2CppInspector/MetadataClasses.cs b/Il2CppInspector/MetadataClasses.cs index 6b59710..df6b4eb 100644 --- a/Il2CppInspector/MetadataClasses.cs +++ b/Il2CppInspector/MetadataClasses.cs @@ -226,4 +226,15 @@ namespace Il2CppInspector public int customAttributeIndex; public uint token; } + + public class Il2CppEventDefinition + { + public int nameIndex; + public int typeIndex; + public int add; + public int remove; + public int raise; + public int customAttributeIndex; + public uint token; + } } diff --git a/Il2CppInspector/Reflection/EventInfo.cs b/Il2CppInspector/Reflection/EventInfo.cs new file mode 100644 index 0000000..a467027 --- /dev/null +++ b/Il2CppInspector/Reflection/EventInfo.cs @@ -0,0 +1,54 @@ +/* + Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com + + All rights reserved. +*/ + +using System.Reflection; + +namespace Il2CppInspector.Reflection +{ + public class EventInfo : MemberInfo + { + // IL2CPP-specific data + public Il2CppEventDefinition Definition { get; } + public int Index { get; } + + // Information/flags about the event + public EventAttributes Attributes { get; } + + // Methods for the event + public MethodInfo AddMethod { get; } + public MethodInfo RemoveMethod { get; } + public MethodInfo RaiseMethod { get; } + + // Event handler delegate type + private Il2CppType eventType; + public TypeInfo EventHandlerType => Assembly.Model.GetType(eventType, MemberTypes.TypeInfo); + + // True if the event has a special name + public bool IsSpecialName => (Attributes & EventAttributes.SpecialName) == EventAttributes.SpecialName; + + public override MemberTypes MemberType => MemberTypes.Event; + + public EventInfo(Il2CppInspector pkg, int eventIndex, TypeInfo declaringType) : + base(declaringType) { + Definition = pkg.Metadata.Events[eventIndex]; + Index = eventIndex; + Name = pkg.Strings[Definition.nameIndex]; + + eventType = pkg.TypeUsages[Definition.typeIndex]; + if ((eventType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME) == Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME) + Attributes |= EventAttributes.SpecialName; + + // 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[Definition.add]; + if (Definition.remove >= 0) + RemoveMethod = declaringType.DeclaredMethods[Definition.remove]; + if (Definition.raise >= 0) + RaiseMethod = declaringType.DeclaredMethods[Definition.raise]; + } + } +} \ No newline at end of file diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index d1b85eb..6ee3f6c 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; namespace Il2CppInspector.Reflection { public class TypeInfo : MemberInfo @@ -41,7 +42,7 @@ namespace Il2CppInspector.Reflection { } public List DeclaredConstructors => throw new NotImplementedException(); - public List DeclaredEvents => throw new NotImplementedException(); + public List DeclaredEvents { get; } = new List(); public List DeclaredFields { get; } = new List(); public List DeclaredMembers => throw new NotImplementedException(); public List DeclaredMethods { get; } = new List(); @@ -129,8 +130,6 @@ namespace Il2CppInspector.Reflection { public Array GetEnumValues() => IsEnum? DeclaredFields.Where(x => x.Name != "value__").Select(x => x.DefaultValue).ToArray() : throw new InvalidOperationException("Type is not an enumeration"); - // TODO: Event stuff - // TODO: Generic stuff // Initialize from specified type index in metadata @@ -198,6 +197,9 @@ namespace Il2CppInspector.Reflection { for (var p = Definition.propertyStart; p < Definition.propertyStart + Definition.property_count; p++) DeclaredProperties.Add(new PropertyInfo(pkg, p, this)); + // Add all events + for (var e = Definition.eventStart; e < Definition.eventStart + Definition.event_count; e++) + DeclaredEvents.Add(new EventInfo(pkg, e, this)); MemberType = MemberTypes.TypeInfo; } diff --git a/README.md b/README.md index 30b4694..7fd8524 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Extract types, methods, properties and fields from Unity IL2CPP binaries. * Supports ARMv7, ARMv7 Thumb T1 and x86 architectures regardless of file format * Supports metadata versions 21, 22, 23 and 24 * No manual reverse-engineering required; all data is calculated automatically +* Special language construct/syntax support for enumerations and events * **Il2CppInspector** re-usable class library for low-level access to IL2CPP binaries and metadata * **Il2CppReflector** re-usable class library for high-level .NET Reflection-style access to IL2CPP types and data as a tree model