Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ModBlame v1.0.0
ModBlame.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.RuntimeDetour; using MonoMod.Utils; using RiskOfOptions; using RiskOfOptions.Options; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Chinchi")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Find out which mods are doing cursed stuff.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+065423eb8b2e82ee71315a7836a5f31391b01331")] [assembly: AssemblyProduct("ModBlame")] [assembly: AssemblyTitle("ModBlame")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/schinchi/ModBlame")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] [CompilerGenerated] internal delegate void <>f__AnonymousDelegate0<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, params T4[] arg4); [CompilerGenerated] internal delegate void <>f__AnonymousDelegate1<T1, T2, T3>(T1 arg1, T2 arg2, params T3[] arg3); 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 ModBlame { internal class HookNoOrig { private delegate void PatchInfoAddPrefixesDelegate(PatchInfo patchInfo, string owner, params HarmonyMethod[] methods); private delegate void HookConstructorDelegate(Hook hook, MethodBase from, MethodInfo to, object target, ref HookConfig config); [CompilerGenerated] private static class <>O { public static EventHandler<SettingChangedEventArgs> <0>__ParseSettings; public static <>f__AnonymousDelegate0<PatchInfoAddPrefixesDelegate, PatchInfo, string, HarmonyMethod> <1>__InspectHarmonyPrefixForReturnFalse; public static <>A{00008000}<HookConstructorDelegate, Hook, MethodBase, MethodInfo, object, HookConfig> <2>__InspectHookForNoOrigInvoke; public static Manipulator <3>__ScanForReturnFalse; public static Manipulator <4>__ScanForNoOrigInvoke; } private static List<Regex> whitelist = new List<Regex>(); private static string origName = null; internal static void Init() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) PluginConfig.file.SettingChanged += ParseSettings; ParseSettings(null, null); new Hook((MethodBase)typeof(PatchInfo).GetMethod("AddPrefixes", (BindingFlags)(-1)), (Delegate)new <>f__AnonymousDelegate0<PatchInfoAddPrefixesDelegate, PatchInfo, string, HarmonyMethod>(InspectHarmonyPrefixForReturnFalse)); new Hook((MethodBase)typeof(Hook).GetConstructor(new Type[4] { typeof(MethodBase), typeof(MethodInfo), typeof(object), typeof(HookConfig).MakeByRefType() }), (Delegate)new <>A{00008000}<HookConstructorDelegate, Hook, MethodBase, MethodInfo, object, HookConfig>(InspectHookForNoOrigInvoke)); } private static void ParseSettings(object sender, SettingChangedEventArgs e) { whitelist = (from p in Utils.SplitText(PluginConfig.noOrigWhitelist.Value) select new Regex(p)).ToList(); } private static void InspectHarmonyPrefixForReturnFalse(PatchInfoAddPrefixesDelegate orig, PatchInfo self, string owner, params HarmonyMethod[] methods) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown orig(self, owner, methods); for (int i = 0; i < methods.Length; i++) { MethodInfo methodInfo = methods[i]?.method; if (!(methodInfo != null) || !(methodInfo.ReturnType == typeof(bool))) { continue; } string text = $"{methodInfo.DeclaringType}.{methodInfo.Name}"; if (!PluginConfig.noOrigEnabled.Value || Utils.MatchPattern(text, whitelist)) { continue; } ILHook val = null; try { object obj = <>O.<3>__ScanForReturnFalse; if (obj == null) { Manipulator val2 = ScanForReturnFalse; <>O.<3>__ScanForReturnFalse = val2; obj = (object)val2; } val = new ILHook((MethodBase)methodInfo, (Manipulator)obj, new ILHookConfig { ManualApply = true }); val.Apply(); } catch (Exception arg) { Log.Error($"Failed to investigate {text} for Harmony prefix\n{arg}"); } finally { if (val != null) { val.Dispose(); } } } } private static void ScanForReturnFalse(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (new ILCursor(il).TryGotoNext(new Func<Instruction, bool>[2] { (Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 0), (Instruction x) => ILPatternMatchingExt.MatchRet(x) })) { string text = ((MemberReference)il.Method).Name.Replace("::", "."); Log.Warning("HarmonyPrefix returns false from " + text); } } private static void InspectHookForNoOrigInvoke(HookConstructorDelegate orig, Hook self, MethodBase method, MethodInfo to, object target, ref HookConfig hookConfig) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown orig(self, method, to, target, ref hookConfig); string text = $"{to.DeclaringType}.{to.Name}"; if (!PluginConfig.noOrigEnabled.Value || Utils.MatchPattern(text, whitelist)) { return; } ILHook val = null; try { MethodInfo methodInfo = ((to.ReturnType == typeof(IEnumerator) || (to.ReturnType.IsGenericType && to.ReturnType.GetGenericTypeDefinition() == typeof(IEnumerator<>))) ? Extensions.GetStateMachineTarget(to) : to); origName = "NULL"; ParameterInfo[] parameters = to.GetParameters(); foreach (ParameterInfo parameterInfo in parameters) { if (typeof(Delegate).IsAssignableFrom(parameterInfo.ParameterType)) { origName = parameterInfo.ParameterType.Name; } } object obj = <>O.<4>__ScanForNoOrigInvoke; if (obj == null) { Manipulator val2 = ScanForNoOrigInvoke; <>O.<4>__ScanForNoOrigInvoke = val2; obj = (object)val2; } val = new ILHook((MethodBase)methodInfo, (Manipulator)obj, new ILHookConfig { ManualApply = true }); val.Apply(); } catch (Exception arg) { Log.Error($"Failed to investigate {text} for not calling orig\n{arg}"); } finally { if (val != null) { val.Dispose(); } } } private static void ScanForNoOrigInvoke(ILContext il) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown ILCursor val = new ILCursor(il); MethodReference methodReference = null; while (val.TryGotoNext(new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt(x, ref methodReference) })) { if (((MemberReference)methodReference).FullName.Contains(origName)) { return; } } string text = ((MemberReference)il.Method).Name.Replace("::", "."); Log.Warning("Orig skipped by " + text); } } internal class ILCursorRemove { private static readonly HashSet<string> removeEvents = new HashSet<string>(); private static readonly HashSet<string> removeRangeEvents = new HashSet<string>(); private static List<Regex> whitelist = new List<Regex>(); internal static void Init() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) PluginConfig.file.SettingChanged += ParseSettings; ParseSettings(null, null); new Hook((MethodBase)typeof(ILCursor).GetMethod("Remove"), (Delegate)new Func<Func<ILCursor, ILCursor>, ILCursor, ILCursor>(LogILCursorRemove)); new Hook((MethodBase)typeof(ILCursor).GetMethod("RemoveRange"), (Delegate)new Func<Func<ILCursor, int, ILCursor>, ILCursor, int, ILCursor>(LogILCursorRemoveRange)); } private static void ParseSettings(object sender, SettingChangedEventArgs e) { whitelist = (from p in Utils.SplitText(PluginConfig.cursorRemoveWhitelist.Value) select new Regex(p)).ToList(); } private static ILCursor LogILCursorRemove(Func<ILCursor, ILCursor> orig, ILCursor self) { if (PluginConfig.cursorRemoveEnabled.Value) { string text = ExtractCaller(); if (!removeEvents.Contains(text) && !Utils.MatchPattern(text, whitelist)) { Log.Warning("ILCursor.Remove called by " + text); removeEvents.Add(text); } } return orig(self); } private static ILCursor LogILCursorRemoveRange(Func<ILCursor, int, ILCursor> orig, ILCursor self, int num) { if (PluginConfig.cursorRemoveEnabled.Value) { string text = ExtractCaller(); if (!removeRangeEvents.Contains(text) && !Utils.MatchPattern(text, whitelist)) { Log.Warning("ILCursor.RemoveRange called by " + text); removeRangeEvents.Add(text); } } return orig(self, num); } private static string ExtractCaller() { StackTrace stackTrace = new StackTrace(); StackFrame[] frames = stackTrace.GetFrames(); for (int i = 0; i < frames.Length; i++) { MethodBase method = frames[i].GetMethod(); ParameterInfo[] parameters = method.GetParameters(); for (int j = 0; j < parameters.Length; j++) { if (parameters[j].ParameterType == typeof(ILContext)) { return $"{method.DeclaringType}.{method.Name}"; } } } return stackTrace.ToString(); } } internal static class Log { private static ManualLogSource _logSource; internal static void Init(ManualLogSource logSource) { _logSource = logSource; } internal static void Debug(object data) { _logSource.LogDebug(data); } internal static void Error(object data) { _logSource.LogError(data); } internal static void Fatal(object data) { _logSource.LogFatal(data); } internal static void Info(object data) { _logSource.LogInfo(data); } internal static void Message(object data) { _logSource.LogMessage(data); } internal static void Warning(object data) { _logSource.LogWarning(data); } } internal class LogSpam { private delegate void LogDelegate_string_params(string text, params object[] args); private delegate void LogDelegate_Object_string_params(Object context, string text, params object[] args); private static List<Regex> blacklist = new List<Regex>(); internal static void Init() { PluginConfig.file.SettingChanged += ParseSettings; ParseSettings(null, null); ApplyHooks(new Type[1] { typeof(object) }, new Action<Action<object>, object>(CatchLog_object)); ApplyHooks(new Type[2] { typeof(object), typeof(Object) }, new Action<Action<object, Object>, object, Object>(CatchLog_object_Object)); ApplyHooks(new Type[2] { typeof(string), typeof(object[]) }, new <>f__AnonymousDelegate1<LogDelegate_string_params, string, object>(CatchLog_string_params), isFormat: true); ApplyHooks(new Type[3] { typeof(Object), typeof(string), typeof(object[]) }, new <>f__AnonymousDelegate0<LogDelegate_Object_string_params, Object, string, object>(CatchLog_Object_string_params), isFormat: true); static void ApplyHooks(Type[] arguments, Delegate delegateMethod, bool isFormat = false) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) string text = (isFormat ? "Format" : ""); new Hook((MethodBase)typeof(Debug).GetMethod("Log" + text, arguments), delegateMethod); new Hook((MethodBase)typeof(Debug).GetMethod("LogWarning" + text, arguments), delegateMethod); new Hook((MethodBase)typeof(Debug).GetMethod("LogError" + text, arguments), delegateMethod); } } private static void ParseSettings(object sender, SettingChangedEventArgs e) { blacklist = (from p in Utils.SplitText(PluginConfig.logSpamText.Value) select new Regex(p)).ToList(); } private static void CatchLog_object(Action<object> orig, object message) { CheckForSpam(message); orig(message); } private static void CatchLog_object_Object(Action<object, Object> orig, object message, Object context) { CheckForSpam(message); orig(message, context); } private static void CatchLog_string_params(LogDelegate_string_params orig, string text, params object[] args) { CheckForSpam(string.Format(text, args)); orig(text, args); } private static void CatchLog_Object_string_params(LogDelegate_Object_string_params orig, Object context, string text, params object[] args) { CheckForSpam(string.Format(text, args)); orig(context, text, args); } private static void CheckForSpam(object message) { if (PluginConfig.logSpamEnabled.Value && Utils.MatchPattern(message.ToString(), blacklist)) { Log.Warning($"Blacklisted text \"{message}\" encountered:\n{new StackTrace()}"); } } } [BepInPlugin("___Chinchi.ModBlame", "ModBlame", "1.0.0")] public class ModBlame : BaseUnityPlugin { public const string PluginGUID = "___Chinchi.ModBlame"; public const string PluginAuthor = "___Chinchi"; public const string PluginName = "ModBlame"; public const string PluginVersion = "1.0.0"; private void Awake() { Log.Init(((BaseUnityPlugin)this).Logger); PluginConfig.Init(((BaseUnityPlugin)this).Config); HookNoOrig.Init(); ILCursorRemove.Init(); LogSpam.Init(); } private void Start() { if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions")) { PluginConfig.RiskOfOptionsInit(((BaseUnityPlugin)this).Info.Location); } } } internal class PluginConfig { internal static ConfigFile file { get; private set; } internal static ConfigEntry<bool> noOrigEnabled { get; private set; } internal static ConfigEntry<string> noOrigWhitelist { get; private set; } internal static ConfigEntry<bool> cursorRemoveEnabled { get; private set; } internal static ConfigEntry<string> cursorRemoveWhitelist { get; private set; } internal static ConfigEntry<bool> logSpamEnabled { get; private set; } internal static ConfigEntry<string> logSpamText { get; private set; } internal static void Init(ConfigFile config) { file = config; noOrigEnabled = config.Bind<bool>("Hook No Orig", "Enable", true, "Whether hooks without calling orig will be reported."); noOrigWhitelist = config.Bind<string>("Hook No Orig", "Whitelist", "RoR2BepInExPack,MiscFixes,R2API.SkinIDRS.SetCustomIDRS,MultiplayerModTesting.Empty", "Comma-separated namespace/method name partials to be ignored. Regex matched."); cursorRemoveEnabled = config.Bind<bool>("ILCursor Remove", "Enable", true, "Whether hooks that use ILCursor.Remove and ILCursor.RemoveRange will be reported."); cursorRemoveWhitelist = config.Bind<string>("ILCursor Remove", "Whitelist", "RoR2BepInExPack,MiscFixes", "Comma-separated namespace/method name partials to be ignored. Regex matched."); logSpamEnabled = config.Bind<bool>("Log Spam", "Enable", true, "Whether blacklisted text will log the caller origin."); logSpamText = config.Bind<string>("Log Spam", "Text", "", "Comma-separated text to check for. Regex matched."); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static void RiskOfOptionsInit(string assemblyLocation) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) FileInfo fileInfo = null; FileInfo[] files = new DirectoryInfo(Path.GetDirectoryName(assemblyLocation)).GetFiles("icon.png", SearchOption.TopDirectoryOnly); if (files != null && files.Length != 0) { fileInfo = files[0]; } if (fileInfo != null) { string name = "ModBlameIcon"; Texture2D val = new Texture2D(256, 256); ((Object)val).name = name; if (ImageConversion.LoadImage(val, File.ReadAllBytes(fileInfo.FullName))) { Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); ((Object)obj).name = name; ModSettingsManager.SetModIcon(obj, "___Chinchi.ModBlame", "ModBlame"); } } ModSettingsManager.SetModDescription("Find out which mods might be doing cursed stuff."); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(noOrigEnabled)); ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(noOrigWhitelist)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(cursorRemoveEnabled)); ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(cursorRemoveWhitelist)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(logSpamEnabled)); ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(logSpamText)); } } internal class Utils { internal static bool MatchPattern(string text, List<Regex> patterns) { return patterns.Any((Regex p) => p.IsMatch(text)); } internal static string[] SplitText(string text, char separator = ',') { if (string.IsNullOrEmpty(text)) { return Array.Empty<string>(); } List<string> list = new List<string>(); string[] array = text.Split(separator); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2)) { list.Add(text2.Trim()); } } return list.ToArray(); } } }