using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Collections.Generic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ModBlocker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0+e561f363516889d3d83473733fa8d9e307d7197d")] [assembly: AssemblyProduct("ModBlocker")] [assembly: AssemblyTitle("ModBlocker")] [assembly: AssemblyVersion("1.3.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ModBlocker { public static class Patcher { private class Unit { public string Name; public List Files = new List(); public bool Blocked; public List AssemblyNames = new List(); public List PluginGuids = new List(); public List ReferencedAssemblies = new List(); public List HardDependencyGuids = new List(); } private static readonly string BepInExRoot = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(Patcher).Assembly.Location) ?? ".", "..")); private static readonly string LogFile = Path.Combine(BepInExRoot, "ModBlocker.log"); private static readonly string[] Protected = new string[2] { "modblocker.dll", "modblockerui.dll" }; public static IEnumerable TargetDLLs { get { yield break; } } public static void Patch(AssemblyDefinition assembly) { } public static void Initialize() { try { File.WriteAllText(LogFile, ""); Run(); } catch (Exception ex) { Log("Fatal error: " + ex); } } private static void Run() { List list = LoadBlocklist(Path.Combine(BepInExRoot, "config", "modblocker.cfg")); string path = Path.Combine(BepInExRoot, "config", "modblocker.server"); string[] array; if (File.Exists(path)) { array = File.ReadAllText(path).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = Normalize(array[i]); if (text.Length > 0 && !list.Contains(text)) { list.Add(text); } } } Log("Blocklist (" + list.Count + "): " + string.Join(", ", list.ToArray())); string text2 = Path.Combine(BepInExRoot, "plugins"); if (!Directory.Exists(text2)) { Log("Plugins folder not found: " + text2); return; } List list2 = new List(); array = Directory.GetDirectories(text2); foreach (string path2 in array) { Unit unit = new Unit { Name = Path.GetFileName(path2) }; unit.Files.AddRange(Directory.GetFiles(path2, "*", SearchOption.AllDirectories)); unit.Blocked = list.Contains(Normalize(unit.Name)) || AnyDllListed(unit, list); list2.Add(unit); } array = Directory.GetFiles(text2); foreach (string text3 in array) { Unit unit2 = new Unit { Name = Path.GetFileName(text3) }; unit2.Files.Add(text3); unit2.Blocked = AnyDllListed(unit2, list); list2.Add(unit2); } foreach (Unit item in list2) { Analyze(item); } foreach (Unit item2 in list2) { if (item2.Blocked && IsCoreLibrary(item2)) { item2.Blocked = false; Log("Refused: " + item2.Name + " is a protected core library (BepInEx/Jotunn)."); } } bool flag = true; while (flag) { flag = false; foreach (Unit item3 in list2) { if (item3.Blocked || IsCoreLibrary(item3)) { continue; } foreach (Unit item4 in list2) { if (item4.Blocked && item4 != item3) { string text4 = Needs(item3, item4); if (text4 != null) { item3.Blocked = true; flag = true; Log("Also blocked: " + item3.Name + " (depends on " + item4.Name + ", " + text4 + ")"); break; } } } } } foreach (Unit item5 in list2) { foreach (string file in item5.Files) { Apply(file, item5.Blocked); } } } private static string Needs(Unit kept, Unit blocked) { foreach (string referencedAssembly in kept.ReferencedAssemblies) { foreach (string assemblyName in blocked.AssemblyNames) { if (referencedAssembly.Equals(assemblyName, StringComparison.OrdinalIgnoreCase)) { return "assembly reference " + assemblyName; } } } foreach (string hardDependencyGuid in kept.HardDependencyGuids) { foreach (string pluginGuid in blocked.PluginGuids) { if (hardDependencyGuid.Equals(pluginGuid, StringComparison.OrdinalIgnoreCase)) { return "hard BepInDependency " + pluginGuid; } } } return null; } private static bool IsCoreLibrary(Unit u) { if (Normalize(u.Name).Contains("jotunn")) { return true; } foreach (string assemblyName in u.AssemblyNames) { if (assemblyName.Equals("Jotunn", StringComparison.OrdinalIgnoreCase) || assemblyName.StartsWith("BepInEx", StringComparison.OrdinalIgnoreCase)) { return true; } } foreach (string pluginGuid in u.PluginGuids) { if (pluginGuid.Equals("com.jotunn.jotunn", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static void Analyze(Unit u) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) foreach (string file in u.Files) { if (!file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !file.EndsWith(".dll.blocked", StringComparison.OrdinalIgnoreCase)) { continue; } try { ModuleDefinition val = ModuleDefinition.ReadModule(file); try { if (val.Assembly != null) { u.AssemblyNames.Add(((AssemblyNameReference)val.Assembly.Name).Name); } Enumerator enumerator2 = val.AssemblyReferences.GetEnumerator(); try { while (enumerator2.MoveNext()) { AssemblyNameReference current2 = enumerator2.Current; u.ReferencedAssemblies.Add(current2.Name); } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } Enumerator enumerator3 = val.Types.GetEnumerator(); try { while (enumerator3.MoveNext()) { TypeDefinition current3 = enumerator3.Current; if (!current3.HasCustomAttributes) { continue; } Enumerator enumerator4 = current3.CustomAttributes.GetEnumerator(); try { while (enumerator4.MoveNext()) { CustomAttribute current4 = enumerator4.Current; string fullName = ((MemberReference)current4.AttributeType).FullName; CustomAttributeArgument val2; if (fullName == "BepInEx.BepInPlugin" && current4.ConstructorArguments.Count > 0) { List pluginGuids = u.PluginGuids; val2 = current4.ConstructorArguments[0]; pluginGuids.Add((((CustomAttributeArgument)(ref val2)).Value as string) ?? ""); } else { if (!(fullName == "BepInEx.BepInDependency") || current4.ConstructorArguments.Count <= 0) { continue; } bool flag = true; if (current4.ConstructorArguments.Count > 1) { val2 = current4.ConstructorArguments[1]; if (((CustomAttributeArgument)(ref val2)).Value is int) { val2 = current4.ConstructorArguments[1]; flag = ((int)((CustomAttributeArgument)(ref val2)).Value & 1) != 0; } } if (flag) { List hardDependencyGuids = u.HardDependencyGuids; val2 = current4.ConstructorArguments[0]; hardDependencyGuids.Add((((CustomAttributeArgument)(ref val2)).Value as string) ?? ""); } } } } finally { ((IDisposable)enumerator4/*cast due to .constrained prefix*/).Dispose(); } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)val)?.Dispose(); } } catch { } } } private static bool AnyDllListed(Unit u, List blocklist) { foreach (string file in u.Files) { if (IsDllMatch(file, blocklist)) { return true; } } return false; } private static string Normalize(string s) { return (s ?? "").Trim().ToLowerInvariant(); } private static bool IsProtected(string file) { string text = Path.GetFileName(file).ToLowerInvariant(); if (text.EndsWith(".blocked")) { text = text.Substring(0, text.Length - ".blocked".Length); } string[] array = Protected; foreach (string text2 in array) { if (text == text2) { return true; } } return false; } private static void Apply(string file, bool block) { if (block && IsProtected(file)) { Log("Ignored: " + Path.GetFileName(file) + " (ModBlocker cannot block itself)"); block = false; } if (block && file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) { TryMove(file, file + ".blocked", "Blocked"); } else if (!block && file.EndsWith(".dll.blocked", StringComparison.OrdinalIgnoreCase)) { string text = file.Substring(0, file.Length - ".blocked".Length); if (!File.Exists(text)) { TryMove(file, text, "Re-enabled"); } } } private static bool IsDllMatch(string file, List blocklist) { string text = Path.GetFileName(file); if (text.EndsWith(".blocked", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - ".blocked".Length); } return blocklist.Contains(Normalize(text)); } private static List LoadBlocklist(string path) { List list = new List(); if (!File.Exists(path)) { File.WriteAllLines(path, new string[11] { "## Settings file was created by plugin ModBlocker", "## Plugin GUID: modblocker", "", "[Blocklist]", "", "## Comma-separated list of mods to block at the NEXT launch.", "## Mod manager folder names (Author-ModName) or DLL file names (SomePlugin.dll). Case-insensitive.", "## Example: SomeAuthor-SomeMod, SomeOldPlugin.dll", "# Setting type: String", "# Default value: ", "Mods = " }); return list; } string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text.StartsWith("#") || text.StartsWith(";") || text.StartsWith("[")) { continue; } if (text.IndexOf('=') >= 0) { if (!text.Substring(0, text.IndexOf('=')).Trim().Equals("Mods", StringComparison.OrdinalIgnoreCase)) { continue; } string[] array2 = text.Substring(text.IndexOf('=') + 1).Split(new char[1] { ',' }); for (int j = 0; j < array2.Length; j++) { string text2 = Normalize(array2[j]); if (text2.Length > 0 && !list.Contains(text2)) { list.Add(text2); } } } else { string text3 = Normalize(text); if (text3.Length > 0 && !list.Contains(text3)) { list.Add(text3); } } } return list; } private static void TryMove(string from, string to, string action) { try { File.Move(from, to); Log(action + ": " + Path.GetFileName(to)); } catch (Exception ex) { Log("Could not rename " + from + ": " + ex.Message); } } private static void Log(string msg) { string text = "[ModBlocker] " + msg; Console.WriteLine(text); try { File.AppendAllText(LogFile, text + Environment.NewLine); } catch { } } } }