Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of MLVScan v1.5.3
Plugins/MLVScan.MelonLoader.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using MLVScan; using MLVScan.Models; using MLVScan.Services; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "MLVScan", "1.5.3", "Bars", null)] [assembly: MelonPriority(int.MinValue)] [assembly: MelonColor(255, 139, 0, 0)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("MLVScan.MelonLoader")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+ed0d0ca873e6deac78fcda7799234dde5c5e9ca8")] [assembly: AssemblyProduct("MLVScan.MelonLoader")] [assembly: AssemblyTitle("MLVScan.MelonLoader")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 MLVScan { public class Core : MelonPlugin { private ServiceFactory _serviceFactory; private ConfigManager _configManager; private ModScanner _modScanner; private ModDisabler _modDisabler; private bool _initialized = false; private static readonly string[] DefaultWhitelistedMods = new string[5] { "MLVScan.dll", "MLVScan.MelonLoader.dll", "CustomTV.dll", "CustomTV_Mono.dll", "CustomTV_IL2CPP.dll" }; public override void OnEarlyInitializeMelon() { try { ((MelonBase)this).LoggerInstance.Msg("Pre-scanning for malicious mods..."); _serviceFactory = new ServiceFactory(((MelonBase)this).LoggerInstance); _configManager = _serviceFactory.CreateConfigManager(); InitializeDefaultWhitelist(); _modScanner = _serviceFactory.CreateModScanner(); _modDisabler = _serviceFactory.CreateModDisabler(); _initialized = true; ScanAndDisableMods(force: true); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("Error in pre-mod scan: " + ex.Message); ((MelonBase)this).LoggerInstance.Error(ex.StackTrace); } } public override void OnInitializeMelon() { try { ((MelonBase)this).LoggerInstance.Msg("MLVScan initialization complete"); if (_configManager.Config.WhitelistedMods.Length != 0) { ((MelonBase)this).LoggerInstance.Msg($"{_configManager.Config.WhitelistedMods.Length} mod(s) are whitelisted and won't be scanned"); ((MelonBase)this).LoggerInstance.Msg("To manage whitelisted mods, edit MelonPreferences.cfg"); } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("Error initializing MLVScan: " + ex.Message); ((MelonBase)this).LoggerInstance.Error(ex.StackTrace); } } private void InitializeDefaultWhitelist() { if (_configManager != null) { string[] whitelistedMods = _configManager.GetWhitelistedMods(); if (whitelistedMods.Length == 0) { ((MelonBase)this).LoggerInstance.Msg("Initializing default whitelist"); _configManager.SetWhitelistedMods(DefaultWhitelistedMods); } } } public Dictionary<string, List<ScanFinding>> ScanAndDisableMods(bool force = false) { try { if (!_initialized) { ((MelonBase)this).LoggerInstance.Error("Cannot scan mods - MLVScan not properly initialized"); return new Dictionary<string, List<ScanFinding>>(); } ((MelonBase)this).LoggerInstance.Msg("Scanning for suspicious mods..."); Dictionary<string, List<ScanFinding>> source = _modScanner.ScanAllMods(force); Dictionary<string, List<ScanFinding>> dictionary = source.Where((KeyValuePair<string, List<ScanFinding>> kv) => kv.Value.Count > 0 && kv.Value.Any((ScanFinding f) => f.Location != "Assembly scanning")).ToDictionary((KeyValuePair<string, List<ScanFinding>> kv) => kv.Key, (KeyValuePair<string, List<ScanFinding>> kv) => kv.Value); if (dictionary.Count > 0) { ((MelonBase)this).LoggerInstance.Warning($"Found {dictionary.Count} potentially malicious mods!"); List<string> list = _modDisabler.DisableSuspiciousMods(dictionary, force); int count = list.Count; ((MelonBase)this).LoggerInstance.Msg($"Disabled {count} suspicious mods"); if (count <= 0) { return dictionary; } GenerateDetailedReports(list, dictionary); ((MelonBase)this).LoggerInstance.Msg("To whitelist any false positives, add them to the MLVScan → WhitelistedMods setting in MelonPreferences.cfg"); } else { ((MelonBase)this).LoggerInstance.Msg("No suspicious mods found"); } return dictionary; } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("Error scanning mods: " + ex.Message); return new Dictionary<string, List<ScanFinding>>(); } } private void GenerateDetailedReports(List<string> disabledMods, Dictionary<string, List<ScanFinding>> scanResults) { ((MelonBase)this).LoggerInstance.Warning("======= DETAILED SCAN REPORT ======="); PromptGeneratorService promptGeneratorService = _serviceFactory.CreatePromptGeneratorService(); string text = Path.Combine(MelonEnvironment.UserDataDirectory, "MLVScan", "Prompts"); foreach (string disabledMod in disabledMods) { string fileName = Path.GetFileName(disabledMod); ((MelonBase)this).LoggerInstance.Warning("SUSPICIOUS MOD: " + fileName); ((MelonBase)this).LoggerInstance.Msg("-------------------------------"); if (scanResults.TryGetValue(disabledMod, out var value)) { List<ScanFinding> list = value.Where((ScanFinding f) => f.Location != "Assembly scanning").ToList(); if (list.Count == 0) { ((MelonBase)this).LoggerInstance.Msg("No specific suspicious patterns were identified."); continue; } Dictionary<string, List<ScanFinding>> dictionary = (from f in list group f by f.Description).ToDictionary((IGrouping<string, ScanFinding> g) => g.Key, (IGrouping<string, ScanFinding> g) => g.ToList()); ((MelonBase)this).LoggerInstance.Warning($"Total suspicious patterns found: {list.Count}"); Dictionary<string, int> dictionary2 = (from f in list group f by f.Severity into g orderby GetSeverityRank(g.Key) descending select g).ToDictionary((IGrouping<string, ScanFinding> g) => g.Key, (IGrouping<string, ScanFinding> g) => g.Count()); ((MelonBase)this).LoggerInstance.Warning("Severity breakdown:"); foreach (KeyValuePair<string, int> item in dictionary2) { string arg = FormatSeverityLabel(item.Key); ((MelonBase)this).LoggerInstance.Msg($" {arg}: {item.Value} issue(s)"); } ((MelonBase)this).LoggerInstance.Msg("-------------------------------"); ((MelonBase)this).LoggerInstance.Warning("Suspicious patterns found:"); foreach (KeyValuePair<string, List<ScanFinding>> item2 in dictionary) { item2.Deconstruct(out var key, out var value2); string arg2 = key; List<ScanFinding> list2 = value2; string arg3 = FormatSeverityLabel(list2[0].Severity); ((MelonBase)this).LoggerInstance.Warning($"[{arg3}] {arg2} ({list2.Count} instances)"); int num = Math.Min(list2.Count, 3); for (int i = 0; i < num; i++) { ScanFinding scanFinding = list2[i]; ((MelonBase)this).LoggerInstance.Msg(" * At: " + scanFinding.Location); if (!string.IsNullOrEmpty(scanFinding.CodeSnippet)) { ((MelonBase)this).LoggerInstance.Msg(" Code Snippet (IL):"); string[] array = scanFinding.CodeSnippet.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { ((MelonBase)this).LoggerInstance.Msg(" " + text2); } } } if (list2.Count > num) { ((MelonBase)this).LoggerInstance.Msg($" * And {list2.Count - num} more instances..."); } ((MelonBase)this).LoggerInstance.Msg(""); } ((MelonBase)this).LoggerInstance.Msg("-------------------------------"); DisplaySecurityNotice(fileName); try { string text3 = Path.Combine(MelonEnvironment.UserDataDirectory, "MLVScan", "Reports"); Directory.CreateDirectory(text3); string text4 = Path.Combine(text3, fileName + ".report.txt"); using (StreamWriter streamWriter = new StreamWriter(text4)) { streamWriter.WriteLine("MLVScan Detailed Report for " + fileName); streamWriter.WriteLine($"Scan Date: {DateTime.Now}"); streamWriter.WriteLine($"Total Suspicious Patterns: {list.Count}"); streamWriter.WriteLine("=============================================="); streamWriter.WriteLine("\nSEVERITY BREAKDOWN:"); foreach (KeyValuePair<string, int> item3 in dictionary2) { streamWriter.WriteLine($"- {item3.Key}: {item3.Value} issue(s)"); } streamWriter.WriteLine("=============================================="); foreach (KeyValuePair<string, List<ScanFinding>> item4 in dictionary) { streamWriter.WriteLine("\n== " + item4.Key + " =="); streamWriter.WriteLine("Severity: " + item4.Value[0].Severity); streamWriter.WriteLine($"Instances: {item4.Value.Count}"); streamWriter.WriteLine("\nLocations & Snippets:"); foreach (ScanFinding item5 in item4.Value) { streamWriter.WriteLine("- " + item5.Location); if (!string.IsNullOrEmpty(item5.CodeSnippet)) { streamWriter.WriteLine(" Code Snippet (IL):"); string[] array2 = item5.CodeSnippet.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text5 in array2) { streamWriter.WriteLine(" " + text5); } streamWriter.WriteLine(); } } } WriteSecurityNoticeToReport(streamWriter); } if (promptGeneratorService.SavePromptToFile(disabledMod, list, text)) { ((MelonBase)this).LoggerInstance.Msg("Detailed report saved to: " + text4); ((MelonBase)this).LoggerInstance.Msg("LLM analysis prompt saved to: " + Path.Combine(text, fileName + ".prompt.md")); ((MelonBase)this).LoggerInstance.Msg("You can copy the contents of the prompt file into ChatGPT to help determine if this is malware or a false positive, although don't trust ChatGPT to be 100% accurate."); } else { ((MelonBase)this).LoggerInstance.Msg("Detailed report saved to: " + text4); } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("Failed to save detailed report: " + ex.Message); } } ((MelonBase)this).LoggerInstance.Warning("-------------------------------"); } ((MelonBase)this).LoggerInstance.Warning("====== END OF SCAN REPORT ======"); } private static int GetSeverityRank(string severity) { string text = severity.ToLower(); if (1 == 0) { } int result = text switch { "critical" => 4, "high" => 3, "medium" => 2, "low" => 1, _ => 0, }; if (1 == 0) { } return result; } private static string FormatSeverityLabel(string severity) { string text = severity.ToLower(); if (1 == 0) { } string result = text switch { "critical" => "CRITICAL", "high" => "HIGH", "medium" => "MEDIUM", "low" => "LOW", _ => severity.ToUpper(), }; if (1 == 0) { } return result; } private void DisplaySecurityNotice(string modName) { ((MelonBase)this).LoggerInstance.Warning("IMPORTANT SECURITY NOTICE"); ((MelonBase)this).LoggerInstance.Msg("MLVScan has detected and disabled " + modName + " before it was loaded."); ((MelonBase)this).LoggerInstance.Msg("If this is your first time running the game with this mod, your PC is likely safe."); ((MelonBase)this).LoggerInstance.Msg("However, if you've previously run the game with this mod, your system MAY be infected."); ((MelonBase)this).LoggerInstance.Msg("Keep in mind that no detection system is perfect, and this mod may be falsely flagged."); ((MelonBase)this).LoggerInstance.Warning("Recommended security steps:"); ((MelonBase)this).LoggerInstance.Msg("1. Check with the modding community first - no detection is perfect"); ((MelonBase)this).LoggerInstance.Msg(" Join the modding Discord at: https://discord.gg/rV2QSAnqhX"); ((MelonBase)this).LoggerInstance.Msg(" Ask about this mod in the #MLVScan or #report-mods channels to confirm if it's actually malicious"); ((MelonBase)this).LoggerInstance.Msg("2. Run a full system scan with a trusted antivirus like Malwarebytes"); ((MelonBase)this).LoggerInstance.Msg(" Malwarebytes is recommended as a free and effective antivirus solution"); ((MelonBase)this).LoggerInstance.Msg("3. Use Microsoft Safety Scanner for a secondary scan"); ((MelonBase)this).LoggerInstance.Msg("4. Change important passwords if antivirus shows a threat"); ((MelonBase)this).LoggerInstance.Warning("Resources for malware removal:"); ((MelonBase)this).LoggerInstance.Msg("- Malwarebytes: https://www.malwarebytes.com/cybersecurity/basics/how-to-remove-virus-from-computer"); ((MelonBase)this).LoggerInstance.Msg("- Microsoft Safety Scanner: https://learn.microsoft.com/en-us/defender-endpoint/safety-scanner-download"); } private static void WriteSecurityNoticeToReport(StreamWriter writer) { writer.WriteLine("\n\n============== SECURITY NOTICE ==============\n"); writer.WriteLine("IMPORTANT: READ THIS SECURITY INFORMATION\n"); writer.WriteLine("MLVScan has detected and disabled this mod before it was loaded."); writer.WriteLine("This mod contains code patterns commonly associated with malware.\n"); writer.WriteLine("YOUR SYSTEM SECURITY STATUS:"); writer.WriteLine("- If this is your FIRST TIME starting the game with this mod installed:"); writer.WriteLine(" Your PC is likely SAFE as MLVScan prevented the mod from loading."); writer.WriteLine("\n- If you have PREVIOUSLY PLAYED the game with this mod loaded:"); writer.WriteLine(" Your system MAY BE INFECTED with malware. Take action immediately.\n"); writer.WriteLine("RECOMMENDED SECURITY STEPS:"); writer.WriteLine("1. Check with the modding community first - no detection system is perfect"); writer.WriteLine(" Join the modding Discord at: https://discord.gg/rV2QSAnqhX"); writer.WriteLine(" Ask about this mod in the #MLVScan or #report-mods channels to confirm if it's actually malicious"); writer.WriteLine("\n2. Run a full system scan with a reputable antivirus program"); writer.WriteLine(" Free option: Malwarebytes (https://www.malwarebytes.com/)"); writer.WriteLine(" Malwarebytes is recommended as a free and effective antivirus solution"); writer.WriteLine("\n3. Run Microsoft Safety Scanner as a secondary check"); writer.WriteLine(" Download: https://learn.microsoft.com/en-us/defender-endpoint/safety-scanner-download"); writer.WriteLine("\n4. Update all your software from official sources"); writer.WriteLine("\n5. Change passwords for important accounts (from a clean device if possible)"); writer.WriteLine("\n6. Monitor your accounts for any suspicious activity"); writer.WriteLine("\nDETAILED MALWARE REMOVAL GUIDES:"); writer.WriteLine("- Malwarebytes Guide: https://www.malwarebytes.com/cybersecurity/basics/how-to-remove-virus-from-computer"); writer.WriteLine("- Microsoft Safety Scanner: https://learn.microsoft.com/en-us/defender-endpoint/safety-scanner-download"); writer.WriteLine("\n============================================="); } } } namespace MLVScan.Services { public class AssemblyScanner { private readonly IEnumerable<IScanRule> _rules; private DefaultAssemblyResolver _assemblyResolver; public AssemblyScanner(IEnumerable<IScanRule> rules) { _rules = rules ?? throw new ArgumentNullException("rules"); InitializeResolver(); } private void InitializeResolver() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown _assemblyResolver = new DefaultAssemblyResolver(); string gameRootDirectory = MelonEnvironment.GameRootDirectory; string text = Path.Combine(gameRootDirectory, "MelonLoader"); ((BaseAssemblyResolver)_assemblyResolver).AddSearchDirectory(gameRootDirectory); if (Directory.Exists(text)) { ((BaseAssemblyResolver)_assemblyResolver).AddSearchDirectory(text); string text2 = Path.Combine(text, "Managed"); if (Directory.Exists(text2)) { ((BaseAssemblyResolver)_assemblyResolver).AddSearchDirectory(text2); } string text3 = Path.Combine(text, "Dependencies"); if (Directory.Exists(text3)) { ((BaseAssemblyResolver)_assemblyResolver).AddSearchDirectory(text3); string[] directories = Directory.GetDirectories(text3, "*", SearchOption.AllDirectories); foreach (string text4 in directories) { ((BaseAssemblyResolver)_assemblyResolver).AddSearchDirectory(text4); } } } string text5 = Path.Combine(gameRootDirectory, "Schedule I_Data", "Managed"); if (Directory.Exists(text5)) { ((BaseAssemblyResolver)_assemblyResolver).AddSearchDirectory(text5); } } public IEnumerable<ScanFinding> Scan(string assemblyPath) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(assemblyPath)) { throw new ArgumentException("Assembly path must be provided", "assemblyPath"); } if (!File.Exists(assemblyPath)) { throw new FileNotFoundException("Assembly file not found", assemblyPath); } List<ScanFinding> list = new List<ScanFinding>(); try { ReaderParameters val = new ReaderParameters { ReadWrite = false, InMemory = true, ReadSymbols = false, AssemblyResolver = (IAssemblyResolver)(object)_assemblyResolver }; AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(assemblyPath, val); Enumerator<ModuleDefinition> enumerator = val2.Modules.GetEnumerator(); try { while (enumerator.MoveNext()) { ModuleDefinition current = enumerator.Current; ScanForDllImports(current, list); Enumerator<TypeDefinition> enumerator2 = current.Types.GetEnumerator(); try { while (enumerator2.MoveNext()) { TypeDefinition current2 = enumerator2.Current; ScanType(current2, list); } } finally { ((IDisposable)enumerator2).Dispose(); } } } finally { ((IDisposable)enumerator).Dispose(); } } catch (Exception) { list.Add(new ScanFinding("Assembly scanning", "Warning: Some parts of the assembly could not be scanned. This doesn't necessarily mean the mod is malicious.", "Low")); } if (list.Count == 1 && list[0].Location == "Assembly scanning" && string.IsNullOrEmpty(list[0].CodeSnippet)) { return new List<ScanFinding>(); } return list; } private void ScanForDllImports(ModuleDefinition module, List<ScanFinding> findings) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) try { foreach (TypeDefinition allType in GetAllTypes(module)) { foreach (MethodDefinition method2 in ((IEnumerable<MethodDefinition>)allType.Methods).Where((MethodDefinition method) => method.HasCustomAttributes)) { try { CustomAttribute val = ((IEnumerable<CustomAttribute>)method2.CustomAttributes).FirstOrDefault((Func<CustomAttribute, bool>)((CustomAttribute attr) => ((MemberReference)attr.AttributeType).Name == "DllImportAttribute")); if (val != null && _rules.Any((IScanRule rule) => rule.IsSuspicious((MethodReference)(object)method2))) { IScanRule scanRule = _rules.First((IScanRule r) => r.IsSuspicious((MethodReference)(object)method2)); object[] array = new object[4]; CustomAttributeArgument val2 = ((IEnumerable<CustomAttributeArgument>)val.ConstructorArguments).FirstOrDefault(); array[0] = ((CustomAttributeArgument)(ref val2)).Value; array[1] = ((MemberReference)((MethodReference)method2).ReturnType).Name; array[2] = ((MemberReference)method2).Name; array[3] = string.Join(", ", ((IEnumerable<ParameterDefinition>)((MethodReference)method2).Parameters).Select((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).Name + " " + ((ParameterReference)p).Name)); string codeSnippet = string.Format("[DllImport(\"{0}\")]\n{1} {2}({3});", array); findings.Add(new ScanFinding(((MemberReference)method2.DeclaringType).FullName + "." + ((MemberReference)method2).Name, scanRule.Description, scanRule.Severity, codeSnippet)); } } catch (Exception) { } } } } catch (Exception) { } } private void ScanType(TypeDefinition type, List<ScanFinding> findings) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) try { Enumerator<MethodDefinition> enumerator = type.Methods.GetEnumerator(); try { while (enumerator.MoveNext()) { MethodDefinition current = enumerator.Current; ScanMethod(current, findings); } } finally { ((IDisposable)enumerator).Dispose(); } Enumerator<TypeDefinition> enumerator2 = type.NestedTypes.GetEnumerator(); try { while (enumerator2.MoveNext()) { TypeDefinition current2 = enumerator2.Current; ScanType(current2, findings); } } finally { ((IDisposable)enumerator2).Dispose(); } } catch (Exception) { } } private void ScanMethod(MethodDefinition method, List<ScanFinding> findings) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) try { if (!method.HasBody) { return; } Collection<Instruction> instructions = method.Body.Instructions; DetectCOMReflectionAttack(method, instructions, findings); for (int i = 0; i < instructions.Count; i++) { Instruction val = instructions[i]; try { if (!(val.OpCode == OpCodes.Call) && !(val.OpCode == OpCodes.Callvirt)) { continue; } object operand = val.Operand; MethodReference calledMethod = (MethodReference)((operand is MethodReference) ? operand : null); if (calledMethod == null) { continue; } if (_rules.Any((IScanRule rule) => rule.IsSuspicious(calledMethod))) { IScanRule scanRule = _rules.First((IScanRule r) => r.IsSuspicious(calledMethod)); StringBuilder stringBuilder = new StringBuilder(); int num = 2; for (int j = Math.Max(0, i - num); j < Math.Min(instructions.Count, i + num + 1); j++) { if (j == i) { stringBuilder.Append(">>> "); } else { stringBuilder.Append(" "); } stringBuilder.AppendLine(((object)instructions[j]).ToString()); } findings.Add(new ScanFinding($"{((MemberReference)method.DeclaringType).FullName}.{((MemberReference)method).Name}:{val.Offset}", scanRule.Description, scanRule.Severity, stringBuilder.ToString().TrimEnd())); } ScanForReflectionInvocation(method, val, calledMethod, i, instructions, findings); } catch (Exception) { } } } catch (Exception) { } } private void DetectCOMReflectionAttack(MethodDefinition methodDef, Collection<Instruction> instructions, List<ScanFinding> findings) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) bool flag = false; bool flag2 = false; bool flag3 = false; string text = null; string text2 = null; Enumerator<Instruction> enumerator = instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; if (current.OpCode != OpCodes.Call && current.OpCode != OpCodes.Callvirt) { continue; } object operand = current.Operand; MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); if (val == null || ((MemberReference)val).DeclaringType == null) { continue; } string fullName = ((MemberReference)((MemberReference)val).DeclaringType).FullName; string name = ((MemberReference)val).Name; if (fullName == "System.Type" && name == "GetTypeFromProgID") { flag = true; int num = instructions.IndexOf(current); for (int i = Math.Max(0, num - 5); i < num; i++) { if (instructions[i].OpCode == OpCodes.Ldstr && instructions[i].Operand is string text3) { text = text3; break; } } } if (fullName == "System.Activator" && name == "CreateInstance") { flag2 = true; } if (!(fullName == "System.Type") || !(name == "InvokeMember")) { continue; } flag3 = true; int num2 = instructions.IndexOf(current); for (int j = Math.Max(0, num2 - 5); j < num2; j++) { if (instructions[j].OpCode == OpCodes.Ldstr && instructions[j].Operand is string text4) { text2 = text4; break; } } } } finally { ((IDisposable)enumerator).Dispose(); } if (!flag || !(flag2 || flag3) || ((text == null || !text.Contains("Shell")) && (text2 == null || !text2.Contains("ShellExecute")) && (text == null || text2 == null))) { return; } StringBuilder stringBuilder = new StringBuilder(); Enumerator<Instruction> enumerator2 = instructions.GetEnumerator(); try { while (enumerator2.MoveNext()) { Instruction current2 = enumerator2.Current; stringBuilder.AppendLine(((object)current2).ToString()); } } finally { ((IDisposable)enumerator2).Dispose(); } findings.Add(new ScanFinding(((MemberReference)methodDef.DeclaringType).FullName + "." + ((MemberReference)methodDef).Name, "Reflective shell execution detected via COM (GetTypeFromProgID + InvokeMember pattern)", "Critical", stringBuilder.ToString().TrimEnd())); } private void ScanForReflectionInvocation(MethodDefinition methodDef, Instruction instruction, MethodReference calledMethod, int index, Collection<Instruction> instructions, List<ScanFinding> findings) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0098: Expected O, but got Unknown try { if (!IsReflectionInvokeMethod(calledMethod)) { return; } string invokedMethodName = ExtractInvokedMethodName(instructions, index); if (string.IsNullOrEmpty(invokedMethodName) || IsLikelyLegitimateModContext(methodDef)) { return; } MethodReference fakeMethodRef = new MethodReference(invokedMethodName, ((MemberReference)methodDef).Module.TypeSystem.Object) { DeclaringType = new TypeReference("", "ReflectedType", ((MemberReference)methodDef).Module, (IMetadataScope)null) }; if (!_rules.Any((IScanRule rule) => rule.IsSuspicious(fakeMethodRef) || WouldRuleMatchMethodName(rule, invokedMethodName))) { return; } IScanRule scanRule = _rules.FirstOrDefault((IScanRule r) => r.IsSuspicious(fakeMethodRef) || WouldRuleMatchMethodName(r, invokedMethodName)); if (scanRule == null) { return; } StringBuilder stringBuilder = new StringBuilder(); int num = 4; for (int i = Math.Max(0, index - num); i < Math.Min(instructions.Count, index + num + 1); i++) { if (i == index) { stringBuilder.Append(">>> "); } else { stringBuilder.Append(" "); } stringBuilder.AppendLine(((object)instructions[i]).ToString()); } findings.Add(new ScanFinding($"{((MemberReference)methodDef.DeclaringType).FullName}.{((MemberReference)methodDef).Name}:{instruction.Offset}", "Potential reflection bypass: " + scanRule.Description, (scanRule.Severity == "Low") ? "Medium" : scanRule.Severity, stringBuilder.ToString().TrimEnd())); } catch (Exception) { } } private bool IsLikelyLegitimateModContext(MethodDefinition methodDef) { if (((MemberReference)methodDef.DeclaringType).FullName.Contains("Patch") || ((MemberReference)methodDef.DeclaringType).FullName.Contains("Harmony") || ((MemberReference)methodDef).Name.Contains("Patch") || ((MemberReference)methodDef.DeclaringType).FullName.Contains("MonoMod")) { return true; } if (!((MemberReference)methodDef.DeclaringType).FullName.Contains("MelonLoader")) { string @namespace = ((TypeReference)methodDef.DeclaringType).Namespace; if (@namespace == null || !@namespace.Contains("MelonLoader")) { string namespace2 = ((TypeReference)methodDef.DeclaringType).Namespace; if (namespace2 == null || !namespace2.Contains("UnityEngine")) { if ((((MemberReference)methodDef).Name.Contains("Initialize") && !((MemberReference)methodDef).Name.Contains("OnInitializeMelon")) || ((MemberReference)methodDef).Name.Contains("Setup") || ((MemberReference)methodDef).Name == "OnSceneWasLoaded") { return true; } return false; } } } return true; } private bool IsReflectionInvokeMethod(MethodReference method) { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) if (((MemberReference)method).DeclaringType == null) { return false; } string fullName = ((MemberReference)((MemberReference)method).DeclaringType).FullName; string name = ((MemberReference)method).Name; if (fullName == "System.Type" && name == "InvokeMember") { return true; } if (fullName == "System.Type" && name == "GetTypeFromProgID") { return true; } if (fullName == "System.Activator" && name == "CreateInstance") { return true; } if ((fullName == "System.Type" && name == "GetTypeFromProgID") || (fullName == "System.Type" && name == "GetTypeFromCLSID")) { Enumerator<ParameterDefinition> enumerator = method.Parameters.GetEnumerator(); try { while (enumerator.MoveNext()) { ParameterDefinition current = enumerator.Current; if (((ParameterReference)current).Name.Contains("Shell") || ((ParameterReference)current).Name.Contains("Command") || ((ParameterReference)current).Name.Contains("Process") || ((ParameterReference)current).Name.Contains("Exec")) { return true; } } } finally { ((IDisposable)enumerator).Dispose(); } } if ((fullName == "System.Reflection.MethodInfo" && name == "Invoke") || (fullName == "System.Reflection.MethodBase" && name == "Invoke")) { return true; } return false; } private string ExtractInvokedMethodName(Collection<Instruction> instructions, int currentIndex) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) for (int i = Math.Max(0, currentIndex - 15); i < currentIndex; i++) { Instruction val = instructions[i]; if (val.OpCode == OpCodes.Ldstr && val.Operand is string text) { if (text.Contains("Shell.Application") || text.Contains("shell32")) { return "ShellExecute"; } if (IsSuspiciousMethodName(text)) { return text; } } } for (int j = currentIndex + 1; j < Math.Min(instructions.Count, currentIndex + 10); j++) { Instruction val2 = instructions[j]; if (val2.OpCode == OpCodes.Ldstr && val2.Operand is string text2 && (text2 == "ShellExecute" || text2 == "Execute" || text2 == "Shell")) { return text2; } } return "UnknownReflectionCall"; } private bool IsSuspiciousMethodName(string str) { if (string.IsNullOrWhiteSpace(str)) { return false; } if (str.Contains("Shell.Application") || str.Contains("shell32")) { return true; } string[] source = new string[18] { "ShellExecute", "Shell", "Execute", "Start", "Process", "Exec", "Run", "Launch", "CreateProcess", "Spawn", "Command", "Eval", "LoadLibrary", "LoadFrom", "cmd.exe", "powershell.exe", "wscript.exe", "cscript.exe" }; return source.Any((string name) => str.Equals(name, StringComparison.OrdinalIgnoreCase) || str.Contains(name, StringComparison.OrdinalIgnoreCase)); } private bool WouldRuleMatchMethodName(IScanRule rule, string methodName) { if (rule is Shell32Rule) { string[] source = new string[9] { "ShellExecute", "Shell", "Execute", "CreateProcess", "Spawn", "Command", "cmd.exe", "powershell.exe", "wscript.exe" }; return source.Any((string name) => methodName.Equals(name, StringComparison.OrdinalIgnoreCase) || methodName.Contains(name, StringComparison.OrdinalIgnoreCase)); } if (rule.Description.Contains("process") || rule.Description.Contains("Process")) { string[] source2 = new string[5] { "Start", "Process", "Exec", "Run", "Launch" }; return source2.Any((string name) => methodName.Equals(name, StringComparison.OrdinalIgnoreCase) || methodName.Contains(name, StringComparison.OrdinalIgnoreCase)); } if (rule.Description.Contains("base64") || rule.Description.Contains("Base64")) { return methodName.Equals("FromBase64String", StringComparison.OrdinalIgnoreCase) || methodName.Equals("ToBase64String", StringComparison.OrdinalIgnoreCase); } if (rule.Description.Contains("Registry")) { return methodName.Contains("Registry") || methodName.Contains("GetValue") || methodName.Contains("SetValue"); } if (rule.Description.Contains("assembly") || rule.Description.Contains("Assembly")) { return methodName.Contains("Load") || methodName.Contains("Assembly") || methodName.Contains("Compile"); } return false; } private IEnumerable<TypeDefinition> GetAllTypes(ModuleDefinition module) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) List<TypeDefinition> list = new List<TypeDefinition>(); try { Enumerator<TypeDefinition> enumerator = module.Types.GetEnumerator(); try { while (enumerator.MoveNext()) { TypeDefinition current = enumerator.Current; list.Add(current); CollectNestedTypes(current, list); } } finally { ((IDisposable)enumerator).Dispose(); } } catch (Exception) { } return list; } private void CollectNestedTypes(TypeDefinition type, List<TypeDefinition> allTypes) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { Enumerator<TypeDefinition> enumerator = type.NestedTypes.GetEnumerator(); try { while (enumerator.MoveNext()) { TypeDefinition current = enumerator.Current; allTypes.Add(current); CollectNestedTypes(current, allTypes); } } finally { ((IDisposable)enumerator).Dispose(); } } catch (Exception) { } } } public class ConfigManager { private readonly Instance _logger; private readonly MelonPreferences_Category _category; private readonly MelonPreferences_Entry<bool> _enableAutoScan; private readonly MelonPreferences_Entry<bool> _enableAutoDisable; private readonly MelonPreferences_Entry<string> _minSeverityForDisable; private readonly MelonPreferences_Entry<string[]> _scanDirectories; private readonly MelonPreferences_Entry<int> _suspiciousThreshold; private readonly MelonPreferences_Entry<string[]> _whitelistedMods; public ScanConfig Config { get; private set; } public ConfigManager(Instance logger) { _logger = logger ?? throw new ArgumentNullException("logger"); try { _category = MelonPreferences.CreateCategory("MLVScan"); _enableAutoScan = _category.CreateEntry<bool>("EnableAutoScan", true, (string)null, "Whether to scan mods at startup", false, false, (ValueValidator)null, (string)null); _enableAutoDisable = _category.CreateEntry<bool>("EnableAutoDisable", true, (string)null, "Whether to disable suspicious mods", false, false, (ValueValidator)null, (string)null); _minSeverityForDisable = _category.CreateEntry<string>("MinSeverityForDisable", "Medium", (string)null, "Minimum severity level to trigger disabling (Low, Medium, High, Critical)", false, false, (ValueValidator)null, (string)null); _scanDirectories = _category.CreateEntry<string[]>("ScanDirectories", new string[2] { "Mods", "Plugins" }, (string)null, "Directories to scan for mods", false, false, (ValueValidator)null, (string)null); _suspiciousThreshold = _category.CreateEntry<int>("SuspiciousThreshold", 1, (string)null, "How many suspicious findings required before disabling a mod", false, false, (ValueValidator)null, (string)null); _whitelistedMods = _category.CreateEntry<string[]>("WhitelistedMods", Array.Empty<string>(), (string)null, "List of mod filenames to skip when scanning (e.g., 'MLVScan.dll')", false, false, (ValueValidator)null, (string)null); ((MelonEventBase<LemonAction<bool, bool>>)(object)_enableAutoScan.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false); ((MelonEventBase<LemonAction<bool, bool>>)(object)_enableAutoDisable.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false); ((MelonEventBase<LemonAction<string, string>>)(object)_minSeverityForDisable.OnEntryValueChanged).Subscribe((LemonAction<string, string>)OnConfigChanged, 0, false); ((MelonEventBase<LemonAction<string[], string[]>>)(object)_scanDirectories.OnEntryValueChanged).Subscribe((LemonAction<string[], string[]>)OnConfigChanged, 0, false); ((MelonEventBase<LemonAction<int, int>>)(object)_suspiciousThreshold.OnEntryValueChanged).Subscribe((LemonAction<int, int>)OnConfigChanged, 0, false); ((MelonEventBase<LemonAction<string[], string[]>>)(object)_whitelistedMods.OnEntryValueChanged).Subscribe((LemonAction<string[], string[]>)OnConfigChanged, 0, false); UpdateConfigFromPreferences(); _logger.Msg("Configuration loaded successfully"); } catch (Exception ex) { _logger.Error("Failed to initialize config system: " + ex.Message); _logger.Msg("Using fallback in-memory configuration"); Config = new ScanConfig(); } } private void OnConfigChanged<T>(T oldValue, T newValue) { UpdateConfigFromPreferences(); _logger.Msg("Configuration updated"); } private void UpdateConfigFromPreferences() { Config = new ScanConfig { EnableAutoScan = _enableAutoScan.Value, EnableAutoDisable = _enableAutoDisable.Value, MinSeverityForDisable = _minSeverityForDisable.Value, ScanDirectories = _scanDirectories.Value, SuspiciousThreshold = _suspiciousThreshold.Value, WhitelistedMods = _whitelistedMods.Value }; } public void SaveConfig(ScanConfig newConfig) { try { _enableAutoScan.Value = newConfig.EnableAutoScan; _enableAutoDisable.Value = newConfig.EnableAutoDisable; _minSeverityForDisable.Value = newConfig.MinSeverityForDisable; _scanDirectories.Value = newConfig.ScanDirectories; _suspiciousThreshold.Value = newConfig.SuspiciousThreshold; _whitelistedMods.Value = newConfig.WhitelistedMods; MelonPreferences.Save(); _logger.Msg("Configuration saved successfully"); } catch (Exception ex) { _logger.Error("Error saving configuration: " + ex.Message); Config = newConfig; } } public string[] GetWhitelistedMods() { return _whitelistedMods.Value; } public void SetWhitelistedMods(string[] mods) { if (mods != null) { string[] array = (from m in mods where !string.IsNullOrWhiteSpace(m) select m.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) ? m : (m + ".dll")).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); _whitelistedMods.Value = array; MelonPreferences.Save(); UpdateConfigFromPreferences(); _logger.Msg($"Updated whitelist with {array.Length} mod(s)"); } } public bool IsModWhitelisted(string modFileName) { if (string.IsNullOrWhiteSpace(modFileName)) { return false; } if (!modFileName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) { modFileName += ".dll"; } return Config.WhitelistedMods.Contains<string>(modFileName, StringComparer.OrdinalIgnoreCase); } } public class ModDisabler { private readonly Instance _logger = logger ?? throw new ArgumentNullException("logger"); private readonly ScanConfig _config = config ?? throw new ArgumentNullException("config"); private const string DisabledExtension = ".di"; public ModDisabler(Instance logger, ScanConfig config) { } public List<string> DisableSuspiciousMods(Dictionary<string, List<ScanFinding>> scanResults, bool forceDisable = false) { if (!forceDisable && !_config.EnableAutoDisable) { _logger.Msg("Automatic disabling is turned off in configuration"); return new List<string>(); } List<string> list = new List<string>(); foreach (KeyValuePair<string, List<ScanFinding>> scanResult in scanResults) { scanResult.Deconstruct(out var key, out var value); string text = key; List<ScanFinding> source = value; List<ScanFinding> list2 = source.Where((ScanFinding f) => string.Compare(f.Severity, _config.MinSeverityForDisable, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); if (!forceDisable && list2.Count < _config.SuspiciousThreshold) { _logger.Msg("Mod " + Path.GetFileName(text) + " has suspicious patterns but below threshold"); continue; } try { string text2 = Path.ChangeExtension(text, ".di"); if (File.Exists(text2)) { File.Delete(text2); } File.Move(text, text2); _logger.Warning("Disabled potentially malicious mod: " + Path.GetFileName(text)); list.Add(text); foreach (ScanFinding item in list2.Take(3)) { _logger.Warning($" - {item}"); } if (list2.Count > 3) { _logger.Warning($" - And {list2.Count - 3} more suspicious patterns..."); } } catch (Exception ex) { _logger.Error("Failed to disable mod " + Path.GetFileName(text) + ": " + ex.Message); } } return list; } } public class ModScanner { private readonly AssemblyScanner _assemblyScanner = assemblyScanner ?? throw new ArgumentNullException("assemblyScanner"); private readonly Instance _logger = logger ?? throw new ArgumentNullException("logger"); private readonly ScanConfig _config = config ?? throw new ArgumentNullException("config"); private readonly ConfigManager _configManager = configManager ?? throw new ArgumentNullException("configManager"); public ModScanner(AssemblyScanner assemblyScanner, Instance logger, ScanConfig config, ConfigManager configManager) { } public Dictionary<string, List<ScanFinding>> ScanAllMods(bool forceScanning = false) { Dictionary<string, List<ScanFinding>> dictionary = new Dictionary<string, List<ScanFinding>>(); if (!forceScanning && !_config.EnableAutoScan) { _logger.Msg("Automatic scanning is disabled in configuration"); return dictionary; } string[] scanDirectories = _config.ScanDirectories; foreach (string path in scanDirectories) { string text = Path.Combine(MelonEnvironment.GameRootDirectory, path); if (!Directory.Exists(text)) { _logger.Warning("Directory not found: " + text); } else { ScanDirectory(text, dictionary); } } ScanThunderstoreModManager(dictionary); return dictionary; } private void ScanDirectory(string directoryPath, Dictionary<string, List<ScanFinding>> results) { string[] files = Directory.GetFiles(directoryPath, "*.dll", SearchOption.AllDirectories); _logger.Msg($"Found {files.Length} potential mod files in {directoryPath}"); string[] array = files; foreach (string text in array) { try { string fileName = Path.GetFileName(text); if (_configManager.IsModWhitelisted(fileName)) { _logger.Msg("Skipping whitelisted mod: " + fileName); continue; } List<ScanFinding> list = _assemblyScanner.Scan(text).ToList(); if (list.Count >= _config.SuspiciousThreshold) { results.Add(text, list); _logger.Warning($"Found {list.Count} suspicious patterns in {Path.GetFileName(text)}"); } } catch (Exception ex) { _logger.Error("Error scanning " + Path.GetFileName(text) + ": " + ex.Message); } } } private void ScanThunderstoreModManager(Dictionary<string, List<ScanFinding>> results) { try { string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string path = Path.Combine(folderPath, "Thunderstore Mod Manager", "DataFolder"); if (!Directory.Exists(path)) { return; } string[] directories = Directory.GetDirectories(path); foreach (string path2 in directories) { string path3 = Path.Combine(path2, "profiles"); if (!Directory.Exists(path3)) { continue; } string[] directories2 = Directory.GetDirectories(path3); foreach (string path4 in directories2) { string text = Path.Combine(path4, "Mods"); if (Directory.Exists(text)) { _logger.Msg("Scanning Thunderstore profile mods: " + text); ScanDirectory(text, results); } string text2 = Path.Combine(path4, "Plugins"); if (Directory.Exists(text2)) { _logger.Msg("Scanning Thunderstore profile plugins: " + text2); ScanDirectory(text2, results); } } } } catch (Exception ex) { _logger.Error("Error scanning Thunderstore Mod Manager directories: " + ex.Message); } } } public class PromptGeneratorService { private readonly ScanConfig _config; private readonly Instance _logger; public PromptGeneratorService(ScanConfig config, Instance logger) { _config = config ?? throw new ArgumentNullException("config"); _logger = logger ?? throw new ArgumentNullException("logger"); } public string GeneratePrompt(string modPath, List<ScanFinding> findings) { if (findings == null || !findings.Any()) { return "No suspicious findings to analyze."; } string fileName = Path.GetFileName(modPath); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Mod Security Analysis Request"); stringBuilder.AppendLine(); stringBuilder.AppendLine("I need to determine if the following mod is malicious or a false positive. Below is a security scan report generated by MLVScan (a security tool for MelonLoader mods)."); stringBuilder.AppendLine(); stringBuilder.AppendLine("## Mod Information"); stringBuilder.AppendLine("- **Filename**: " + fileName); stringBuilder.AppendLine($"- **Scan Date**: {DateTime.Now}"); stringBuilder.AppendLine($"- **Total Suspicious Patterns**: {findings.Count}"); stringBuilder.AppendLine(); Dictionary<string, int> dictionary = (from f in findings group f by f.Severity into g orderby GetSeverityRank(g.Key) descending select g).ToDictionary((IGrouping<string, ScanFinding> g) => g.Key, (IGrouping<string, ScanFinding> g) => g.Count()); stringBuilder.AppendLine("## Severity Breakdown"); foreach (KeyValuePair<string, int> item in dictionary) { stringBuilder.AppendLine($"- **{FormatSeverityLabel(item.Key)}**: {item.Value} issue(s)"); } stringBuilder.AppendLine(); stringBuilder.AppendLine("## Detailed Findings"); Dictionary<string, List<ScanFinding>> dictionary2 = (from f in findings group f by f.Description).ToDictionary((IGrouping<string, ScanFinding> g) => g.Key, (IGrouping<string, ScanFinding> g) => g.ToList()); var (dictionary5, dictionary6) = ExtractCodeBlocks(modPath, findings); foreach (KeyValuePair<string, List<ScanFinding>> item2 in dictionary2) { stringBuilder.AppendLine("### " + item2.Key); stringBuilder.AppendLine("- **Severity**: " + item2.Value[0].Severity); stringBuilder.AppendLine($"- **Occurrences**: {item2.Value.Count}"); stringBuilder.AppendLine("- **Locations & Snippets**:"); foreach (ScanFinding item3 in item2.Value.Take(5)) { stringBuilder.AppendLine(" - **Location**: " + item3.Location); if (!string.IsNullOrEmpty(item3.CodeSnippet)) { stringBuilder.AppendLine(" **IL Snippet (Exact location of suspicious call)**:"); stringBuilder.AppendLine(" ```"); string[] array = item3.CodeSnippet.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { stringBuilder.AppendLine(" " + text); } stringBuilder.AppendLine(" ```"); } if (dictionary5.TryGetValue(item3.Location, out var value) && !string.IsNullOrWhiteSpace(value)) { stringBuilder.AppendLine(" **Attempted C# Decompilation (Entire Method Context & Type Info)**:"); stringBuilder.AppendLine(" ```csharp"); string[] array2 = value.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array2) { stringBuilder.AppendLine(" " + text2); } stringBuilder.AppendLine(" ```"); } if (dictionary6.TryGetValue(item3.Location, out var value2) && !string.IsNullOrWhiteSpace(value2)) { stringBuilder.AppendLine(" **Surrounding Class Structure (Member Signatures Only)**:"); stringBuilder.AppendLine(" ```csharp"); string[] array3 = value2.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text3 in array3) { stringBuilder.AppendLine(" " + text3); } stringBuilder.AppendLine(" ```"); } stringBuilder.AppendLine(); } if (item2.Value.Count > 5) { stringBuilder.AppendLine($" - *And {item2.Value.Count - 5} more occurrences (details omitted for brevity)*"); } stringBuilder.AppendLine(); } string value3 = ExtractAssemblyMetadata(modPath); if (!string.IsNullOrEmpty(value3)) { stringBuilder.AppendLine("## Assembly Metadata"); stringBuilder.AppendLine(value3); stringBuilder.AppendLine(); } stringBuilder.AppendLine("## Request"); stringBuilder.AppendLine("Based on this scan report and code analysis, please help me determine:"); stringBuilder.AppendLine("1. Is this mod likely to be malicious or is it a false positive?"); stringBuilder.AppendLine("2. If potentially malicious, what specific security risks does it pose?"); stringBuilder.AppendLine("3. What is the intent of the suspicious code? Is there a benign explanation?"); stringBuilder.AppendLine("4. What further actions should I take? (Whitelist it, delete it, report it, etc.)"); stringBuilder.AppendLine(); stringBuilder.AppendLine("Please explain your reasoning with reference to the specific code patterns and provide context about:"); stringBuilder.AppendLine("- Whether these patterns are common in legitimate mods or game utilities"); stringBuilder.AppendLine("- Alternative explanations for the suspicious patterns"); stringBuilder.AppendLine("- Your confidence level in the assessment"); stringBuilder.AppendLine(); stringBuilder.AppendLine("Important context: This is a mod for a game using MelonLoader (a mod loading framework). Legitimate mods generally don't need to use system-level APIs like shell execution, registry access, etc."); return stringBuilder.ToString(); } private Tuple<Dictionary<string, string>, Dictionary<string, string>> ExtractCodeBlocks(string modPath, List<ScanFinding> findings) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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) Dictionary<string, string> dictionary = new Dictionary<string, string>(); Dictionary<string, string> dictionary2 = new Dictionary<string, string>(); try { if (!File.Exists(modPath)) { return Tuple.Create(dictionary, dictionary2); } ReaderParameters val = new ReaderParameters { ReadWrite = false, InMemory = true, ReadSymbols = false }; AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(modPath, val); try { Dictionary<string, List<string>> dictionary3 = new Dictionary<string, List<string>>(); Enumerator<ModuleDefinition> enumerator = val2.Modules.GetEnumerator(); try { while (enumerator.MoveNext()) { ModuleDefinition current = enumerator.Current; Enumerator<TypeDefinition> enumerator2 = current.Types.GetEnumerator(); try { while (enumerator2.MoveNext()) { TypeDefinition current2 = enumerator2.Current; CollectSuspiciousStrings(current2, dictionary3); } } finally { ((IDisposable)enumerator2).Dispose(); } } } finally { ((IDisposable)enumerator).Dispose(); } if (dictionary3.Any()) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("// Notable string literals found in the assembly:"); stringBuilder.AppendLine(); foreach (string item in dictionary3.Keys.OrderBy((string k) => k)) { stringBuilder.AppendLine("// Potential " + item + ":"); foreach (string item2 in dictionary3[item].Take(10)) { stringBuilder.AppendLine("// \"" + EscapeStringForCode(item2) + "\""); } if (dictionary3[item].Count > 10) { stringBuilder.AppendLine($"// ... and {dictionary3[item].Count - 10} more"); } stringBuilder.AppendLine(); } dictionary["SuspiciousStrings"] = stringBuilder.ToString(); } foreach (ScanFinding finding in findings) { try { string location = finding.Location; if (location == "Assembly scanning" || !location.Contains(".")) { continue; } string empty = string.Empty; string fullTypeName; string methodNameFromFinding; if (location.Contains(":")) { string[] array = location.Split(':'); string text = array[0]; empty = array[1]; int num = text.LastIndexOf('.'); if (num > 0) { fullTypeName = text.Substring(0, num); methodNameFromFinding = text.Substring(num + 1); goto IL_032f; } } else { int num2 = location.LastIndexOf('.'); if (num2 > 0) { fullTypeName = location.Substring(0, num2); methodNameFromFinding = location.Substring(num2 + 1); goto IL_032f; } } goto end_IL_0244; IL_032f: TypeDefinition val3 = FindType(val2.MainModule, fullTypeName); if (val3 == null) { continue; } if (!dictionary2.ContainsKey(finding.Location) || dictionary2[finding.Location] == string.Empty) { dictionary2[finding.Location] = GenerateClassStructure(val3, methodNameFromFinding); } MethodDefinition val4 = ((IEnumerable<MethodDefinition>)val3.Methods).FirstOrDefault((Func<MethodDefinition, bool>)((MethodDefinition m) => ((MemberReference)m).Name == methodNameFromFinding)); if (val4 == null) { if (!dictionary.ContainsKey(finding.Location)) { dictionary[finding.Location] = "// DllImport or external method: " + finding.Location + ". Primary context is the IL snippet and class structure."; } continue; } if (!val4.HasBody && !val4.IsAbstract) { if (!dictionary.ContainsKey(finding.Location)) { dictionary[finding.Location] = "// Method " + methodNameFromFinding + " has no body or is abstract."; } continue; } string text2 = DecompileMethod(val4); if (string.IsNullOrEmpty(text2)) { continue; } StringBuilder stringBuilder2 = new StringBuilder(); stringBuilder2.AppendLine("// Context: Method is part of " + ((MemberReference)val3).FullName); if (val3.HasCustomAttributes) { stringBuilder2.AppendLine("// Type attributes:"); foreach (CustomAttribute item3 in ((IEnumerable<CustomAttribute>)val3.CustomAttributes).Take(5)) { stringBuilder2.AppendLine("// - " + ((MemberReference)item3.AttributeType).Name); } if (val3.CustomAttributes.Count > 5) { stringBuilder2.AppendLine($"// - ...and {val3.CustomAttributes.Count - 5} more"); } } if (val3.BaseType != null && ((MemberReference)val3.BaseType).FullName != "System.Object") { stringBuilder2.AppendLine("// Inherits from: " + ((MemberReference)val3.BaseType).FullName); } List<MethodDefinition> list = FindRelatedSuspiciousMethods(val3, val4); if (list.Any()) { stringBuilder2.AppendLine("// Other suspicious methods in this class (names only):"); foreach (MethodDefinition item4 in list.Take(3)) { stringBuilder2.AppendLine("// - " + ((MemberReference)item4).Name); } if (list.Count > 3) { stringBuilder2.AppendLine($"// - ...and {list.Count - 3} more"); } } stringBuilder2.AppendLine(); stringBuilder2.AppendLine("// Finding Description: " + finding.Description); stringBuilder2.AppendLine("// Severity: " + finding.Severity); stringBuilder2.AppendLine(); dictionary[finding.Location] = stringBuilder2.ToString() + text2; end_IL_0244:; } catch (Exception ex) { _logger.Error("Failed to extract code for " + finding.Location + ": " + ex.Message); dictionary[finding.Location] = "// Error extracting detailed code for " + finding.Location + ": " + ex.Message; } } if (((IEnumerable<Resource>)val2.MainModule.Resources).Any()) { StringBuilder stringBuilder3 = new StringBuilder(); stringBuilder3.AppendLine("// Assembly Resources (could contain hidden payloads):"); foreach (Resource item5 in ((IEnumerable<Resource>)val2.MainModule.Resources).Take(20)) { stringBuilder3.AppendLine("// " + item5.Name + " - " + GetResourceTypeName(item5)); } if (val2.MainModule.Resources.Count > 20) { stringBuilder3.AppendLine($"// ...and {val2.MainModule.Resources.Count - 20} more resources"); } dictionary["Assembly.Resources"] = stringBuilder3.ToString(); } } finally { ((IDisposable)val2)?.Dispose(); } } catch (Exception ex2) { _logger.Error("Failed to extract code blocks from " + modPath + ": " + ex2.Message); } return Tuple.Create(dictionary, dictionary2); } private void CollectSuspiciousStrings(TypeDefinition type, Dictionary<string, List<string>> suspiciousStrings) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) Enumerator<MethodDefinition> enumerator = type.Methods.GetEnumerator(); try { while (enumerator.MoveNext()) { MethodDefinition current = enumerator.Current; if (!current.HasBody) { continue; } Enumerator<Instruction> enumerator2 = current.Body.Instructions.GetEnumerator(); try { while (enumerator2.MoveNext()) { Instruction current2 = enumerator2.Current; OpCode opCode = current2.OpCode; if (((OpCode)(ref opCode)).Name == "ldstr" && current2.Operand is string text && IsSuspiciousString(text, out var category)) { if (!suspiciousStrings.ContainsKey(category)) { suspiciousStrings[category] = new List<string>(); } if (!suspiciousStrings[category].Contains(text)) { suspiciousStrings[category].Add(text); } } } } finally { ((IDisposable)enumerator2).Dispose(); } } } finally { ((IDisposable)enumerator).Dispose(); } Enumerator<TypeDefinition> enumerator3 = type.NestedTypes.GetEnumerator(); try { while (enumerator3.MoveNext()) { TypeDefinition current3 = enumerator3.Current; CollectSuspiciousStrings(current3, suspiciousStrings); } } finally { ((IDisposable)enumerator3).Dispose(); } } private bool IsSuspiciousString(string value, out string category) { if (string.IsNullOrEmpty(value)) { category = string.Empty; return false; } if (value.Length > 20 && IsLikelyBase64(value)) { category = "Base64 encoded data"; return true; } if (value.StartsWith("http://") || value.StartsWith("https://") || value.StartsWith("ftp://") || value.StartsWith("ws://")) { category = "URL"; return true; } if (value.Contains(".exe") || value.Contains(".dll") || value.Contains(".bat") || value.Contains(".cmd") || value.Contains(".ps1") || value.Contains(".vbs")) { category = "executable file reference"; return true; } if (IsLikelyIPAddress(value)) { category = "IP address"; return true; } if (value.StartsWith("HKEY_") || value.Contains("\\Software\\") || value.Contains("\\Microsoft\\") || value.Contains("\\System\\")) { category = "registry path"; return true; } if (value.StartsWith("cmd ") || value.StartsWith("powershell ") || value.Contains(" /c ") || value.Contains(" /k ")) { category = "command line"; return true; } if (value.Contains("encrypt") || value.Contains("decrypt") || value.Contains("aes") || value.Contains("rsa") || value.Contains("md5") || value.Contains("sha") || value.Contains("hash")) { category = "cryptographic reference"; return true; } if ((value.Contains(".") && !value.Contains(" ") && !value.Contains("\\") && !value.EndsWith(".cs") && !value.EndsWith(".txt") && value.Length > 5) || value.Contains("pastebin") || value.Contains("discord") || value.Contains("webhook")) { category = "domain or web service"; return true; } category = string.Empty; return false; } private bool IsLikelyBase64(string value) { if (value.Length % 4 != 0) { return false; } foreach (char c2 in value) { if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 > 'z') && (c2 < '0' || c2 > '9') && c2 != '+' && c2 != '/' && c2 != '=') { return false; } } return value.Length > 20 && value.Any((char c) => c >= 'A' && c <= 'Z') && value.Any((char c) => c >= 'a' && c <= 'z') && value.Any((char c) => c >= '0' && c <= '9'); } private bool IsLikelyIPAddress(string value) { string pattern = "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"; string pattern2 = "^[0-9a-fA-F:]+"; return Regex.IsMatch(value, pattern) || Regex.IsMatch(value, pattern2); } private string GetResourceTypeName(Resource resource) { //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) EmbeddedResource val = (EmbeddedResource)(object)((resource is EmbeddedResource) ? resource : null); if (val != null) { using (Stream stream = val.GetResourceStream()) { if (stream.Length > 0) { byte[] array = new byte[Math.Min(stream.Length, 16L)]; stream.Read(array, 0, array.Length); if (array.Length >= 2 && array[0] == 77 && array[1] == 90) { return "PE File/DLL (MZ header)"; } if (array.Length >= 4 && array[0] == 127 && array[1] == 69 && array[2] == 76 && array[3] == 70) { return "ELF Binary"; } if (array.Length >= 4 && array[0] == 80 && array[1] == 75 && array[2] == 3 && array[3] == 4) { return "ZIP Archive"; } if (array.Length >= 2 && array[0] == byte.MaxValue && array[1] == 216) { return "JPEG Image"; } if (array.Length >= 3 && array[0] == 71 && array[1] == 73 && array[2] == 70) { return "GIF Image"; } if (array.Length >= 4 && ((array[0] == 137 && array[1] == 80 && array[2] == 78 && array[3] == 71) || (array[0] == 66 && array[1] == 77))) { return "PNG or BMP Image"; } return $"Binary data ({stream.Length} bytes)"; } return "Empty resource"; } } ResourceType resourceType = resource.ResourceType; return ((object)(ResourceType)(ref resourceType)).ToString(); } private List<MethodDefinition> FindRelatedSuspiciousMethods(TypeDefinition type, MethodDefinition currentMethod) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) List<MethodDefinition> list = new List<MethodDefinition>(); Enumerator<MethodDefinition> enumerator = type.Methods.GetEnumerator(); try { while (enumerator.MoveNext()) { MethodDefinition current = enumerator.Current; if (current == currentMethod || !current.HasBody) { continue; } Enumerator<Instruction> enumerator2 = current.Body.Instructions.GetEnumerator(); try { while (enumerator2.MoveNext()) { Instruction current2 = enumerator2.Current; OpCode opCode = current2.OpCode; if (!(((OpCode)(ref opCode)).Name == "call")) { opCode = current2.OpCode; if (!(((OpCode)(ref opCode)).Name == "callvirt")) { opCode = current2.OpCode; if (!(((OpCode)(ref opCode)).Name == "newobj")) { continue; } } } object operand = current2.Operand; MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); if (val != null && IsSuspiciousMethodCall(val)) { list.Add(current); break; } } } finally { ((IDisposable)enumerator2).Dispose(); } } } finally { ((IDisposable)enumerator).Dispose(); } return list; } private TypeDefinition FindType(ModuleDefinition module, string fullTypeName) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) Enumerator<TypeDefinition> enumerator = module.Types.GetEnumerator(); try { while (enumerator.MoveNext()) { TypeDefinition current = enumerator.Current; if (((MemberReference)current).FullName == fullTypeName) { return current; } TypeDefinition val = FindNestedType(current, fullTypeName); if (val != null) { return val; } } } finally { ((IDisposable)enumerator).Dispose(); } return null; } private TypeDefinition FindNestedType(TypeDefinition parentType, string fullTypeName) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) Enumerator<TypeDefinition> enumerator = parentType.NestedTypes.GetEnumerator(); try { while (enumerator.MoveNext()) { TypeDefinition current = enumerator.Current; if (((MemberReference)current).FullName == fullTypeName) { return current; } TypeDefinition val = FindNestedType(current, fullTypeName); if (val != null) { return val; } } } finally { ((IDisposable)enumerator).Dispose(); } return null; } private string DecompileMethod(MethodDefinition method) { //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_041e: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) if (!method.HasBody) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(GetMethodVisibility(method) + " " + ((MemberReference)((MethodReference)method).ReturnType).Name + " " + ((MemberReference)method).Name + "(" + GetMethodParameters(method) + ")"); stringBuilder.AppendLine("{"); string value = ReconstructCode(method); if (!string.IsNullOrEmpty(value)) { stringBuilder.AppendLine(value); } else { stringBuilder.AppendLine(" // Method body:"); List<ExceptionHandler> source = ((IEnumerable<ExceptionHandler>)method.Body.ExceptionHandlers).ToList(); HashSet<int> hashSet = source.Select((ExceptionHandler h) => h.HandlerStart.Offset).ToHashSet(); HashSet<int> hashSet2 = source.Select(delegate(ExceptionHandler h) { Instruction handlerEnd = h.HandlerEnd; return (handlerEnd != null) ? handlerEnd.Offset : int.MaxValue; }).ToHashSet(); HashSet<int> hashSet3 = source.Select((ExceptionHandler h) => h.TryStart.Offset).ToHashSet(); HashSet<int> hashSet4 = source.Select(delegate(ExceptionHandler h) { Instruction tryEnd = h.TryEnd; return (tryEnd != null) ? tryEnd.Offset : int.MaxValue; }).ToHashSet(); bool flag = false; bool flag2 = false; List<VariableDefinition> variables = ((IEnumerable<VariableDefinition>)method.Body.Variables).ToList(); List<ParameterDefinition> parameters = ((IEnumerable<ParameterDefinition>)((MethodReference)method).Parameters).ToList(); List<Instruction> list = ((IEnumerable<Instruction>)method.Body.Instructions).ToList(); for (int i = 0; i < list.Count; i++) { Instruction instruction = list[i]; if (hashSet3.Contains(instruction.Offset) && !flag) { stringBuilder.AppendLine(" try {"); flag = true; } if (hashSet.Contains(instruction.Offset) && !flag2) { ExceptionHandler val = ((IEnumerable<ExceptionHandler>)source).FirstOrDefault((Func<ExceptionHandler, bool>)((ExceptionHandler h) => h.HandlerStart.Offset == instruction.Offset)); object obj; if (val == null) { obj = null; } else { TypeReference catchType = val.CatchType; obj = ((catchType != null) ? ((MemberReference)catchType).Name : null); } if (obj == null) { obj = "Exception"; } stringBuilder.AppendLine(" } catch (" + (string?)obj + ") {"); flag = false; flag2 = true; } if ((hashSet4.Contains(instruction.Offset) && flag) || (hashSet2.Contains(instruction.Offset) && flag2)) { stringBuilder.AppendLine(" }"); flag = false; flag2 = false; } string text = FormatInstructionWithContext(instruction, i, list, variables, parameters); if (!string.IsNullOrEmpty(text)) { string text2 = " "; if (flag || flag2) { text2 += " "; } stringBuilder.AppendLine(text2 + text); } } if (flag || flag2) { stringBuilder.AppendLine(" }"); } } if (((MethodReference)method).Parameters.Count > 0) { stringBuilder.AppendLine(); stringBuilder.AppendLine(" // Method parameters:"); Enumerator<ParameterDefinition> enumerator = ((MethodReference)method).Parameters.GetEnumerator(); try { while (enumerator.MoveNext()) { ParameterDefinition current = enumerator.Current; stringBuilder.AppendLine(" // " + ((MemberReference)((ParameterReference)current).ParameterType).FullName + " " + ((ParameterReference)current).Name); } } finally { ((IDisposable)enumerator).Dispose(); } } if (method.Body.Variables.Count > 0) { stringBuilder.AppendLine(); stringBuilder.AppendLine(" // Local variables:"); Enumerator<VariableDefinition> enumerator2 = method.Body.Variables.GetEnumerator(); try { while (enumerator2.MoveNext()) { VariableDefinition current2 = enumerator2.Current; stringBuilder.AppendLine($" // {((MemberReference)((VariableReference)current2).VariableType).FullName} var_{((VariableReference)current2).Index}"); } } finally { ((IDisposable)enumerator2).Dispose(); } } List<string> list2 = FindSuspiciousApiCalls(method); if (list2.Any()) { stringBuilder.AppendLine(); stringBuilder.AppendLine(" // Suspicious API calls:"); foreach (string item in list2) { stringBuilder.AppendLine(" // " + item); } } stringBuilder.AppendLine("}"); return stringBuilder.ToString(); } private string ReconstructCode(MethodDefinition method) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); try { List<Instruction> list = ((IEnumerable<Instruction>)method.Body.Instructions).ToList(); for (int i = 0; i < list.Count; i++) { Instruction val = list[i]; OpCode opCode = val.OpCode; if (!(((OpCode)(ref opCode)).Name == "call")) { opCode = val.OpCode; if (!(((OpCode)(ref opCode)).Name == "callvirt")) { opCode = val.OpCode; if (((OpCode)(ref opCode)).Name == "newobj") { object operand = val.Operand; MethodReference val2 = (MethodReference)((operand is MethodReference) ? operand : null); if (val2 != null) { stringBuilder.AppendLine(" new " + ((MemberReference)((MemberReference)val2).DeclaringType).Name + "(...);"); continue; } } opCode = val.OpCode; if (((OpCode)(ref opCode)).Name == "ldstr" && val.Operand is string value) { stringBuilder.AppendLine(" // String: \"" + EscapeStringForCode(value) + "\""); } continue; } } object operand2 = val.Operand; MethodReference val3 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (val3 != null) { string methodCallRepresentation = GetMethodCallRepresentation(val3, list, i); if (!string.IsNullOrEmpty(methodCallRepresentation)) { stringBuilder.AppendLine(" " + methodCallRepresentation + ";"); } } } if (stringBuilder.Length == 0) { return string.Empty; } return stringBuilder.ToString(); } catch { return string.Empty; } } private string EscapeStringForCode(string value) { if (string.IsNullOrEmpty(value)) { return value; } return value.Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n") .Replace("\t", "\\t"); } private string GetMethodCallRepresentation(MethodReference methodRef, List<Instruction> instructions, int currentIndex) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) string name = ((MemberReference)methodRef).Name; string name2 = ((MemberReference)((MemberReference)methodRef).DeclaringType).Name; if (name.Contains("Process") && name.Contains("Start")) { return "System.Diagnostics.Process.Start(...) // Executes external process"; } if (name.Contains("Load") && name2.Contains("Assembly")) { return "Assembly." + name + "(...) // Dynamically loads code"; } if ((name.Contains("FromBase64") || name.Contains("GetString")) && name2.Contains("Convert")) { string text = "..."; for (int num = currentIndex - 1; num >= Math.Max(0, currentIndex - 5); num--) { OpCode opCode = instructions[num].OpCode; if (((OpCode)(ref opCode)).Name == "ldstr" && instructions[num].Operand is string) { text = instructions[num].Operand.ToString(); if (text.Length > 20) { text = text.Substring(0, 17) + "..."; } break; } } return "Convert." + name + "(\"" + text + "\") // Decodes Base64 data"; } if (name.Contains("RegOpenKey") || name.Contains("RegCreateKey") || (name.Contains("Registry") && (name.Contains("Get") || name.Contains("Set")))) { return name2 + "." + name + "(...) // Registry manipulation"; } if (name.Contains("CreateFile") || name.Contains("WriteFile") || name.Contains("ReadFile")) { return name2 + "." + name + "(...) // File system operation"; } if (name.Contains("Socket") || name.Contains("Connect") || name.Contains("Send") || name.Contains("Receive")) { return name2 + "." + name + "(...) // Network communication"; } return name2 + "." + name + "(...)"; } private string FormatInstructionWithContext(Instruction instruction, int index, List<Instruction> allInstructions, List<VariableDefinition> variables, List<ParameterDefinition> parameters) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) OpCode opCode = instruction.OpCode; string name = ((OpCode)(ref opCode)).Name; if (name == "nop") { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append($"/* {instruction.Offset:X4} */ "); object operand = instruction.Operand; MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); if (val != null) { stringBuilder.Append(name + " " + ((MemberReference)((MemberReference)val).DeclaringType).FullName + "." + ((MemberReference)val).Name + "()"); if (val.Parameters.Count > 0) { stringBuilder.Append(" // Takes: " + string.Join(", ", ((IEnumerable<ParameterDefinition>)val.Parameters).Select((ParameterDefinition p) => ((MemberReference)((ParameterReference)p).ParameterType).Name))); } if (IsSuspiciousMethodCall(val)) { stringBuilder.Append(" // SUSPICIOUS: " + GetSuspiciousMethodDescription(val)); } } else { object operand2 = instruction.Operand; TypeReference val2 = (TypeReference)((operand2 is TypeReference) ? operand2 : null); if (val2 != null) { stringBuilder.Append(name + " " + ((MemberReference)val2).FullName); } else { object operand3 = instruction.Operand; FieldReference val3 = (FieldReference)((operand3 is FieldReference) ? operand3 : null); if (val3 != null) { stringBuilder.Append(name + " " + ((MemberReference)((MemberReference)val3).DeclaringType).Name + "." + ((MemberReference)val3).Name); } else { object operand4 = instruction.Operand; VariableDefinition val4 = (VariableDefinition)((operand4 is VariableDefinition) ? operand4 : null); if (val4 != null) { string name2 = ((MemberReference)((VariableReference)val4).VariableType).Name; stringBuilder.Append($"{name} V_{((VariableReference)val4).Index} /* {name2} */"); } else { object operand5 = instruction.Operand; ParameterDefinition val5 = (ParameterDefinition)((operand5 is ParameterDefinition) ? operand5 : null); if (val5 != null) { string name3 = ((MemberReference)((ParameterReference)val5).ParameterType).Name; stringBuilder.Append(name + " " + ((ParameterReference)val5).Name + " /* " + name3 + " */"); } else if (instruction.Operand is string text) { string text2 = text; if (text2.Length > 50) { text2 = text2.Substring(0, 47) + "..."; } stringBuilder.Append(name + " \"" + EscapeStringForCode(text2) + "\""); } else { object operand6 = instruction.Operand; Instruction val6 = (Instruction)((operand6 is Instruction) ? operand6 : null); if (val6 != null) { stringBuilder.Append($"{name} IL_{val6.Offset:X4}"); } else { string text3 = instruction.Operand?.ToString() ?? string.Empty; stringBuilder.Append(name + " " + text3); } } } } } } return stringBuilder.ToString(); } private bool IsSuspiciousMethodCall(MethodReference methodRef) { string fullName = ((MemberReference)((MemberReference)methodRef).DeclaringType).FullName; string name = ((MemberReference)methodRef).Name; return (fullName.Contains("Process") && name.Contains("Start")) || (fullName.Contains("Assembly") && (name.Contains("Load") || name.Contains("LoadFrom") || name.Contains("LoadFile"))) || (fullName.Contains("Convert") && name.Contains("FromBase64")) || ((fullName.Contains("Registry") || name.Contains("Reg")) && (name.Contains("CreateKey") || name.Contains("OpenKey") || name.Contains("SetValue") || name.Contains("GetValue"))) || fullName.Contains("Shell32") || name.Contains("ShellExecute") || ((fullName.Contains("Socket") || fullName.Contains("Http") || fullName.Contains("Tcp") || fullName.Contains("Web") || fullName.Contains("Net")) && (name.Contains("Connect") || name.Contains("Send") || name.Contains("Download") || name.Contains("Upload"))); } private string GetSuspiciousMethodDescription(MethodReference methodRef) { string fullName = ((MemberReference)((MemberReference)methodRef).DeclaringType).FullName; string name = ((MemberReference)methodRef).Name; if (fullName.Contains("Process") && name.Contains("Start")) { return "Executes external programs"; } if (fullName.Contains("Assembly") && (name.Contains("Load") || name.Contains("LoadFrom") || name.Contains("LoadFile"))) { return "Dynamically loads code which could be malicious"; } if (fullName.Contains("Convert") && name.Contains("FromBase64")) { return "Decodes potentially obfuscated data"; } if ((fullName.Contains("Registry") || name.Contains("Reg")) && (name.Contains("CreateKey") || name.Contains("OpenKey") || name.Contains("SetValue") || name.Contains("GetValue"))) { return "Manipulates system registry which can persist malware"; } if (fullName.Contains("Shell32") || name.Contains("ShellExecute")) { return "Executes system commands"; } if ((fullName.Contains("Socket") || fullName.Contains("Http") || fullName.Contains("Tcp") || fullName.Contains("Web") || fullName.Contains("Net")) && (name.Contains("Connect") || name.Contains("Send") || name.Contains("Download") || name.Contains("Upload"))) { return "Performs network operations that could exfiltrate data or download malware"; } return "Potentially suspicious behavior"; } private List<string> FindSuspiciousApiCalls(MethodDefinition method) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); Enumerator<Instruction> enumerator = method.Body.Instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; OpCode opCode = current.OpCode; if (!(((OpCode)(ref opCode)).Name == "call")) { opCode = current.OpCode; if (!(((OpCode)(ref opCode)).Name == "callvirt")) { opCode = current.OpCode; if (!(((OpCode)(ref opCode)).Name == "newobj")) { continue; } } } object operand = current.Operand; MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); if (val != null && IsSuspiciousMethodCall(val)) { list.Add(((MemberReference)((MemberReference)val).DeclaringType).FullName + "." + ((MemberReference)val).Name + "() - " + GetSuspiciousMethodDescription(val)); } } } finally { ((IDisposable)enumerator).Dispose(); } return list.Distinct().ToList(); } private string GetFieldVisibility(FieldDefinition field) { if (field.IsPublic) { return "public"; } if (field.IsPrivate) { return "private"; } if (field.IsFamily) { return "protected"; } if (field.IsFamilyOrAssembly) { return "protected internal"; } if (field.IsAssembly) { return "internal"; } return "private"; } private string GetPropertyVisibility(PropertyDefinition prop) { MethodDefinition getMethod = prop.GetMethod; MethodDefinition setMethod = prop.SetMethod; if ((getMethod != null && getMethod.IsPublic) || (setMethod != null && setMethod.IsPublic)) { return "public"; } if ((getMethod != null && getMethod.IsFamilyOrAssembly) || (setMethod != null && setMethod.IsFamilyOrAssembly)) { return "protected internal"; } if ((getMethod != null && getMethod.IsFamily) || (setMethod != null && setMethod.IsFamily)) { return "protected"; } if ((getMethod != null && getMethod.IsAssembly) || (setMethod != null && setMethod.IsAssembly)) { return "internal"; } if ((getMethod != null && getMethod.IsPrivate) || (setMethod != null && setMethod.IsPrivate)) { return "private"; } if (getMethod == null && setMethod == null) { return "public"; } return "public"; } private string GenerateClassStructure(TypeDefinition typeDef, string highlightMethodName = null) { //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) if (typeDef == null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("// Class Outline: " + ((MemberReference)typeDef).FullName); if (typeDef.BaseType != null && ((MemberReference)typeDef.BaseType).FullName != "System.Object") { stringBuilder.AppendLine("// Inherits from: " + ((MemberReference)typeDef.BaseType).FullName); } if (typeDef.HasInterfaces) { Enumerator<InterfaceImplementation> enumerator = typeDef.Interfaces.GetEnumerator(); try { while (enumerator.MoveNext()) { InterfaceImplementation current = enumerator.Current; stringBuilder.AppendLine("// Implements: " + ((MemberReference)current.InterfaceType).FullName); } } finally { ((IDisposable)enumerator).Dispose(); } } string text = "class"; if (typeDef.IsInterface) { text = "interface"; } else if (typeDef.IsEnum) { text = "enum"; } else if (((TypeReference)typeDef).IsValueType) { text = "struct"; } stringBuilder.AppendLine("public " + text + " " + ((MemberReference)typeDef).Name + " // Simplified declaration"); stringBuilder.AppendLine("{"); List<FieldDefinition> list = ((IEnumerable<FieldDefinition>)typeDef.Fields).ToList(); if (list.Any()) { stringBuilder.AppendLine(" // Fields"); foreach (FieldDefinition item in list.Take(10)) { stringBuilder.AppendLine(" " + GetFieldVisibility(item) + " " + (item.IsStatic ? "static " : "") + ((MemberReference)((FieldReference)item).FieldType).Name + " " + ((MemberReference)item).Name + ";"); } if (list.Count > 10) { stringBuilder.AppendLine($" // ... and {list.Count - 10} more fields"); } stringBuilder.AppendLine(); } List<PropertyDefinition> list2 = ((IEnumerable<PropertyDefinition>)typeDef.Properties).ToList(); if (list2.Any()) { stringBuilder.AppendLine(" // Properties"); foreach (PropertyDefinition item2 in list2.Take(10)) { string text2 = "{ "; if (item2.GetMethod != null) { text2 += "get; "; } if (item2.SetMethod != null) { text2 += "set; "; } text2 += "}"; MethodDefinition getMethod = item2.GetMethod; int num; if (getMethod == null || !getMethod.IsStatic) { MethodDefinition setMethod = item2.SetMethod; num = ((setMethod != null && setMethod.IsStatic) ? 1 : 0); } else { num = 1; } bool flag = (byte)num != 0; stringBuilder.AppendLine(" " + GetPropertyVisibility(item2) + " " + (flag ? "static " : "") + ((MemberReference)((PropertyReference)item2).PropertyType).Name + " " + ((MemberReference)item2).Name + " " + text2); } if (list2.Count > 10) { stringBuilder.AppendLine($" // ... and {list2.Count - 10} more properties"); } stringBuilder.AppendLine(); } List<MethodDefinition> list3 = ((IEnumerable<MethodDefinition>)typeDef.Methods).Where((MethodDefinition m) => !m.IsConstructor && !m.IsSpecialName).ToList(); if (list3.Any()) { stringBuilder.AppendLine(" // Methods (signatures only, excluding constructors/property accessors)"); foreach (MethodDefinition item3 in list3.Take(15)) { string text3 = ((highlightMethodName != null && ((MemberReference)item3).Name == highlightMethodName) ? " // <<< Method with finding" : ""); stringBuilder.AppendLine(" " + GetMethodVisibility(item3) + " " + (item3.IsStatic ? "static " : "") + ((MemberReference)((MethodReference)item3).ReturnType).Name + " " + ((MemberReference)item3).Name + "(" + GetMethodParameters(item3) + ");" + text3); } if (list3.Count > 15) { stringBuilder.AppendLine($" // ... and {list3.Count - 15} more methods"); } } stringBuilder.AppendLine("}"); return stringBuilder.ToString(); } private string GetMethodVisibility(MethodDefinition method) { if (method.IsPublic) { return "public"; } if (method.IsPrivate) { return "private"; } if (method.IsFamily) { return "protected"; } if (method.IsFamilyOrAssembly) { return "protected internal"; } return "internal"; } private string GetMethodParameters(MethodDefinition method) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); Enumerator<ParameterDefinition> enumerator = ((MethodReference)method).Parameters.GetEnumerator(); try { while (enumerator.MoveNext()) { ParameterDefinition current = enumerator.Current; list.Add(((MemberReference)((ParameterReference)current).ParameterType).Name + " " + ((ParameterReference)current).Name); } } finally { ((IDisposable)enumerator).Dispose(); } return string.Join(", ", list); } private string FormatInstruction(Instruction instruction) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) OpCode opCode = instruction.OpCode; string name = ((OpCode)(ref opCode)).Name; if (name == "nop") { return string.Empty; } string text = FormatOperand(instruction.Operand); return (name + " " + text).Trim(); } private string FormatOperand(object operand) { if (operand == null) { return string.Empty; } MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); if (val != null) { return ((MemberReference)((MemberReference)val).DeclaringType).Name + "." + ((MemberReference)val).Name + "()"; } TypeReference val2 = (TypeReference)((operand is TypeReference) ? operand : null); if (val2 != null) { return ((MemberReference)val2).FullName; } FieldReference val3 = (FieldReference)((operand is FieldReference) ? operand : null); if (val3 != null) { return ((MemberReference)((MemberReference)val3).DeclaringType).Name + "." + ((MemberReference)val3).Name; } if (operand is string text) { return "\"" + text + "\""; } return operand.ToString(); } private string ExtractAssemblyMetadata(string modPath) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown try { if (!File.Exists(modPath)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); ReaderParameters val = new ReaderParameters { ReadWrite = false, InMemory = true, ReadSymbols = false }; AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(modPath, val); try { stringBuilder.AppendLine("- **Assembly Name**: " + ((AssemblyNameReference)val2.Name).Name); stringBuilder.AppendLine($"- **Version**: {((AssemblyNameReference)val2.Name).Version}"); if (!string.IsNullOrEmpty(((AssemblyNameReference)val2.Name).Culture)) { stringBuilder.AppendLine("- **Culture**: " + ((AssemblyNameReference)val2.Name).Culture); } stringBuilder.AppendLine("- **Referenced Assemblies**:"); foreach (AssemblyNameReference item in ((IEnumerable<AssemblyNameReference>)val2.MainModule.AssemblyReferences).Take(10)) { stringBuilder.AppendLine($" - {item.Name} (v{item.Version})"); } if (val2.MainModule.AssemblyReferences.Count > 10) { stringBuilder.AppendLine($" - *and {val2.MainModule.AssemblyReferences.Count - 10} more...*"); } List<CustomAttribute> list = ((IEnumerable<CustomAttribute>)val2.CustomAttributes).Where((CustomAttribute attr) => ((MemberReference)attr.AttributeType).Name.Contains("Security") || ((MemberReference)attr.AttributeType).Name.Contains("Permission") || ((MemberReference)attr.AttributeType).Name.Contains("Unsafe")).ToList(); if (list.Any()) { stringBuilder.AppendLine("- **Security-Related Attributes**:"); foreach (CustomAttribute item2 in list) { stringBuilder.AppendLine(" - " + ((MemberReference)item2.AttributeType).Name); } } return stringBuilder.ToString(); } finally { ((IDisposable)val2)?.Dispose(); } } catch (Exception ex) { _logger.Error("Failed to extract assembly metadata from " + modPath + ": " + ex.Message); return string.Empty; } } public bool SavePromptToFile(string modPath, List<ScanFinding> findings, string outputDirectory) { try { string contents = GeneratePrompt(modPath, findings); string fileName = Path.GetFileName(modPath); Directory.CreateDirectory(outputDirectory); string path = Path.Combine(outputDirectory, fileName + ".prompt.md"); File.WriteAllText(path, contents); return true; } catch (Exception ex) { _logger.Error("Failed to save prompt to file: " + ex.Message); return false; } } private static int GetSeverityRank(string severity) { string text = severity.ToLower(); if (1 == 0) { } int result = text switch { "critical" => 4, "high" => 3, "medium" => 2, "low" => 1, _ => 0, }; if (1 == 0) { } return result; } private static string FormatSeverityLabel(stri