Example #1
0
        /// <summary>
        /// Reads a rename map from the specified name into the specified instance of options.
        /// The stream is not closed by this method.
        /// </summary>
        public static void ReadRenameMap(Stream fileStream, bool isGzip, UnhollowerOptions options)
        {
            if (isGzip)
            {
                using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress, true);
                ReadRenameMap(gzipStream, false, options);
                return;
            }

            using var reader = new StreamReader(fileStream, Encoding.UTF8, false, 65536, true);
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                if (string.IsNullOrEmpty(line) || line.StartsWith("#"))
                {
                    continue;
                }
                var split = line.Split(';');
                if (split.Length < 2)
                {
                    continue;
                }
                options.RenameMap[split[0]] = split[1];
            }
        }
        public static void AnalyzeDeobfuscationParams(UnhollowerOptions options)
        {
            RewriteGlobalContext  rewriteContext;
            IIl2CppMetadataAccess inputAssemblies;

            using (new TimingCookie("Reading assemblies"))
                inputAssemblies = new CecilMetadataAccess(options.Source);

            using (new TimingCookie("Creating assembly contexts"))
                rewriteContext = new RewriteGlobalContext(options, inputAssemblies, NullMetadataAccess.Instance, NullMetadataAccess.Instance);

            for (var chars = 1; chars <= 3; chars++)
            {
                for (var uniq = 3; uniq <= 15; uniq++)
                {
                    options.TypeDeobfuscationCharsPerUniquifier = chars;
                    options.TypeDeobfuscationMaxUniquifiers     = uniq;

                    rewriteContext.RenamedTypes.Clear();
                    rewriteContext.RenameGroups.Clear();

                    Pass05CreateRenameGroups.DoPass(rewriteContext);

                    var uniqueTypes    = rewriteContext.RenameGroups.Values.Count(it => it.Count == 1);
                    var nonUniqueTypes = rewriteContext.RenameGroups.Values.Count(it => it.Count > 1);

                    Console.WriteLine($"Chars=\t{chars}\tMaxU=\t{uniq}\tUniq=\t{uniqueTypes}\tNonUniq=\t{nonUniqueTypes}");
                }
            }
        }
Example #3
0
        public static void AnalyzeDeobfuscationParams(UnhollowerOptions options)
        {
            RewriteGlobalContext rewriteContext;

            using (new TimingCookie("Reading assemblies"))
                rewriteContext = new RewriteGlobalContext(options, Directory.EnumerateFiles(options.SourceDir, "*.dll"));

            for (var chars = 1; chars <= 3; chars++)
            {
                for (var uniq = 3; uniq <= 15; uniq++)
                {
                    options.TypeDeobfuscationCharsPerUniquifier = chars;
                    options.TypeDeobfuscationMaxUniquifiers     = uniq;

                    rewriteContext.RenamedTypes.Clear();
                    rewriteContext.RenameGroups.Clear();

                    Pass05CreateRenameGroups.DoPass(rewriteContext);

                    var uniqueTypes    = rewriteContext.RenameGroups.Values.Count(it => it.Count == 1);
                    var nonUniqueTypes = rewriteContext.RenameGroups.Values.Count(it => it.Count > 1);

                    Console.WriteLine($"Chars=\t{chars}\tMaxU=\t{uniq}\tUniq=\t{uniqueTypes}\tNonUniq=\t{nonUniqueTypes}");
                }
            }
        }
