Skip to content

Getting Started

ifBars edited this page Jan 24, 2026 · 3 revisions

Getting Started with MLVScan.Core

This guide will help you integrate MLVScan.Core into your application for malware detection in Unity mod assemblies.

Installation

Via NuGet Package Manager

dotnet add package MLVScan.Core

Via Package Manager Console (Visual Studio)

Install-Package MLVScan.Core

Via .csproj File

<ItemGroup>
  <PackageReference Include="MLVScan.Core" Version="1.0.0" />
</ItemGroup>

Basic Usage

Simple File Scanning

The simplest way to scan a mod file:

using MLVScan;
using MLVScan.Services;

// Create scanner with default rules
var rules = RuleFactory.CreateDefaultRules();
var scanner = new AssemblyScanner(rules);

// Scan a file
var findings = scanner.Scan("path/to/mod.dll");

// Process results
foreach (var finding in findings)
{
    Console.WriteLine($"[{finding.Severity}] {finding.Description}");
    Console.WriteLine($"  Location: {finding.Location}");
    
    if (finding.HasCallChain)
    {
        Console.WriteLine($"  Call Chain: {finding.CallChain!.Nodes.Count} nodes");
        Console.WriteLine(finding.CallChain!.ToDetailedDescription());
    }
    else if (!string.IsNullOrEmpty(finding.CodeSnippet))
    {
        Console.WriteLine($"  Code: {finding.CodeSnippet}");
    }
}

Stream-Based Scanning

For web applications or in-memory scenarios:

using var stream = File.OpenRead("mod.dll");
var findings = scanner.Scan(stream, "mod.dll");

This is particularly useful for:

  • Web file uploads
  • In-memory assembly analysis
  • Network-streamed content

Understanding Scan Results

ScanFinding Structure

public class ScanFinding
{
    public string Description { get; set; }    // What was detected
    public Severity Severity { get; set; }     // Threat level
    public string Location { get; set; }       // Where in the code
    public string CodeSnippet { get; set; }    // IL instructions (optional)
    public string RuleId { get; set; }         // Which rule triggered this
    public bool HasCallChain { get; }          // Does this have a call chain?
    public CallChain? CallChain { get; set; }  // Full attack path (if available)
}

Note: AssemblyScanner automatically includes:

  • Traditional rule-based detection
  • Call graph analysis for attack path visibility
  • Data flow analysis for multi-step attack pattern recognition

See Call Graph Analysis for details on using call chains.
See Data Flow Analysis for details on data flow tracking.

Severity Levels

public enum Severity
{
    Low = 0,      // Minor suspicious patterns
    Medium = 1,   // Potentially dangerous
    High = 2,     // Dangerous behaviors
    Critical = 3  // Highly dangerous activities
}

Interpreting Results

var findings = scanner.Scan("suspicious_mod.dll");

// Group by severity
var critical = findings.Where(f => f.Severity == Severity.Critical);
var high = findings.Where(f => f.Severity == Severity.High);

// Decision logic
if (critical.Any())
{
    Console.WriteLine("CRITICAL THREAT DETECTED - DO NOT LOAD");
}
else if (high.Count() > 3)
{
    Console.WriteLine("Multiple high-severity issues - investigate carefully");
}

Advanced Configuration

Custom Scan Configuration

using MLVScan.Models;

var config = new ScanConfig
{
    EnableMultiSignalDetection = true,  // Reduce false positives
    DetectAssemblyMetadata = true       // Check assembly attributes
};

var scanner = new AssemblyScanner(rules, config);

Custom Logging

Implement IScanLogger for platform-specific logging:

using MLVScan.Abstractions;

public class MyLogger : IScanLogger
{
    public void Log(string message)
    {
        // Your logging implementation
        MyLoggingFramework.Info(message);
    }
    
    public void LogWarning(string message)
    {
        MyLoggingFramework.Warn(message);
    }
    
    public void LogError(string message)
    {
        MyLoggingFramework.Error(message);
    }
}

// Use with scanner
var scanner = new AssemblyScanner(rules, config, logger: new MyLogger());

Platform-Specific Assembly Resolution

For platforms that need to resolve game assemblies:

using Mono.Cecil;
using MLVScan.Abstractions;

public class MyResolverProvider : IAssemblyResolverProvider
{
    private readonly string _gameDirectory;
    
    public MyResolverProvider(string gameDirectory)
    {
        _gameDirectory = gameDirectory;
    }
    
    public IAssemblyResolver CreateResolver()
    {
        var resolver = new DefaultAssemblyResolver();
        resolver.AddSearchDirectory(Path.Combine(_gameDirectory, "Managed"));
        resolver.AddSearchDirectory(Path.Combine(_gameDirectory, "MelonLoader"));
        return resolver;
    }
}

