-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add BepInEx platform support #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4f8f826
78b8f21
83ab177
065534d
0a45086
7590f5d
e207206
e0f263c
168ca7d
d6dcf96
a35f822
2a24166
51c3601
ad0ca67
2fef523
29b669b
e5e0fc8
033d9de
3f94267
147b6e2
3d24b8e
bdb590b
bcfca00
617b47d
5eceddd
7987c23
98ab2b2
2ab298d
1a0eaf5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| name: Build MLVScan | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ "master", "main" ] | ||
| pull_request: | ||
| branches: [ "master", "main" ] | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup .NET | ||
| uses: actions/setup-dotnet@v4 | ||
| with: | ||
| dotnet-version: '8.0.x' | ||
|
|
||
| - name: Install Mono (for ILRepack) | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y mono-complete | ||
|
|
||
| # MelonLoader | ||
| - name: Restore MelonLoader dependencies | ||
| run: dotnet restore MLVScan.csproj -p:Configuration=MelonLoader | ||
|
|
||
| - name: Build MelonLoader | ||
| run: dotnet build MLVScan.csproj -c MelonLoader --no-restore | ||
|
|
||
| - name: Upload MelonLoader Artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: MLVScan.MelonLoader | ||
| path: bin/MelonLoader/**/MLVScan.MelonLoader.dll | ||
|
|
||
| # BepInEx 5 | ||
| - name: Restore BepInEx dependencies | ||
| run: dotnet restore MLVScan.csproj -p:Configuration=BepInEx | ||
|
|
||
| - name: Build BepInEx | ||
| run: dotnet build MLVScan.csproj -c BepInEx --no-restore | ||
|
|
||
| - name: Upload BepInEx Artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: MLVScan.BepInEx | ||
| path: bin/BepInEx/**/MLVScan.BepInEx.dll | ||
|
|
||
| # BepInEx 6.x Mono | ||
| - name: Restore BepInEx6Mono dependencies | ||
| run: dotnet restore MLVScan.csproj -p:Configuration=BepInEx6Mono | ||
|
|
||
| - name: Build BepInEx6Mono | ||
| run: dotnet build MLVScan.csproj -c BepInEx6Mono --no-restore | ||
|
|
||
| - name: Upload BepInEx6Mono Artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: MLVScan.BepInEx6.Mono | ||
| path: bin/BepInEx6Mono/**/MLVScan.BepInEx6.Mono.dll | ||
|
|
||
| # BepInEx 6.x IL2CPP | ||
| - name: Restore BepInEx6IL2CPP dependencies | ||
| run: dotnet restore MLVScan.csproj -p:Configuration=BepInEx6IL2CPP | ||
|
|
||
| - name: Build BepInEx6IL2CPP | ||
| run: dotnet build MLVScan.csproj -c BepInEx6IL2CPP --no-restore | ||
|
|
||
| - name: Upload BepInEx6IL2CPP Artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: MLVScan.BepInEx6.IL2CPP | ||
| path: bin/BepInEx6IL2CPP/**/MLVScan.BepInEx6.IL2CPP.dll | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| using MLVScan.Models; | ||
|
|
||
| namespace MLVScan.Abstractions | ||
| { | ||
| /// <summary> | ||
| /// Abstraction for configuration management across different mod platforms. | ||
| /// MelonLoader uses MelonPreferences (INI-based), BepInEx uses JSON files. | ||
| /// </summary> | ||
| public interface IConfigManager | ||
| { | ||
| /// <summary> | ||
| /// Gets the current configuration. | ||
| /// </summary> | ||
| ScanConfig Config { get; } | ||
|
|
||
| /// <summary> | ||
| /// Loads configuration from persistent storage. | ||
| /// Creates default configuration if none exists. | ||
| /// </summary> | ||
| ScanConfig LoadConfig(); | ||
|
|
||
| /// <summary> | ||
| /// Saves configuration to persistent storage. | ||
| /// </summary> | ||
| void SaveConfig(ScanConfig config); | ||
|
|
||
| /// <summary> | ||
| /// Checks if a file hash is in the whitelist. | ||
| /// </summary> | ||
| bool IsHashWhitelisted(string hash); | ||
|
|
||
| /// <summary> | ||
| /// Gets all whitelisted hashes. | ||
| /// </summary> | ||
| string[] GetWhitelistedHashes(); | ||
|
|
||
| /// <summary> | ||
| /// Sets the whitelisted hashes (normalizes and deduplicates). | ||
| /// </summary> | ||
| void SetWhitelistedHashes(string[] hashes); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| namespace MLVScan.Abstractions | ||
| { | ||
| /// <summary> | ||
| /// Abstraction for platform-specific paths and environment info. | ||
| /// MelonLoader uses MelonEnvironment, BepInEx uses BepInEx.Paths. | ||
| /// </summary> | ||
| public interface IPlatformEnvironment | ||
| { | ||
| /// <summary> | ||
| /// Gets the game's root directory. | ||
| /// </summary> | ||
| string GameRootDirectory { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the directory where plugins/mods are stored. | ||
| /// MelonLoader: Mods/ and Plugins/ | ||
| /// BepInEx: BepInEx/plugins/ | ||
| /// </summary> | ||
| string[] PluginDirectories { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the directory for MLVScan's own data (reports, disabled info, etc.). | ||
| /// </summary> | ||
| string DataDirectory { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the directory for scan reports. | ||
| /// </summary> | ||
| string ReportsDirectory { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the managed assemblies directory (Unity DLLs, game code). | ||
| /// </summary> | ||
| string ManagedDirectory { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the path to the MLVScan assembly itself (for self-exclusion). | ||
| /// </summary> | ||
| string SelfAssemblyPath { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the platform name for display/logging. | ||
| /// </summary> | ||
| string PlatformName { get; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using BepInEx; | ||
| using BepInEx.Logging; | ||
| using Mono.Cecil; | ||
| using MLVScan.BepInEx; | ||
| using MLVScan.BepInEx.Adapters; | ||
|
|
||
| namespace MLVScan.BepInEx5 | ||
| { | ||
| /// <summary> | ||
| /// BepInEx 5.x preloader patcher that scans plugins for malicious patterns | ||
| /// before the chainloader initializes them. | ||
| /// </summary> | ||
| public static class BepInEx5Patcher | ||
| { | ||
| private static ManualLogSource _logger; | ||
|
|
||
| /// <summary> | ||
| /// Default whitelist for known-safe BepInEx ecosystem plugins. | ||
| /// </summary> | ||
| private static readonly string[] DefaultWhitelistedHashes = | ||
| [ | ||
| // BepInEx ecosystem - known safe plugins | ||
| "8c0735f521d0fa785bf81b2e627a93042362b736ebc2c4c7ac425276b49fa692", | ||
| "9f86b196ffc845bdbc85192054e2876388ce1294b5a880459c93cbed7de2ae9d", | ||
| "bc67dab59532d0daca129e574c87d43b24a0b63ccb7312ccd25e0d7c4887784c", | ||
| "f1f3ff967bdb8f63a4bfd878255890f6393af37d3cc357babb6b504d9473ee06", | ||
| "d034d0e941deb47ea6b5ee8ca288bdb1d0bb25475dfba02cb61f6eadf0fa448e", | ||
| "e28b71abefdb5c2e90ea2d9e3c79bdff95f8173d08022732f62f35d2c328895d", | ||
| "bd5ec0343880b528ef190afe91778d172a239a625929dc176492eddc5c66cc31", | ||
| "503f851721ffacc7839e42d7c6c8a7c39fa2cea6e70a480b8bad822064d65aa0", | ||
| "184386c0f5f5bae6b63c96b73e312d3f39eba0d0ca81de3e3bd574ef389d1e29" | ||
| ]; | ||
|
|
||
| /// <summary> | ||
| /// Required: Declares which assemblies to patch. | ||
| /// Empty = we don't patch game assemblies, just use Initialize() as entry point. | ||
| /// </summary> | ||
| public static IEnumerable<string> TargetDLLs { get; } = Array.Empty<string>(); | ||
|
|
||
| /// <summary> | ||
| /// Required: Patching method (no-op - we don't modify game code). | ||
| /// </summary> | ||
| public static void Patch(AssemblyDefinition assembly) { } | ||
|
|
||
| /// <summary> | ||
| /// Called before patching - our main entry point. | ||
| /// Runs BEFORE the chainloader loads any plugins. | ||
| /// </summary> | ||
| public static void Initialize() | ||
| { | ||
| _logger = Logger.CreateLogSource("MLVScan"); | ||
|
|
||
| try | ||
| { | ||
| _logger.LogInfo("MLVScan preloader patcher initializing..."); | ||
| _logger.LogInfo($"Plugin directory: {Paths.PluginPath}"); | ||
|
|
||
| // Create platform environment | ||
| var environment = new BepInExPlatformEnvironment(); | ||
|
|
||
| // Load or create configuration | ||
| var configManager = new BepInExConfigManager(_logger, DefaultWhitelistedHashes); | ||
| var config = configManager.LoadConfig(); | ||
|
|
||
| // Create adapters | ||
| var scanLogger = new BepInExScanLogger(_logger); | ||
| var resolverProvider = new BepInExAssemblyResolverProvider(); | ||
|
|
||
| // Create scanner and disabler | ||
| var pluginScanner = new BepInExPluginScanner( | ||
| scanLogger, | ||
| resolverProvider, | ||
| config, | ||
| configManager, | ||
| environment); | ||
|
|
||
| var pluginDisabler = new BepInExPluginDisabler(scanLogger, config); | ||
| var reportGenerator = new BepInExReportGenerator(_logger, config); | ||
|
|
||
| // Scan all plugins | ||
| var scanResults = pluginScanner.ScanAllPlugins(); | ||
|
|
||
| if (scanResults.Count > 0) | ||
| { | ||
| // Disable suspicious plugins | ||
| var disabledPlugins = pluginDisabler.DisableSuspiciousPlugins(scanResults); | ||
|
|
||
| // Generate reports for disabled plugins | ||
| if (disabledPlugins.Count > 0) | ||
| { | ||
| reportGenerator.GenerateReports(disabledPlugins, scanResults); | ||
|
|
||
| _logger.LogWarning($"MLVScan blocked {disabledPlugins.Count} suspicious plugin(s)."); | ||
| _logger.LogWarning("Check BepInEx/MLVScan/Reports/ for details."); | ||
| } | ||
| } | ||
| else if (!config.EnableAutoScan) | ||
| { | ||
| _logger.LogInfo("Automatic scanning is disabled in configuration."); | ||
| } | ||
| else | ||
| { | ||
| _logger.LogInfo("No suspicious plugins detected."); | ||
ifBars marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
Comment on lines
+82
to
+106
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent This patcher calls Current behavior here: scanning happens even when 🔧 Suggested fix to match BepInEx6MonoPatcher pattern- // Scan all plugins
- var scanResults = pluginScanner.ScanAllPlugins();
-
- if (scanResults.Count > 0)
+ if (!config.EnableAutoScan)
+ {
+ _logger.LogInfo("Auto-scan is disabled. Skipping plugin scan.");
+ }
+ else
{
- // Disable suspicious plugins
- var disabledPlugins = pluginDisabler.DisableSuspiciousPlugins(scanResults);
+ var scanResults = pluginScanner.ScanAllPlugins();
- // Generate reports for disabled plugins
- if (disabledPlugins.Count > 0)
+ if (scanResults.Count > 0)
{
- reportGenerator.GenerateReports(disabledPlugins, scanResults);
+ var disabledPlugins = pluginDisabler.DisableSuspiciousPlugins(scanResults);
- _logger.LogWarning($"MLVScan blocked {disabledPlugins.Count} suspicious plugin(s).");
- _logger.LogWarning("Check BepInEx/MLVScan/Reports/ for details.");
+ if (disabledPlugins.Count > 0)
+ {
+ reportGenerator.GenerateReports(disabledPlugins, scanResults);
+
+ _logger.LogWarning($"MLVScan blocked {disabledPlugins.Count} suspicious plugin(s).");
+ _logger.LogWarning("Check BepInEx/MLVScan/Reports/ for details.");
+ }
+ }
+ else
+ {
+ _logger.LogInfo("No suspicious plugins detected.");
}
}
- else if (!config.EnableAutoScan)
- {
- _logger.LogInfo("Automatic scanning is disabled in configuration.");
- }
- else
- {
- _logger.LogInfo("No suspicious plugins detected.");
- }🤖 Prompt for AI Agents |
||
|
|
||
| _logger.LogInfo("MLVScan preloader scan complete."); | ||
| } | ||
ifBars marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| catch (Exception ex) | ||
| { | ||
| _logger?.LogError($"MLVScan initialization failed: {ex}"); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Called after all patching and assembly loading is complete. | ||
| /// </summary> | ||
| public static void Finish() | ||
| { | ||
| // Optional: cleanup, final summary logging | ||
| _logger?.LogDebug("MLVScan patcher finished."); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.