Example #4
0
        public static void Main(string[] args)
        {
            LogSupport.InstallConsoleHandlers();

            var options     = new UnhollowerOptions();
            var analyze     = false;
            var generateMap = false;

            foreach (var s in args)
            {
                if (s == ParamAnalyze)
                {
                    analyze = true;
                }
                else if (s == ParamGenerateDeobMap)
                {
                    generateMap = true;
                }
                else if (s == ParamHelp || s == ParamHelpShort || s == ParamHelpShortSlash)
                {
                    PrintUsage();
                    return;
                }
                else if (s == ParamVerbose)
                {
                    LogSupport.TraceHandler += Console.WriteLine;
                    options.Verbose          = true;
                }
                else if (s == ParamNoXrefCache)
                {
                    options.NoXrefCache = true;
                }
                else if (s == ParamNoCopyUnhollowerLibs)
                {
                    options.NoCopyUnhollowerLibs = true;
                }
                else if (s.StartsWith(ParamInputDir))
                {
                    options.SourceDir = s.Substring(ParamInputDir.Length);
                }
                else if (s.StartsWith(ParamOutputDir))
                {
                    options.OutputDir = s.Substring(ParamOutputDir.Length);
                }
                else if (s.StartsWith(ParamMscorlibPath))
                {
                    options.MscorlibPath = s.Substring(ParamMscorlibPath.Length);
                }
                else if (s.StartsWith(ParamUnityDir))
                {
                    options.UnityBaseLibsDir = s.Substring(ParamUnityDir.Length);
                }
                else if (s.StartsWith(ParamGameAssemblyPath))
                {
                    options.GameAssemblyPath = s.Substring(ParamGameAssemblyPath.Length);
                }
                else if (s.StartsWith(ParamUniqChars))
                {
                    options.TypeDeobfuscationCharsPerUniquifier = Int32.Parse(s.Substring(ParamUniqChars.Length));
                }
                else if (s.StartsWith(ParamUniqMax))
                {
                    options.TypeDeobfuscationMaxUniquifiers = Int32.Parse(s.Substring(ParamUniqMax.Length));
                }
                else if (s.StartsWith(ParamBlacklistAssembly))
                {
                    options.AdditionalAssembliesBlacklist.Add(s.Substring(ParamBlacklistAssembly.Length));
                }
                else if (s.StartsWith(ParamObfRegex))
                {
                    options.ObfuscatedNamesRegex = new Regex(s.Substring(ParamObfRegex.Length), RegexOptions.Compiled);
                }
                else if (s.StartsWith(ParamRenameMap))
                {
                    ReadRenameMap(s.Substring(ParamRenameMap.Length), options);
                }
                else if (s.StartsWith(ParamGenerateDeobMapAssembly))
                {
                    options.DeobfuscationGenerationAssemblies.Add(s.Substring(ParamGenerateDeobMapAssembly.Length));
                }
                else if (s.StartsWith(ParamGenerateDeobMapNew))
                {
                    options.DeobfuscationNewAssembliesPath = s.Substring(ParamGenerateDeobMapNew.Length);
                }
                else
                {
                    LogSupport.Error($"Unrecognized option {s}; use -h for help");
                    return;
                }
            }

            if (analyze && generateMap)
            {
                LogSupport.Error($"Can't use {ParamAnalyze} and {ParamGenerateDeobMap} at the same time");
                return;
            }

            if (analyze)
            {
                AnalyzeDeobfuscationParams(options);
            }
            else if (generateMap)
            {
                DeobfuscationMapGenerator.GenerateDeobfuscationMap(options);
            }
            else
            {
                Main(options);
            }
        }
Example #5
0
 /// <summary>
 /// Reads a rename map from the specified name into the specified instance of options
 /// </summary>
 public static void ReadRenameMap(string fileName, UnhollowerOptions options)
 {
     using var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
     ReadRenameMap(fileStream, fileName.EndsWith(".gz"), options);
 }
Example #6
0
        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();
        }