// Use with scanner
var resolverProvider = new MyResolverProvider(@"C:\Games\MyGame");
var scanner = new AssemblyScanner(rules, config, resolverProvider);

Practical Examples

Example 1: Web File Upload Scanner

public async Task<ScanResult> ScanUploadedFile(IFormFile file)
{
    var rules = RuleFactory.CreateDefaultRules();
    var scanner = new AssemblyScanner(rules);
    
    using var stream = file.OpenReadStream();
    var findings = scanner.Scan(stream, file.FileName);
    
    return new ScanResult
    {
        FileName = file.FileName,
        IsClean = !findings.Any(f => f.Severity >= Severity.High),
        Findings = findings.ToList()
    };
}

Example 2: Batch Mod Scanner

public Dictionary<string, List<ScanFinding>> ScanAllMods(string modsDirectory)
{
    var rules = RuleFactory.CreateDefaultRules();
    var scanner = new AssemblyScanner(rules);
    var results = new Dictionary<string, List<ScanFinding>>();
    
    var modFiles = Directory.GetFiles(modsDirectory, "*.dll");
    
    foreach (var modFile in modFiles)
    {
        try
        {
            var findings = scanner.Scan(modFile).ToList();
            if (findings.Any())
            {
                results[modFile] = findings;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error scanning {modFile}: {ex.Message}");
        }
    }
    
    return results;
}

Example 3: Custom Rule Set

// Create custom rule set for specific needs
public List<IScanRule> CreateStrictRules()
{
    return new List<IScanRule>
    {
        new Shell32Rule(),
        new ProcessStartRule(),
        new LoadFromStreamRule(),
        new DataExfiltrationRule(),
        new PersistenceRule()
        // Only check for critical threats
    };
}

var scanner = new AssemblyScanner(CreateStrictRules());

SHA256 Hash Calculation

You can use .NET's built-in cryptography for hash verification:

using System.Security.Cryptography;

public string CalculateHash(string filePath)
{
    using var sha256 = SHA256.Create();
    using var stream = File.OpenRead(filePath);
    var hash = sha256.ComputeHash(stream);
    return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}

// Use for whitelisting
var fileHash = CalculateHash("mod.dll");
var whitelist = new[] { "3918e145...", "8e6dd194..." };

if (whitelist.Contains(fileHash))
{
    Console.WriteLine("Mod is whitelisted - skipping scan");
}
else
{
    var findings = scanner.Scan("mod.dll");
}

Error Handling

Always wrap scanning in try-catch:

try
{
    var findings = scanner.Scan("mod.dll");
    // Process findings
}
catch (BadImageFormatException)
{
    Console.WriteLine("Not a valid .NET assembly");
}
catch (FileNotFoundException)
{
    Console.WriteLine("File not found");
}
catch (Exception ex)
{
    Console.WriteLine($"Scan error: {ex.Message}");
}

Multi-Signal Detection

MLVScan.Core uses multi-signal detection to reduce false positives. Some patterns are only flagged when combined with others:

// Example: Base64 alone might not trigger
// But Base64 + Process.Start will

var config = new ScanConfig
{
    EnableMultiSignalDetection = true  // Enable contextual analysis
};

This helps avoid flagging legitimate uses of reflection, encoding, etc.

Performance Considerations

Scan Once, Cache Results

private Dictionary<string, List<ScanFinding>> _scanCache = new();

public List<ScanFinding> GetScanResults(string modPath)
{
    if (_scanCache.ContainsKey(modPath))
    {
        return _scanCache[modPath];
    }
    
    var findings = scanner.Scan(modPath).ToList();
    _scanCache[modPath] = findings;
    return findings;
}

Parallel Scanning

For scanning multiple files:

var results = modFiles.AsParallel()
    .Select(file => new
    {
        File = file,
        Findings = scanner.Scan(file).ToList()
    })
    .Where(r => r.Findings.Any())
    .ToDictionary(r => r.File, r => r.Findings);

Next Steps

Common Questions

Q: Does MLVScan.Core execute the assembly being scanned?
A: No, it uses static analysis only via Mono.Cecil. Assemblies are never executed.

Q: Can I use this with non-Unity assemblies?
A: Yes, it works with any .NET assembly, though it's optimized for Unity mod detection.

Q: How do I add custom detection rules?
A: See Detection Rules for creating custom rules.

Q: What happens if a scan fails?
A: Exceptions are thrown for invalid assemblies. Always wrap scans in try-catch.

Related Resources