示例#1
0
        /// <summary>Process the result from an instruction handler.</summary>
        /// <param name="mod">The mod being analyzed.</param>
        /// <param name="handler">The instruction handler.</param>
        /// <param name="result">The result returned by the handler.</param>
        /// <param name="loggedMessages">The messages already logged for the current mod.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <param name="filename">The assembly filename for log messages.</param>
        private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet <string> loggedMessages, string logPrefix, string filename)
        {
            switch (result)
            {
            case InstructionHandleResult.Rewritten:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewrote {filename} to fix {handler.NounPhrase}...");
                break;

            case InstructionHandleResult.NotCompatible:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Broken code in {filename}: {handler.NounPhrase}.");
                mod.SetWarning(ModWarning.BrokenCodeLoaded);
                break;

            case InstructionHandleResult.DetectedGamePatch:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected game patcher ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.PatchesGame);
                break;

            case InstructionHandleResult.DetectedSaveSerializer:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected possible save serializer change ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.ChangesSaveSerializer);
                break;

            case InstructionHandleResult.DetectedUnvalidatedUpdateTick:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected reference to {handler.NounPhrase} in assembly {filename}.");
                mod.SetWarning(ModWarning.UsesUnvalidatedUpdateTick);
                break;

            case InstructionHandleResult.DetectedDynamic:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected 'dynamic' keyword ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.UsesDynamic);
                break;

            case InstructionHandleResult.DetectedConsoleAccess:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected direct console access ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.AccessesConsole);
                break;

            case InstructionHandleResult.DetectedFilesystemAccess:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected filesystem access ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.AccessesFilesystem);
                break;

            case InstructionHandleResult.DetectedShellAccess:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected shell or process access ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.AccessesShell);
                break;

            case InstructionHandleResult.None:
                break;

            default:
                throw new NotSupportedException($"Unrecognized instruction handler result '{result}'.");
            }
        }
示例#2
0
        /// <summary>Process the result from an instruction handler.</summary>
        /// <param name="mod">The mod being analysed.</param>
        /// <param name="handler">The instruction handler.</param>
        /// <param name="result">The result returned by the handler.</param>
        /// <param name="loggedMessages">The messages already logged for the current mod.</param>
        /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <param name="filename">The assembly filename for log messages.</param>
        private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet <string> loggedMessages, string logPrefix, bool assumeCompatible, string filename)
        {
            switch (result)
            {
            case InstructionHandleResult.Rewritten:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewrote {filename} to fix {handler.NounPhrase}...");
                break;

            case InstructionHandleResult.NotCompatible:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Broken code in {filename}: {handler.NounPhrase}.");
                if (!assumeCompatible)
                {
                    throw new IncompatibleInstructionException(handler.NounPhrase, $"Found an incompatible CIL instruction ({handler.NounPhrase}) while loading assembly {filename}.");
                }
                mod.SetWarning(ModWarning.BrokenCodeLoaded);
                break;

            case InstructionHandleResult.DetectedGamePatch:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected game patcher ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.PatchesGame);
                break;

            case InstructionHandleResult.DetectedSaveSerialiser:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected possible save serialiser change ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.ChangesSaveSerialiser);
                break;

            case InstructionHandleResult.DetectedUnvalidatedUpdateTick:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected reference to {handler.NounPhrase} in assembly {filename}.");
                mod.SetWarning(ModWarning.UsesUnvalidatedUpdateTick);
                break;

            case InstructionHandleResult.DetectedDynamic:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected 'dynamic' keyword ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.UsesDynamic);
                break;

            case InstructionHandleResult.None:
                break;

            default:
                throw new NotSupportedException($"Unrecognised instruction handler result '{result}'.");
            }
        }
