internal DllField(FieldDefinition f) { LayoutOffset = f.Offset; DeclaringType = DllTypeRef.From(f.DeclaringType); Type = DllTypeRef.From(f.FieldType); Name = f.Name; Offset = -1; if (f.HasCustomAttributes) { foreach (var ca in f.CustomAttributes) { if (ca.AttributeType.Name == "FieldOffsetAttribute") { if (ca.Fields.Count > 0) { Offset = Convert.ToInt32(ca.Fields.FirstOrDefault().Argument.Value as string, 16); } } else { // Ignore the DummyDll attributes var atr = new DllAttribute(ca); if (!string.IsNullOrEmpty(atr.Name)) { Attributes.Add(atr); } } } } Specifiers.AddRange(DllSpecifierHelpers.From(f)); This = f; Constant = f.Constant; }
internal DllProperty(PropertyDefinition p) { DeclaringType = DllTypeRef.From(p.DeclaringType); Type = DllTypeRef.From(p.PropertyType); Name = p.Name; GetMethod = p.GetMethod != null; SetMethod = p.SetMethod != null; if (p.HasCustomAttributes) { Attributes.AddRange(p.CustomAttributes.Select(ca => new DllAttribute(ca)).Where(a => !string.IsNullOrEmpty(a.Name))); } Specifiers.AddRange(DllSpecifierHelpers.From(p)); }
private ITypeData?Resolve(DllTypeRef typeRef) { // Generic parameters can never "Resolve" if (typeRef.IsGenericParameter) { return(null); } // TODO: Resolve only among our types that we actually plan on serializing // Basically, check it against our whitelist/blacklist ITypeData ret; if (typeRef.IsGenericInstance) { // This is a generic instance. We want to convert this instance to a generic type that we have already created in _types var def = typeRef.This.Resolve(); var check = DllTypeRef.From(def); // Try to get our Generic Definition out of _types if (!_types.TryGetValue(check, out ret)) { // This should never happen. All generic definitions should already be resolved. throw new InvalidOperationException($"Generic instance: {typeRef} (definition: {check}) cannot map to any type in _types!"); } return(ret); } if (!_types.TryGetValue(typeRef, out ret)) { var def = typeRef.This.Resolve(); if (def != null) { ret = new DllTypeData(def, _config); if (!_types.ContainsKey(ret.This)) { Console.Error.WriteLine($"Too late to add {def} to Types!"); } else { Console.Error.WriteLine($"{typeRef} already existed in _types?! Matching item: {_types[ret.This].This}"); ret = _types[ret.This]; } } else { throw new InvalidOperationException($"Non-generic-parameter {typeRef} cannot be resolved!"); } } return(ret); }
internal DllTypeData(TypeDefinition def, DllConfig config) { _config = config; foreach (var i in def.Interfaces) { ImplementingInterfaces.Add(DllTypeRef.From(i.InterfaceType)); } This = DllTypeRef.From(def); Type = def.IsEnum ? TypeEnum.Enum : (def.IsInterface ? TypeEnum.Interface : (def.IsValueType ? TypeEnum.Struct : TypeEnum.Class)); Info = new TypeInfo { Refness = def.IsValueType ? Refness.ValueType : Refness.ReferenceType }; if (def.BaseType != null) { Parent = DllTypeRef.From(def.BaseType); } // TODO: Parse this eventually TypeDefIndex = -1; if (_config.ParseTypeAttributes && def.HasCustomAttributes) { Attributes.AddRange(def.CustomAttributes.Select(ca => new DllAttribute(ca)).Where(a => !string.IsNullOrEmpty(a.Name))); } Layout = (ITypeData.LayoutKind)(def.Attributes & TypeAttributes.LayoutMask); if (_config.ParseTypeFields) { InstanceFields.AddRange(def.Fields.Where(f => !f.IsStatic).Select(f => new DllField(f))); StaticFields.AddRange(def.Fields.Where(f => f.IsStatic).Select(f => new DllField(f))); } if (_config.ParseTypeProperties) { Properties.AddRange(def.Properties.Select(p => new DllProperty(p))); } if (_config.ParseTypeMethods) { var mappedBaseMethods = new HashSet <MethodDefinition>(); var methods = def.Methods.Select(m => DllMethod.From(m, ref mappedBaseMethods)).ToList(); // It's important that Foo.IBar.func() goes after func() (if present) Methods.AddRange(methods.Where(m => m.ImplementedFrom is null)); Methods.AddRange(methods.Where(m => m.ImplementedFrom != null)); } }
internal DllField(FieldDefinition f, TypeDefinition info) { LayoutOffset = f.Offset; DeclaringType = DllTypeRef.From(f.DeclaringType); Type = DllTypeRef.From(f.FieldType); Name = f.Name; Offset = -1; if (f.HasCustomAttributes) { foreach (var ca in f.CustomAttributes) { if (ca.AttributeType.Name == "FieldOffsetAttribute" || ca.AttributeType.Name == "StaticFieldOffsetAttribute") { if (ca.Fields.Count > 0) { Offset = Convert.ToInt32(ca.Fields.FirstOrDefault().Argument.Value as string, 16); } //if (info.IsEnum) // // Because Il2CppInspector is bad and emits 0x10 for fields on enums. I seriously don't know why. // Offset -= 0x10; } else { // Ignore the DummyDll attributes var atr = new DllAttribute(ca); if (!string.IsNullOrEmpty(atr.Name)) { Attributes.Add(atr); } } } } Specifiers.AddRange(DllSpecifierHelpers.From(f)); This = f; Constant = f.Constant; }
internal DllData(string dir, DllConfig config) { _config = config; _dir = dir; AddSearchDirectory(dir); _metadataResolver = new MetadataResolver(this); _readerParams = new ReaderParameters(ReadingMode.Immediate) { AssemblyResolver = this, MetadataResolver = _metadataResolver }; var modules = new List <ModuleDefinition>(); foreach (var file in Directory.GetFiles(dir)) { if (file.EndsWith(".dll") && !_config.BlacklistDlls.Contains(file)) { var assemb = AssemblyDefinition.ReadAssembly(file, _readerParams); foreach (var module in assemb.Modules) { modules.Add(module); } } } Queue <TypeDefinition> frontier = new Queue <TypeDefinition>(); modules.ForEach(m => m.Types.ToList().ForEach(t => { if (_config.ParseTypes && !_config.BlacklistTypes.Contains(t.Name)) { frontier.Enqueue(t); } })); while (frontier.Count > 0) { var t = frontier.Dequeue(); if (t.Name.StartsWith("<") && t.Namespace.Length == 0 && t.DeclaringType is null) { if (!t.Name.StartsWith("<Module>") && !t.Name.StartsWith("<PrivateImplementationDetails>")) { Console.Error.WriteLine($"Skipping TypeDefinition {t}"); } continue; } var dllRef = DllTypeRef.From(t); if (!_types.ContainsKey(dllRef)) { var type = new DllTypeData(t, _config); if (dllRef.DeclaringType != null) { _types[dllRef.DeclaringType].NestedTypes.AddOrThrow(type); } foreach (var nested in t.NestedTypes) { frontier.Enqueue(nested); } _types.Add(dllRef, type); } else { Console.Error.WriteLine($"{dllRef} already in _types! Matching item: {_types[dllRef].This}"); } } int total = DllTypeRef.Hits + DllTypeRef.Misses; Console.WriteLine($"{nameof(DllTypeRef)} cache hits: {DllTypeRef.Hits} / {total} = {100.0f * DllTypeRef.Hits / total}"); // Ignore images for now. }
private DllMethod(MethodDefinition m, ref HashSet <MethodDefinition> mappedBaseMethods) { cache.Add(m, this); This = m; // Il2CppName is the MethodDefinition Name (hopefully we don't need to convert it for il2cpp, but we might) Il2CppName = m.Name; Name = m.Name; Parameters.AddRange(m.Parameters.Select(p => new Parameter(p))); Specifiers.AddRange(DllSpecifierHelpers.From(m)); // This is not necessary: m.GenericParameters.Any(param => !m.DeclaringType.GenericParameters.Contains(param)); Generic = m.HasGenericParameters; GenericParameters = m.GenericParameters.Select(g => DllTypeRef.From(g)).ToList(); // This may not always be the case, we could have a special name in which case we have to do some sorcery // Grab the special name, grab the type from the special name int idxDot = Name.LastIndexOf('.'); if (idxDot >= 2) { // Call a utilities function for converting a special name method to a proper base method var baseMethod = m.GetSpecialNameBaseMethod(out var iface, idxDot); if (baseMethod is null) { throw new Exception("Failed to find baseMethod for dotted method name!"); } if (iface is null) { throw new Exception("Failed to get iface for dotted method name!"); } if (!mappedBaseMethods.Add(baseMethod)) { throw new InvalidOperationException($"Base method: {baseMethod} has already been overriden!"); } // Only one base method for special named methods BaseMethods.Add(From(baseMethod, ref mappedBaseMethods)); ImplementedFrom = DllTypeRef.From(iface); } else { var baseMethod = m.GetBaseMethod(); if (baseMethod == m) { var baseMethods = m.GetBaseMethods(); if (baseMethods.Count > 0) { HidesBase = true; } // We need to check here SPECIFICALLY for a method in our declaring type that shares the same name as us, since we could have the same BaseMethod as it. // If either ourselves or a method of the same safe name (after . prefixes) exists, we need to ensure that only the one with the dots gets the base method // It correctly describes. // Basically, we need to take all our specially named methods on our type that have already been defined and remove them from our current list of baseMethods. // We should only ever have baseMethods of methods that are of methods that we haven't already used yet. if (baseMethods.Count > 0) { foreach (var baseM in mappedBaseMethods) { baseMethods.Remove(baseM); } } foreach (var bm in baseMethods) { BaseMethods.Add(From(bm, ref mappedBaseMethods)); } } else { if (!mappedBaseMethods.Add(baseMethod)) { throw new InvalidOperationException($"Base method: {baseMethod} has already been overriden!"); } BaseMethods.Add(From(baseMethod, ref mappedBaseMethods)); } } if (BaseMethods.Count > 0) { // TODO: This may not be true for generic methods. Should ensure validity for IEnumerator<T> methods // This method is an implemented/overriden method. // TODO: We need to double check to see if we need multiple ImplementedFroms ImplementedFrom = BaseMethods.First().DeclaringType; // Add ourselves to our BaseMethod's ImplementingMethods foreach (var bm in BaseMethods) { bm.ImplementingMethods.Add(this); } } ReturnType = DllTypeRef.From(m.ReturnType); DeclaringType = DllTypeRef.From(m.DeclaringType); // This is a very rare condition that we need to handle if it ever happens, but for now just log it if (m.HasOverrides) { Console.WriteLine($"{m}.HasOverrides!!! Overrides: {string.Join(", ", m.Overrides)}"); } RVA = -1; Offset = -1; VA = -1; Slot = -1; if (m.HasCustomAttributes) { foreach (var ca in m.CustomAttributes) { if (ca.AttributeType.Name == "AddressAttribute") { if (ca.Fields.Count >= 3) { for (int i = 0; i < ca.Fields.Count; i++) { var f = ca.Fields[i]; if (f.Name == "RVA" || f.Name == "Offset" || f.Name == "VA") { var val = Convert.ToInt32(f.Argument.Value as string, 16); if (f.Name == "RVA") { RVA = val; } else if (f.Name == "Offset") { Offset = val; } else if (f.Name == "VA") { VA = val; } } else if (f.Name == "Slot") { Slot = Convert.ToInt32(f.Argument.Value as string); } } } } else { // Ignore the DummyDll attributes Attributes.Add(new DllAttribute(ca)); } } } }