private static void HookClassFromType() { LogSupport.Info("xref scanning type to class"); var lib = LoadLibrary("GameAssembly.dll"); LogSupport.Info($"lib: {lib}"); var classFromTypeEntryPoint = GetProcAddress(lib, nameof(IL2CPP.il2cpp_class_from_il2cpp_type)); LogSupport.Info($"hook_method: {classFromTypeEntryPoint}"); var scanner = new XrefScanner(classFromTypeEntryPoint); var targetMethod = scanner.JumpTargets().Single(); LogSupport.Info($"target_method: {targetMethod}"); if (targetMethod == IntPtr.Zero) { return; } IntPtr *targetVarPointer = &targetMethod; DoHook((IntPtr)targetVarPointer, Marshal.GetFunctionPointerForDelegate(new TypeToClassDelegate(ClassFromTypePatch))); ourOriginalTypeToClassMethod = Marshal.GetDelegateForFunctionPointer <TypeToClassDelegate>(targetMethod); LogSupport.Info("patched"); }
public static void DoPass(RewriteGlobalContext context) { var targetAssembly = context.TryGetAssemblyByName("UnityEngine"); if (targetAssembly == null) { LogSupport.Info("No UnityEngine.dll, will not generate forwarders"); return; } var targetModule = targetAssembly.NewAssembly.MainModule; foreach (var assemblyRewriteContext in context.Assemblies) { if (!assemblyRewriteContext.NewAssembly.Name.Name.StartsWith("UnityEngine.")) { continue; } foreach (var mainModuleType in assemblyRewriteContext.NewAssembly.MainModule.Types) { var importedType = targetModule.ImportReference(mainModuleType); var exportedType = new ExportedType(mainModuleType.Namespace, mainModuleType.Name, importedType.Module, importedType.Scope) { Attributes = TypeAttributes.Forwarder }; targetModule.ExportedTypes.Add(exportedType); AddNestedTypes(mainModuleType, exportedType, targetModule); } } }
public void InitPrototype <T>(List <T> list, string path, string nameKey) { LogSupport.Info("start init " + typeof(T).Name); try { list.Clear(); if (Directory.Exists(path)) { var subDir = Directory.GetDirectories(path); foreach (var dir in subDir) { var folder = new DirectoryInfo(dir); foreach (FileInfo tFile in folder.GetFiles()) { if ((tFile.Name.EndsWith("dll") || tFile.Name.EndsWith("exe")) && tFile.Name.Contains(nameKey)) { Assembly ass = Assembly.LoadFile(tFile.DirectoryName + "\\" + tFile.Name); var tl = ass.GetTypes().ToList(); tl.ForEach(v => { if (v.GetInterface(typeof(T).Name) != null) { object obj = Activator.CreateInstance(v); var dp = (T)obj; if (dp != null) { LogSupport.Info("loading:" + tFile.Name); //dp.Initialize(); list.Add(dp); } } }); } } } } } catch (Exception ex) { LogSupport.Error(ex); } LogSupport.Info("Loaded count:" + list.Count.ToString()); }
public static void DoPass(RewriteGlobalContext context) { int methodsUnstripped = 0; int methodsIgnored = 0; foreach (var unityAssembly in context.UnityAssemblies.Assemblies) { var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name.Name); if (processedAssembly == null) { continue; } var imports = processedAssembly.Imports; foreach (var unityType in unityAssembly.MainModule.Types) { var processedType = processedAssembly.TryGetTypeByName(unityType.FullName); if (processedType == null) { continue; } foreach (var unityMethod in unityType.Methods) { if (unityMethod.Name == ".cctor" || unityMethod.Name == ".ctor") { continue; } if (unityMethod.IsAbstract) { continue; } var processedMethod = processedType.TryGetMethodByUnityAssemblyMethod(unityMethod); if (processedMethod != null) { continue; } var returnType = ResolveTypeInNewAssemblies(context, unityMethod.ReturnType, imports); if (returnType == null) { LogSupport.Trace($"Method {unityMethod} has unsupported return type {unityMethod.ReturnType}"); methodsIgnored++; continue; } var newMethod = new MethodDefinition(unityMethod.Name, unityMethod.Attributes & ~MethodAttributes.MemberAccessMask | MethodAttributes.Public, returnType); var hadBadParameter = false; foreach (var unityMethodParameter in unityMethod.Parameters) { var convertedType = ResolveTypeInNewAssemblies(context, unityMethodParameter.ParameterType, imports); if (convertedType == null) { hadBadParameter = true; LogSupport.Trace($"Method {unityMethod} has unsupported parameter type {unityMethodParameter.ParameterType}"); break; } newMethod.Parameters.Add(new ParameterDefinition(unityMethodParameter.Name, unityMethodParameter.Attributes, convertedType)); } if (hadBadParameter) { methodsIgnored++; continue; } foreach (var unityMethodGenericParameter in unityMethod.GenericParameters) { var newParameter = new GenericParameter(unityMethodGenericParameter.Name, newMethod); newParameter.Attributes = unityMethodGenericParameter.Attributes; foreach (var genericParameterConstraint in unityMethodGenericParameter.Constraints) { if (genericParameterConstraint.ConstraintType.FullName == "System.ValueType") { continue; } if (genericParameterConstraint.ConstraintType.Resolve().IsInterface) { continue; } var newType = ResolveTypeInNewAssemblies(context, genericParameterConstraint.ConstraintType, imports); if (newType != null) { newParameter.Constraints.Add(new GenericParameterConstraint(newType)); } } newMethod.GenericParameters.Add(newParameter); } if ((unityMethod.ImplAttributes & MethodImplAttributes.InternalCall) != 0) { var delegateType = UnstripGenerator.CreateDelegateTypeForICallMethod(unityMethod, newMethod, imports); processedType.NewType.NestedTypes.Add(delegateType); delegateType.DeclaringType = processedType.NewType; processedType.NewType.Methods.Add(newMethod); var delegateField = UnstripGenerator.GenerateStaticCtorSuffix(processedType.NewType, delegateType, unityMethod, imports); UnstripGenerator.GenerateInvokerMethodBody(newMethod, delegateField, delegateType, processedType, imports); } else { Pass81FillUnstrippedMethodBodies.PushMethod(unityMethod, newMethod, processedType, imports); processedType.NewType.Methods.Add(newMethod); } if (unityMethod.IsGetter) { GetOrCreateProperty(unityMethod, newMethod).GetMethod = newMethod; } else if (unityMethod.IsSetter) { GetOrCreateProperty(unityMethod, newMethod).SetMethod = newMethod; } var paramsMethod = context.CreateParamsMethod(unityMethod, newMethod, imports, type => ResolveTypeInNewAssemblies(context, type, imports)); if (paramsMethod != null) { processedType.NewType.Methods.Add(paramsMethod); } methodsUnstripped++; } } } LogSupport.Info(""); // finish the progress line LogSupport.Info($"{methodsUnstripped} methods restored"); LogSupport.Info($"{methodsIgnored} methods failed to restore"); }
public static void RegisterTypeInIl2Cpp <T>() where T : class { var type = typeof(T); if (type.IsGenericType || type.IsGenericTypeDefinition) { throw new ArgumentException($"Type {type} is generic and can't be used in il2cpp"); } var currentPointer = Il2CppClassPointerStore <T> .NativeClassPtr; if (currentPointer != IntPtr.Zero) { throw new ArgumentException($"Type {type} is already registered in il2cpp"); } var baseType = type.BaseType; var baseClassPointer = UnityVersionHandler.Wrap((Il2CppClass *)ReadClassPointerForType(baseType)); if (baseClassPointer == null) { throw new ArgumentException($"Base class {baseType} of class {type} is not registered in il2cpp"); } if ((baseClassPointer.Bitfield1 & ClassBitfield1.valuetype) != 0 || (baseClassPointer.Bitfield1 & ClassBitfield1.enumtype) != 0) { throw new ArgumentException($"Base class {baseType} is value type and can't be inherited from"); } if ((baseClassPointer.Bitfield1 & ClassBitfield1.is_generic) != 0) { throw new ArgumentException($"Base class {baseType} is generic and can't be inherited from"); } if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_SEALED) != 0) { throw new ArgumentException($"Base class {baseType} is sealed and can't be inherited from"); } if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_INTERFACE) != 0) { throw new ArgumentException($"Base class {baseType} is an interface and can't be inherited from"); } lock (InjectedTypes) if (!InjectedTypes.Add(typeof(T).FullName)) { throw new ArgumentException($"Type with FullName {typeof(T).FullName} is already injected. Don't inject the same type twice, or use a different namespace"); } if (ourOriginalTypeToClassMethod == null) { HookClassFromType(); } var classPointer = UnityVersionHandler.NewClass(baseClassPointer.VtableCount); classPointer.Image = FakeImage; classPointer.Parent = baseClassPointer.ClassPointer; classPointer.ElementClass = classPointer.Class = classPointer.CastClass = classPointer.ClassPointer; classPointer.NativeSize = -1; classPointer.ActualSize = classPointer.InstanceSize = baseClassPointer.InstanceSize + (uint)IntPtr.Size; classPointer.Bitfield1 = ClassBitfield1.initialized | ClassBitfield1.initialized_and_no_error | ClassBitfield1.size_inited; classPointer.Bitfield2 = ClassBitfield2.has_finalize | ClassBitfield2.is_vtable_initialized; classPointer.Name = Marshal.StringToHGlobalAnsi(type.Name); classPointer.Namespace = Marshal.StringToHGlobalAnsi(type.Namespace); classPointer.ThisArg.type = classPointer.ByValArg.type = Il2CppTypeEnum.IL2CPP_TYPE_CLASS; classPointer.ThisArg.mods_byref_pin = 64; classPointer.Flags = baseClassPointer.Flags; // todo: adjust flags? var eligibleMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Where(IsMethodEligible).ToArray(); var methodCount = 2 + eligibleMethods.Length; // 1 is the finalizer, 1 is empty ctor classPointer.MethodCount = (ushort)methodCount; var methodPointerArray = (Il2CppMethodInfo **)Marshal.AllocHGlobal(methodCount * IntPtr.Size); classPointer.Methods = methodPointerArray; methodPointerArray[0] = ConvertStaticMethod(FinalizeDelegate, "Finalize", classPointer); methodPointerArray[1] = ConvertStaticMethod(CreateEmptyCtor(type), ".ctor", classPointer); for (var i = 0; i < eligibleMethods.Length; i++) { var methodInfo = eligibleMethods[i]; methodPointerArray[i + 2] = ConvertMethodInfo(methodInfo, classPointer); } var vTablePointer = (VirtualInvokeData *)classPointer.VTable; var baseVTablePointer = (VirtualInvokeData *)baseClassPointer.VTable; classPointer.VtableCount = baseClassPointer.VtableCount; for (var i = 0; i < classPointer.VtableCount; i++) { vTablePointer[i] = baseVTablePointer[i]; if (Marshal.PtrToStringAnsi(vTablePointer[i].method->name) == "Finalize") // slot number is not static { vTablePointer[i].method = methodPointerArray[0]; vTablePointer[i].methodPtr = methodPointerArray[0]->methodPointer; } } var newCounter = Interlocked.Decrement(ref ourClassOverrideCounter); FakeTokenClasses[newCounter] = classPointer.Pointer; classPointer.ByValArg.data = classPointer.ThisArg.data = (IntPtr)newCounter; RuntimeSpecificsStore.SetClassInfo(classPointer.Pointer, true, true); Il2CppClassPointerStore <T> .NativeClassPtr = classPointer.Pointer; LogSupport.Info($"Registered mono type {typeof(T)} in il2cpp domain"); }
public static void Main(UnhollowerOptions options) { if (string.IsNullOrEmpty(options.SourceDir)) { Console.WriteLine("No input dir specified; use -h for help"); return; } if (string.IsNullOrEmpty(options.OutputDir)) { Console.WriteLine("No target dir specified; use -h for help"); return; } if (string.IsNullOrEmpty(options.MscorlibPath)) { Console.WriteLine("No mscorlib specified; use -h for help"); return; } if (!Directory.Exists(options.OutputDir)) { Directory.CreateDirectory(options.OutputDir); } RewriteGlobalContext rewriteContext; using (new TimingCookie("Reading assemblies")) rewriteContext = new RewriteGlobalContext(options, Directory.EnumerateFiles(options.SourceDir, "*.dll")); using (new TimingCookie("Computing renames")) Pass05CreateRenameGroups.DoPass(rewriteContext); using (new TimingCookie("Creating typedefs")) Pass10CreateTypedefs.DoPass(rewriteContext); using (new TimingCookie("Computing struct blittability")) Pass11ComputeTypeSpecifics.DoPass(rewriteContext); using (new TimingCookie("Filling typedefs")) Pass12FillTypedefs.DoPass(rewriteContext); using (new TimingCookie("Filling generic constraints")) Pass13FillGenericConstraints.DoPass(rewriteContext); using (new TimingCookie("Creating members")) Pass15GenerateMemberContexts.DoPass(rewriteContext); using (new TimingCookie("Scanning method cross-references")) Pass16ScanMethodRefs.DoPass(rewriteContext, options); using (new TimingCookie("Finalizing method declarations")) Pass18FinalizeMethodContexts.DoPass(rewriteContext); LogSupport.Info($"{Pass18FinalizeMethodContexts.TotalPotentiallyDeadMethods} total potentially dead methods"); using (new TimingCookie("Filling method parameters")) Pass19CopyMethodParameters.DoPass(rewriteContext); using (new TimingCookie("Creating static constructors")) Pass20GenerateStaticConstructors.DoPass(rewriteContext); using (new TimingCookie("Creating value type fields")) Pass21GenerateValueTypeFields.DoPass(rewriteContext); using (new TimingCookie("Creating enums")) Pass22GenerateEnums.DoPass(rewriteContext); using (new TimingCookie("Creating IntPtr constructors")) Pass23GeneratePointerConstructors.DoPass(rewriteContext); using (new TimingCookie("Creating type getters")) Pass24GenerateTypeStaticGetters.DoPass(rewriteContext); using (new TimingCookie("Creating non-blittable struct constructors")) Pass25GenerateNonBlittableValueTypeDefaultCtors.DoPass(rewriteContext); using (new TimingCookie("Creating generic method static constructors")) Pass30GenerateGenericMethodStoreConstructors.DoPass(rewriteContext); using (new TimingCookie("Creating field accessors")) Pass40GenerateFieldAccessors.DoPass(rewriteContext); using (new TimingCookie("Filling methods")) Pass50GenerateMethods.DoPass(rewriteContext); using (new TimingCookie("Generating implicit conversions")) Pass60AddImplicitConversions.DoPass(rewriteContext); using (new TimingCookie("Creating properties")) Pass70GenerateProperties.DoPass(rewriteContext); if (options.UnityBaseLibsDir != null) { using (new TimingCookie("Unstripping types")) Pass79UnstripTypes.DoPass(rewriteContext); using (new TimingCookie("Unstripping fields")) Pass80UnstripFields.DoPass(rewriteContext); using (new TimingCookie("Unstripping methods")) Pass80UnstripMethods.DoPass(rewriteContext); using (new TimingCookie("Unstripping method bodies")) Pass81FillUnstrippedMethodBodies.DoPass(rewriteContext); } else { LogSupport.Warning("Not performing unstripping as unity libs are not specified"); } using (new TimingCookie("Generating forwarded types")) Pass89GenerateForwarders.DoPass(rewriteContext); using (new TimingCookie("Writing xref cache")) Pass89GenerateMethodXrefCache.DoPass(rewriteContext, options); using (new TimingCookie("Writing assemblies")) Pass90WriteToDisk.DoPass(rewriteContext, options); using (new TimingCookie("Writing method pointer map")) Pass91GenerateMethodPointerMap.DoPass(rewriteContext, options); if (!options.NoCopyUnhollowerLibs) { File.Copy(typeof(IL2CPP).Assembly.Location, Path.Combine(options.OutputDir, typeof(IL2CPP).Assembly.GetName().Name + ".dll"), true); File.Copy(typeof(RuntimeLibMarker).Assembly.Location, Path.Combine(options.OutputDir, typeof(RuntimeLibMarker).Assembly.GetName().Name + ".dll"), true); File.Copy(typeof(Decoder).Assembly.Location, Path.Combine(options.OutputDir, typeof(Decoder).Assembly.GetName().Name + ".dll"), true); } LogSupport.Info("Done!"); rewriteContext.Dispose(); }
public void Dispose() { LogSupport.Info($"Done in {myStopwatch.Elapsed}"); }
public TimingCookie(string message) { LogSupport.Info(message + "... "); myStopwatch = Stopwatch.StartNew(); }
public static void GenerateDeobfuscationMap(UnhollowerOptions options) { if (string.IsNullOrEmpty(options.SourceDir)) { Console.WriteLine("No input dir specified; use -h for help"); return; } if (string.IsNullOrEmpty(options.OutputDir)) { Console.WriteLine("No target dir specified; use -h for help"); return; } if (string.IsNullOrEmpty(options.DeobfuscationNewAssembliesPath)) { Console.WriteLine("No obfuscated assembly path specified; use -h for help"); return; } if (!Directory.Exists(options.OutputDir)) { Directory.CreateDirectory(options.OutputDir); } RewriteGlobalContext rewriteContext; IIl2CppMetadataAccess inputAssemblies; IIl2CppMetadataAccess systemAssemblies; using (new TimingCookie("Reading assemblies")) inputAssemblies = new CecilMetadataAccess(Directory.EnumerateFiles(options.DeobfuscationNewAssembliesPath, "*.dll")); using (new TimingCookie("Reading system assemblies")) { if (!string.IsNullOrEmpty(options.SystemLibrariesPath)) { systemAssemblies = new CecilMetadataAccess(Directory.EnumerateFiles(options.SystemLibrariesPath, "*.dll") .Where(it => Path.GetFileName(it).StartsWith("System.") || Path.GetFileName(it) == "mscorlib.dll" || Path.GetFileName(it) == "netstandard.dll")); } else { systemAssemblies = new CecilMetadataAccess(new[] { options.MscorlibPath }); } } using (new TimingCookie("Creating rewrite assemblies")) rewriteContext = new RewriteGlobalContext(options, inputAssemblies, systemAssemblies, NullMetadataAccess.Instance); using (new TimingCookie("Computing renames")) Pass05CreateRenameGroups.DoPass(rewriteContext); using (new TimingCookie("Creating typedefs")) Pass10CreateTypedefs.DoPass(rewriteContext); using (new TimingCookie("Computing struct blittability")) Pass11ComputeTypeSpecifics.DoPass(rewriteContext); using (new TimingCookie("Filling typedefs")) Pass12FillTypedefs.DoPass(rewriteContext); using (new TimingCookie("Filling generic constraints")) Pass13FillGenericConstraints.DoPass(rewriteContext); using (new TimingCookie("Creating members")) Pass15GenerateMemberContexts.DoPass(rewriteContext); RewriteGlobalContext cleanContext; IIl2CppMetadataAccess cleanAssemblies; using (new TimingCookie("Reading clean assemblies")) cleanAssemblies = new CecilMetadataAccess(Directory.EnumerateFiles(options.SourceDir, "*.dll")); using (new TimingCookie("Creating clean rewrite assemblies")) cleanContext = new RewriteGlobalContext(options, cleanAssemblies, systemAssemblies, NullMetadataAccess.Instance); using (new TimingCookie("Computing clean assembly renames")) Pass05CreateRenameGroups.DoPass(cleanContext); using (new TimingCookie("Creating clean assembly typedefs")) Pass10CreateTypedefs.DoPass(cleanContext); var usedNames = new Dictionary <TypeDefinition, (string OldName, int Penalty, bool ForceNs)>(); using var fileOutput = new FileStream(options.OutputDir + Path.DirectorySeparatorChar + "RenameMap.csv.gz", FileMode.Create, FileAccess.Write); using var gzipStream = new GZipStream(fileOutput, CompressionLevel.Optimal, true); using var writer = new StreamWriter(gzipStream, Encoding.UTF8, 65536, true); void DoEnum(TypeRewriteContext obfuscatedType, TypeRewriteContext cleanType) { foreach (var originalTypeField in obfuscatedType.OriginalType.Fields) { if (!originalTypeField.Name.IsObfuscated(obfuscatedType.AssemblyContext.GlobalContext.Options)) { continue; } var matchedField = cleanType.OriginalType.Fields[obfuscatedType.OriginalType.Fields.IndexOf(originalTypeField)]; writer.WriteLine(obfuscatedType.NewType.GetNamespacePrefix() + "." + obfuscatedType.NewType.Name + "::" + Pass22GenerateEnums.GetUnmangledName(originalTypeField) + ";" + matchedField.Name + ";0"); } } foreach (var assemblyContext in rewriteContext.Assemblies) { if (options.DeobfuscationGenerationAssemblies.Count > 0 && !options.DeobfuscationGenerationAssemblies.Contains(assemblyContext.NewAssembly.Name.Name)) { continue; } var cleanAssembly = cleanContext.GetAssemblyByName(assemblyContext.OriginalAssembly.Name.Name); void DoType(TypeRewriteContext typeContext, TypeRewriteContext?enclosingType) { if (!typeContext.OriginalNameWasObfuscated) { return; } var cleanType = FindBestMatchType(typeContext, cleanAssembly, enclosingType); if (cleanType.Item1 == null) { return; } if (!usedNames.TryGetValue(cleanType.Item1.NewType, out var existing) || existing.Item2 < cleanType.Item2) { usedNames[cleanType.Item1.NewType] = (typeContext.NewType.GetNamespacePrefix() + "." + typeContext.NewType.Name, cleanType.Item2, typeContext.OriginalType.Namespace != cleanType.Item1.OriginalType.Namespace); } else { return; } if (typeContext.OriginalType.IsEnum) { DoEnum(typeContext, cleanType.Item1); } foreach (var originalTypeNestedType in typeContext.OriginalType.NestedTypes) { DoType(typeContext.AssemblyContext.GetContextForOriginalType(originalTypeNestedType), cleanType.Item1); } } foreach (var typeContext in assemblyContext.Types) { if (typeContext.NewType.DeclaringType != null) { continue; } DoType(typeContext, null); } } foreach (var keyValuePair in usedNames) { writer.WriteLine(keyValuePair.Value.Item1 + ";" + (keyValuePair.Value.ForceNs ? keyValuePair.Key.Namespace + "." : "") + keyValuePair.Key.Name + ";" + keyValuePair.Value.Item2); } LogSupport.Info("Done!"); rewriteContext.Dispose(); }
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { LogSupport.Info(e.ToString()); }
public static void DoPass(RewriteGlobalContext context) { int fieldsUnstripped = 0; int fieldsIgnored = 0; foreach (var unityAssembly in context.UnityAssemblies.Assemblies) { var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name.Name); if (processedAssembly == null) { continue; } var imports = processedAssembly.Imports; foreach (var unityType in unityAssembly.MainModule.Types) { var processedType = processedAssembly.TryGetTypeByName(unityType.FullName); if (processedType == null) { continue; } if (!unityType.IsValueType || unityType.IsEnum) { continue; } foreach (var unityField in unityType.Fields) { if (unityField.IsStatic && !unityField.HasConstant) { continue; } if (processedType.NewType.IsExplicitLayout && !unityField.IsStatic) { continue; } var processedField = processedType.TryGetFieldByUnityAssemblyField(unityField); if (processedField != null) { continue; } var fieldType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(context, unityField.FieldType, imports); if (fieldType == null) { LogSupport.Trace($"Field {unityField} on type {unityType.FullName} has unsupported type {unityField.FieldType}, the type will be unusable"); fieldsIgnored++; continue; } var newField = new FieldDefinition(unityField.Name, unityField.Attributes & ~FieldAttributes.FieldAccessMask | FieldAttributes.Public, fieldType); if (unityField.HasConstant) { newField.Constant = unityField.Constant; } processedType.NewType.Fields.Add(newField); fieldsUnstripped++; } } } LogSupport.Info(""); // finish the progress line LogSupport.Info($"{fieldsUnstripped} fields restored"); LogSupport.Info($"{fieldsIgnored} fields failed to restore"); }
public static void DoPass(RewriteGlobalContext context) { var unityAssemblyFiles = Directory.EnumerateFiles(context.Options.UnityBaseLibsDir, "*.dll"); var loadedAssemblies = unityAssemblyFiles.Select(it => AssemblyDefinition.ReadAssembly(it, new ReaderParameters(ReadingMode.Deferred))).ToList(); int fieldsUnstripped = 0; int fieldsIgnored = 0; foreach (var unityAssembly in loadedAssemblies) { var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name.Name); if (processedAssembly == null) { continue; } var imports = processedAssembly.Imports; foreach (var unityType in unityAssembly.MainModule.Types) { var processedType = processedAssembly.TryGetTypeByName(unityType.FullName); if (processedType == null) { continue; } if (!unityType.IsValueType || unityType.IsEnum) { continue; } foreach (var unityField in unityType.Fields) { if (unityField.IsStatic) { continue; } var processedField = processedType.TryGetFieldByUnityAssemblyField(unityField); if (processedField != null) { continue; } var fieldType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(context, unityField.FieldType, imports); if (fieldType == null) { LogSupport.Trace($"Field {unityField} on type {unityType.FullName} has unsupported type {unityField.FieldType}, the type will be unusable"); fieldsIgnored++; continue; } var newMethod = new FieldDefinition(unityField.Name, unityField.Attributes & ~FieldAttributes.FieldAccessMask | FieldAttributes.Public, fieldType); processedType.NewType.Fields.Add(newMethod); fieldsUnstripped++; } } } LogSupport.Info(""); // finish the progress line LogSupport.Info($"{fieldsUnstripped} fields restored"); LogSupport.Info($"{fieldsIgnored} fields failed to restore"); }