示例#3
0
        /// <summary>Preprocess and load an assembly.</summary>
        /// <param name="mod">The mod for which the assembly is being loaded.</param>
        /// <param name="assemblyPath">The assembly file path.</param>
        /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
        /// <returns>Returns the rewrite metadata for the preprocessed assembly.</returns>
        /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
        public Assembly Load(IModMetadata mod, string assemblyPath, bool assumeCompatible)
        {
            // get referenced local assemblies
            AssemblyParseResult[] assemblies;
            {
                HashSet <string> visitedAssemblyNames = new HashSet <string>(AppDomain.CurrentDomain.GetAssemblies().Select(p => p.GetName().Name)); // don't try loading assemblies that are already loaded
                assemblies = this.GetReferencedLocalAssemblies(new FileInfo(assemblyPath), visitedAssemblyNames, this.AssemblyDefinitionResolver).ToArray();
            }

            // validate load
            if (!assemblies.Any() || assemblies[0].Status == AssemblyLoadStatus.Failed)
            {
                throw new SAssemblyLoadFailedException(!File.Exists(assemblyPath)
                    ? $"Could not load '{assemblyPath}' because it doesn't exist."
                    : $"Could not load '{assemblyPath}'."
                                                       );
            }
            if (assemblies.Last().Status == AssemblyLoadStatus.AlreadyLoaded) // mod assembly is last in dependency order
            {
                throw new SAssemblyLoadFailedException($"Could not load '{assemblyPath}' because it was already loaded. Do you have two copies of this mod?");
            }

            // rewrite & load assemblies in leaf-to-root order
            bool             oneAssembly    = assemblies.Length == 1;
            Assembly         lastAssembly   = null;
            HashSet <string> loggedMessages = new HashSet <string>();

            foreach (AssemblyParseResult assembly in assemblies)
            {
                if (assembly.Status == AssemblyLoadStatus.AlreadyLoaded)
                {
                    continue;
                }

                // rewrite assembly
                bool changed = this.RewriteAssembly(mod, assembly.Definition, assumeCompatible, loggedMessages, logPrefix: "      ");

                // detect broken assembly reference
                foreach (AssemblyNameReference reference in assembly.Definition.MainModule.AssemblyReferences)
                {
                    if (!reference.Name.StartsWith("System.") && !this.IsAssemblyLoaded(reference))
                    {
                        this.Monitor.LogOnce(loggedMessages, $"      Broken code in {assembly.File.Name}: reference to missing assembly '{reference.FullName}'.");
                        if (!assumeCompatible)
                        {
                            throw new IncompatibleInstructionException($"assembly reference to {reference.FullName}", $"Found a reference to missing assembly '{reference.FullName}' while loading assembly {assembly.File.Name}.");
                        }
                        mod.SetWarning(ModWarning.BrokenCodeLoaded);
                        break;
                    }
                }

                // load assembly
                if (changed)
                {
                    if (!oneAssembly)
                    {
                        this.Monitor.Log($"      Loading {assembly.File.Name} (rewritten in memory)...", LogLevel.Trace);
                    }
                    using (MemoryStream outStream = new MemoryStream())
                    {
                        assembly.Definition.Write(outStream);
                        byte[] bytes = outStream.ToArray();
                        lastAssembly = Assembly.Load(bytes);
                    }
                }
                else
                {
                    if (!oneAssembly)
                    {
                        this.Monitor.Log($"      Loading {assembly.File.Name}...", LogLevel.Trace);
                    }
                    lastAssembly = Assembly.UnsafeLoadFrom(assembly.File.FullName);
                }

                // track loaded assembly for definition resolution
                this.AssemblyDefinitionResolver.Add(assembly.Definition);
            }

            // last assembly loaded is the root
            return(lastAssembly);
        }
示例#4
0
        /// <summary>Process the result from an instruction handler.</summary>
        /// <param name="mod">The mod being analyzed.</param>
        /// <param name="handler">The instruction handler.</param>
        /// <param name="result">The result returned by the handler.</param>
        /// <param name="loggedMessages">The messages already logged for the current mod.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <param name="filename">The assembly filename for log messages.</param>
        private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet <string> loggedMessages, string logPrefix, string filename)
        {
            // get message template
            // ($phrase is replaced with the noun phrase or messages)
            string template = null;

            switch (result)
            {
            case InstructionHandleResult.Rewritten:
                template = $"{logPrefix}Rewrote {filename} to fix $phrase...";
                break;

            case InstructionHandleResult.NotCompatible:
                template = $"{logPrefix}Broken code in {filename}: $phrase.";
                mod.SetWarning(ModWarning.BrokenCodeLoaded);
                break;

            case InstructionHandleResult.DetectedGamePatch:
                template = $"{logPrefix}Detected game patcher ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.PatchesGame);
                break;

            case InstructionHandleResult.DetectedSaveSerializer:
                template = $"{logPrefix}Detected possible save serializer change ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.ChangesSaveSerializer);
                break;

            case InstructionHandleResult.DetectedUnvalidatedUpdateTick:
                template = $"{logPrefix}Detected reference to $phrase in assembly {filename}.";
                mod.SetWarning(ModWarning.UsesUnvalidatedUpdateTick);
                break;

            case InstructionHandleResult.DetectedDynamic:
                template = $"{logPrefix}Detected 'dynamic' keyword ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.UsesDynamic);
                break;

            case InstructionHandleResult.DetectedConsoleAccess:
                template = $"{logPrefix}Detected direct console access ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.AccessesConsole);
                break;

            case InstructionHandleResult.DetectedFilesystemAccess:
                template = $"{logPrefix}Detected filesystem access ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.AccessesFilesystem);
                break;

            case InstructionHandleResult.DetectedShellAccess:
                template = $"{logPrefix}Detected shell or process access ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.AccessesShell);
                break;

            case InstructionHandleResult.None:
                break;

            default:
                throw new NotSupportedException($"Unrecognized instruction handler result '{result}'.");
            }
            if (template == null)
            {
                return;
            }

            // format messages
            if (handler.Phrases.Any())
            {
                foreach (string message in handler.Phrases)
                {
                    this.Monitor.LogOnce(loggedMessages, template.Replace("$phrase", message));
                }
            }
            else
            {
                this.Monitor.LogOnce(loggedMessages, template.Replace("$phrase", handler.DefaultPhrase ?? handler.GetType().Name));
            }
        }