Example #7
0
        public static void Main(string[] args)
        {
            LogSupport.InstallConsoleHandlers();

            var options = new UnhollowerOptions();
            var analyze = false;

            foreach (var s in args)
            {
                if (s == ParamAnalyze)
                {
                    analyze = true;
                }
                else if (s == ParamHelp || s == ParamHelpShort || s == ParamHelpShortSlash)
                {
                    PrintUsage();
                    return;
                }
                else if (s == ParamVerbose)
                {
                    LogSupport.TraceHandler += Console.WriteLine;
                    options.Verbose          = true;
                }
                else if (s.StartsWith(ParamInputDir))
                {
                    options.SourceDir = s.Substring(ParamInputDir.Length);
                }
                else if (s.StartsWith(ParamOutputDir))
                {
                    options.OutputDir = s.Substring(ParamOutputDir.Length);
                }
                else if (s.StartsWith(ParamMscorlibPath))
                {
                    options.MscorlibPath = s.Substring(ParamMscorlibPath.Length);
                }
                else if (s.StartsWith(ParamUnityDir))
                {
                    options.UnityBaseLibsDir = s.Substring(ParamUnityDir.Length);
                }
                else if (s.StartsWith(ParamGameAssemblyPath))
                {
                    options.GameAssemblyPath = s.Substring(ParamGameAssemblyPath.Length);
                }
                else if (s.StartsWith(ParamUniqChars))
                {
                    options.TypeDeobfuscationCharsPerUniquifier = Int32.Parse(s.Substring(ParamUniqChars.Length));
                }
                else if (s.StartsWith(ParamUniqMax))
                {
                    options.TypeDeobfuscationMaxUniquifiers = Int32.Parse(s.Substring(ParamUniqMax.Length));
                }
                else if (s.StartsWith(ParamBlacklistAssembly))
                {
                    options.AdditionalAssembliesBlacklist.Add(s.Substring(ParamBlacklistAssembly.Length));
                }
                else
                {
                    Console.WriteLine($"Unrecognized option {s}; use -h for help");
                    return;
                }
            }

            if (analyze)
            {
                AnalyzeDeobfuscationParams(options);
            }
            else
            {
                Main(options);
            }
        }
        private static int MethodSignatureMatchWeight(MethodDefinition a, MethodDefinition b, UnhollowerOptions options)
        {
            if (a.Parameters.Count != b.Parameters.Count || a.IsStatic != b.IsStatic ||
                (a.Attributes & MethodAttributes.MemberAccessMask) !=
                (b.Attributes & MethodAttributes.MemberAccessMask))
            {
                return(-1);
            }

            var runningSum = TypeMatchWeight(a.ReturnType, b.ReturnType, options);

            if (runningSum == -1)
            {
                return(-1);
            }

            void Accumulate(int i)
            {
                if (i < 0 || runningSum < 0)
                {
                    runningSum = -1;
                }
                else
                {
                    runningSum += i;
                }
            }

            for (var i = 0; i < a.Parameters.Count; i++)
            {
                Accumulate(TypeMatchWeight(a.Parameters[i].ParameterType, b.Parameters[i].ParameterType, options));
            }

            return(runningSum * (a.Parameters.Count + 1));
        }
        private static int TypeMatchWeight(TypeReference a, TypeReference b, UnhollowerOptions options)
        {
            if (a.GetType() != b.GetType())
            {
                return(-1);
            }

            var runningSum = 0;

            void Accumulate(int i)
            {
                if (i < 0 || runningSum < 0)
                {
                    runningSum = -1;
                }
                else
                {
                    runningSum += i;
                }
            }

            switch (a)
            {
            case ArrayType arr:
                if (!(b is ArrayType brr))
                {
                    return(-1);
                }
                return(TypeMatchWeight(arr.ElementType, brr.ElementType, options) * 5);

            case ByReferenceType abr:
                if (!(b is ByReferenceType bbr))
                {
                    return(-1);
                }
                return(TypeMatchWeight(abr.ElementType, bbr.ElementType, options) * 5);

            case GenericInstanceType agi:
                if (!(b is GenericInstanceType bgi))
                {
                    return(-1);
                }
                if (agi.GenericArguments.Count != bgi.GenericArguments.Count)
                {
                    return(-1);
                }
                Accumulate(TypeMatchWeight(agi.ElementType, bgi.ElementType, options));
                for (var i = 0; i < agi.GenericArguments.Count; i++)
                {
                    Accumulate(TypeMatchWeight(agi.GenericArguments[i], bgi.GenericArguments[i], options));
                }
                return(runningSum * 5);

            case GenericParameter:
                if (!(b is GenericParameter))
                {
                    return(-1);
                }
                return(5);

            default:
                if (a.IsNested)
                {
                    if (!b.IsNested)
                    {
                        return(-1);
                    }

                    if (a.Name.IsObfuscated(options))
                    {
                        return(0);
                    }

                    var declMatch = TypeMatchWeight(a.DeclaringType, b.DeclaringType, options);
                    if (declMatch == -1 || a.Name != b.Name)
                    {
                        return(-1);
                    }

                    return(1);
                }
                if (a.Name.IsObfuscated(options))
                {
                    return(0);
                }
                return(a.Name == b.Name && a.Namespace == b.Namespace ? 1 : -1);
            }
        }
        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();
        }
        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("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 methods"))
                    Pass80UnstripMethods.DoPass(rewriteContext);
            }
            else
            {
                Console.WriteLine("Not performing unstripping as unity libs are not specified");
            }

            using (new TimingCookie("Writing assemblies"))
                Pass99WriteToDisk.DoPass(rewriteContext, options);

            File.Copy(typeof(IL2CPP).Assembly.Location, Path.Combine(options.OutputDir, typeof(IL2CPP).Assembly.GetName().Name + ".dll"), true);
            File.Copy(typeof(DelegateSupport).Assembly.Location, Path.Combine(options.OutputDir, typeof(DelegateSupport).Assembly.GetName().Name + ".dll"), true);
            File.Copy(typeof(Decoder).Assembly.Location, Path.Combine(options.OutputDir, typeof(Decoder).Assembly.GetName().Name + ".dll"), true);

            Console.WriteLine("Done!");
        }