MLVScan.Core
1.9.1
dotnet add package MLVScan.Core --version 1.9.1
NuGet\Install-Package MLVScan.Core -Version 1.9.1
<PackageReference Include="MLVScan.Core" Version="1.9.1" />
<PackageVersion Include="MLVScan.Core" Version="1.9.1" />
<PackageReference Include="MLVScan.Core" />
paket add MLVScan.Core --version 1.9.1
#r "nuget: MLVScan.Core, 1.9.1"
#:package MLVScan.Core@1.9.1
#addin nuget:?package=MLVScan.Core&version=1.9.1
#tool nuget:?package=MLVScan.Core&version=1.9.1
MLVScan.Core
MLVScan.Core is a cross-platform malware detection engine for .NET assemblies, powered by IL analysis with Mono.Cecil. It serves as the scanning backbone for MLVScan (MelonLoader, BepInEx 5.x, and BepInEx 6.x Mono/Il2Cpp), MLVScan.DevCLI, and MLVScan.Web through the @mlvscan/wasm-core package.
Why MLVScan?
Most modders download mods from trusted sites like Thunderstore or NexusMods, where files are typically already scanned by VirusTotal. This catches most plain viruses, but sophisticated threats often slip through.
MLVScan acts as your second line of defense. It specifically targets:
- Virus loaders: malicious DLLs designed to download additional payloads.
- Obfuscation techniques: code that hides its true purpose from standard AV.
- Stealthy payloads: threats that do not match known signatures.
Think of it as the checkpoint after VirusTotal: catching what traditional scanners miss.
Installation
dotnet add package MLVScan.Core
Quick Usage
using MLVScan;
using MLVScan.Models.Dto;
using MLVScan.Services;
var rules = RuleFactory.CreateDefaultRules();
var scanner = new AssemblyScanner(rules);
var assemblyPath = "path/to/suspicious.dll";
var assemblyBytes = File.ReadAllBytes(assemblyPath);
var findings = scanner.Scan(assemblyPath).ToList();
var result = ScanResultMapper.ToDto(
findings,
Path.GetFileName(assemblyPath),
assemblyBytes,
new ScanResultOptions
{
Platform = "my-tool",
PlatformVersion = "1.0.0"
});
Console.WriteLine($"{result.Disposition?.Classification}: {result.Disposition?.Headline}");
if (result.ThreatFamilies?.Count > 0)
{
foreach (var family in result.ThreatFamilies)
{
Console.WriteLine($"Matched family: {family.DisplayName} ({family.FamilyId})");
}
}
foreach (var finding in result.Findings)
{
Console.WriteLine($"[{finding.Severity}] {finding.RuleId}: {finding.Description}");
}
The scanner emits rule findings as the foundational evidence, but the primary verdict comes from the threat-intel layer: matched threatFamilies and the final disposition built on top of those findings.
A Suspicious disposition can carry blockingRecommended: false. Currently this is limited to a verified local restart script: a bounded IL analysis binds the written script to its encoded, detached PowerShell launcher. The accepted script waits for the current process and launches a literal steam://run/<app-id> URI, with optional logging, bounded delays/retry, and non-recursive *.delete cleanup. The original execution findings remain visible. This is advisory review, not a clean verdict or a guarantee about other code in the assembly.
Unknown script operations, unresolved launch bindings, additional high-risk findings, suspicious data flows, incomplete analysis, or known threat-family matches prevent this advisory classification. The recognizer does not use file hashes, mod names, method names, or game-specific IDs. Unsupported restart implementations retain the existing classification.
Process argument resolution follows bounded Encoding.GetBytes/Convert.ToBase64String, Convert.FromBase64String/Encoding.GetString, and string replacement chains. Recovered PowerShell encoded commands participate in detection and appear in finding descriptions, including CLI output. Runtime-dependent values remain explicitly partial; competing local assignments retain their possible values instead of selecting the nearest store. Recovery has size, work, and depth limits and never executes sample code. This improves evidence coverage; it is not a guarantee that every runtime command can be reconstructed.
For the supported detached PowerShell launcher, a proven binding to a local WriteAllText also exposes the written script body to detection and reporting. Script binding and restart approval are separate: a bound script containing additional operations stays visible and must still pass the closed restart grammar before it can receive an advisory disposition.
Conflicting helper returns and observed mutable-field writes retain bounded alternatives, including unresolved values. Supported encoding transforms preserve those alternatives separately. Field observations do not establish receiver identity or runtime write order; search limits are reported explicitly. Possible dangerous process targets remain detection evidence even when another alternative names a legitimate tool.
Deep analysis modes
ScanConfig.DeepScanMode controls when larger, still finite analysis budgets are used:
using MLVScan.Models;
var config = new ScanConfig { DeepScanMode = DeepScanMode.RetryOnIncomplete };
var scanner = new AssemblyScanner(RuleFactory.CreateDefaultRules(), config);
var findings = scanner.Scan(assemblyPath).ToList();
var modeUsed = scanner.LastScanUsedDeepAnalysis ? "deep" : "standard";
Disabled is the default single pass. RetryOnIncomplete runs a second pass when data-flow analysis or process value recovery reaches a configured limit. Always uses deep budgets from the start. Deep analysis increases call depth, data-flow budgets, and process value recovery budgets; it can take longer and can still report incomplete analysis if a higher limit is reached. Hosts should report deep as the result scan mode when LastScanUsedDeepAnalysis is true.
Process recovery defaults to 4,096 work units, depth 32, and 32 alternatives. Deep mode raises these to at least 65,536, 64, and 128 respectively. MaxValueResolutionWork, MaxValueResolutionDepth, and MaxValueResolutionAlternatives configure these finite budgets. Conflicting returns and mutable field writes remain uncertain in every mode; increasing a budget never makes an ambiguous value concrete.
ValueResolutionBudget is an advanced diagnostic for partial process-value recovery. It triggers retry mode and retains the candidates recovered so far. It does not, by itself, establish malicious behavior or mean that assembly instructions were skipped. Deep analysis can still reach a limit or encounter values that depend on runtime input.
Documentation
Complete documentation is available in the MLVScan.Core docs.
Features
- Platform agnostic: works on Windows, Linux, and WebAssembly.
- Multi-signal detection: context-aware analysis reduces false positives.
- Stream support: scan files from memory without writing to disk.
- Deep behavior analysis: correlates practical decode, load, and execute behavior chains in compiled code.
Licensed under GPL-3.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Mono.Cecil (>= 0.11.6)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.9.1 | 0 | 10/28/2026 |
| 1.9.0 | 498 | 9/25/2026 |
| 1.8.3 | 132 | 9/23/2026 |
| 1.8.2 | 156 | 9/20/2026 |
| 1.8.0 | 383 | 8/28/2026 |
| 1.7.2 | 979 | 6/20/2026 |
| 1.7.1 | 229 | 6/13/2026 |
| 1.7.0 | 280 | 6/1/2026 |
| 1.6.0 | 191 | 5/28/2026 |
| 1.5.0 | 275 | 5/18/2026 |
| 1.4.1 | 289 | 5/5/2026 |
| 1.4.0 | 369 | 4/17/2026 |
| 1.3.8 | 305 | 4/4/2026 |
| 1.3.7 | 170 | 4/3/2026 |
| 1.3.6 | 302 | 3/23/2026 |
| 1.3.5 | 140 | 3/23/2026 |
| 1.3.4 | 152 | 3/23/2026 |
| 1.3.3 | 171 | 3/21/2026 |
| 1.3.2 | 144 | 3/21/2026 |
| 1.3.1 | 152 | 3/20/2026 |