diff --git a/README.md b/README.md index d24fa13f..2ade977f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ BitMono is a free, open-source C# obfuscator that was initially designed and int BitMono uses [AsmResolver][asmresolver] instead of [dnlib][dnlib] (which we used in the past) for handling assemblies. If you have questions or issues, please let us know [here][bitmono_issues]. Download the latest version of BitMono [here][bitmono_releases]. -You can also use BitMono as an engine to build custom obfuscators. It is built using dependency injection (DI) using [Autofac][autofac_repo] and follows the latest C# best practices. +You can also use BitMono as an engine to build custom obfuscators. It is built using dependency injection (DI) with a lightweight custom container based on [MinIoC][minioc_repo] (we used [Autofac][autofac_repo] in the past) and follows the latest C# best practices.

protectionSettings, - IOptions criticalsSettings, - IOptions obfuscationSettings, - IServiceProvider serviceProvider) : base(serviceProvider) + ProtectionSettings protectionSettings, + CriticalsSettings criticalsSettings, + ObfuscationSettings obfuscationSettings, + IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { - _protectionSettings = protectionSettings.Value; - _criticalsSettings = criticalsSettings.Value; - _obfuscationSettings = obfuscationSettings.Value; - } + _protectionSettings = protectionSettings; + _criticalsSettings = criticalsSettings; + _obfuscationSettings = obfuscationSettings; + } } \ No newline at end of file diff --git a/docs/source/developers/first-protection.rst b/docs/source/developers/first-protection.rst index aab4d762..3e4d3230 100644 --- a/docs/source/developers/first-protection.rst +++ b/docs/source/developers/first-protection.rst @@ -18,10 +18,10 @@ Create your protection in the ``BitMono.Protections`` namespace. public class StandardProtection : Protection { // Inject services right here - public StandardProtection(IServiceProvider serviceProvider) : base(serviceProvider) + public StandardProtection(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } - + public override Task ExecuteAsync() { // All protection are intended to be async, so you can simply await your things, or if you don't have, diff --git a/docs/source/faq/disable-path-masking.rst b/docs/source/faq/disable-path-masking.rst index 1b109809..8a68ba7c 100644 --- a/docs/source/faq/disable-path-masking.rst +++ b/docs/source/faq/disable-path-masking.rst @@ -1,58 +1,11 @@ How to disable path masking? ============================ -You're probably getting a message with the file/directory or just a path ``(***\things)``, and you might have the same folder twice somewhere, and you need to see the full path without masking if this is what you're looking for, all instructions how to do that are provided here. +.. note:: -Open-up ``logging.json`` in the root of the downloaded BitMono, edit this file, and remove this: + Path masking was a feature in older versions of BitMono that used Serilog for logging. + Since BitMono now uses a lightweight custom logger, path masking is no longer applied by default. -.. code-block:: json +In current versions of BitMono, file paths are displayed in full without any masking. If you're seeing masked paths like ``(***\things)``, you may be using an older version of BitMono. - "Enrich": [ - { - "Name": "WithSensitiveDataMasking", - "Args": { - "options": { - "MaskValue": "***\\", - "MaskProperties": [ "path", "directory", "file" ], - "MaskingOperators": [ "BitMono.Host.Extensions.PathMaskingOperator, BitMono.Host" ] - } - } - }, - ], - - -So, after edit ``logging.json`` looks like this: - -.. code-block:: json - - { - "Serilog": { - "Using": [ - "Serilog", - "Serilog.Sinks.Console", - "Serilog.Sinks.File", - "Serilog.Sinks.Async", - "Serilog.Enrichers.Sensitive" - ], - "WriteTo": [ - { - "Name": "Async", - "Args": { - "configure": [ - { - "Name": "File", - "Args": { - "path": "logs/bitmono-{{date}}.log", - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}][{SourceContext}] {Message:lj}{NewLine}{Exception}" - } - } - ] - } - } - ], - "Enrich": [ - "FromLogContext" - ], - "MinimumLevel": "Debug" - } - } \ No newline at end of file +To get full path visibility, simply update to the latest version of BitMono from the `releases page `_. diff --git a/docs/source/usage/how-to-use.rst b/docs/source/usage/how-to-use.rst index e690c164..e8d413f7 100644 --- a/docs/source/usage/how-to-use.rst +++ b/docs/source/usage/how-to-use.rst @@ -247,8 +247,7 @@ After importing, your project will contain: ├── BitMono.CLI.exe # The actual obfuscation tool ├── protections.json # Protection settings ├── obfuscation.json # Obfuscation settings - ├── criticals.json # What not to obfuscate - └── logging.json # Logging configuration + └── criticals.json # What not to obfuscate Configuration ~~~~~~~~~~~~~ diff --git a/src/BitMono.API/Configuration/IBitMonoCriticalsConfiguration.cs b/src/BitMono.API/Configuration/IBitMonoCriticalsConfiguration.cs deleted file mode 100644 index 5fb3a1e1..00000000 --- a/src/BitMono.API/Configuration/IBitMonoCriticalsConfiguration.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace BitMono.API.Configuration; - -public interface IBitMonoCriticalsConfiguration : IConfigurationAccessor -{ -} \ No newline at end of file diff --git a/src/BitMono.API/Configuration/IBitMonoObfuscationConfiguration.cs b/src/BitMono.API/Configuration/IBitMonoObfuscationConfiguration.cs deleted file mode 100644 index 3a7576d5..00000000 --- a/src/BitMono.API/Configuration/IBitMonoObfuscationConfiguration.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace BitMono.API.Configuration; - -public interface IBitMonoObfuscationConfiguration : IConfigurationAccessor -{ -} \ No newline at end of file diff --git a/src/BitMono.API/Configuration/IBitMonoProtectionsConfiguration.cs b/src/BitMono.API/Configuration/IBitMonoProtectionsConfiguration.cs deleted file mode 100644 index 01302ca5..00000000 --- a/src/BitMono.API/Configuration/IBitMonoProtectionsConfiguration.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace BitMono.API.Configuration; - -public interface IBitMonoProtectionsConfiguration : IConfigurationAccessor -{ -} \ No newline at end of file diff --git a/src/BitMono.API/Configuration/IConfigurationAccessor.cs b/src/BitMono.API/Configuration/IConfigurationAccessor.cs deleted file mode 100644 index ef6c3321..00000000 --- a/src/BitMono.API/Configuration/IConfigurationAccessor.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BitMono.API.Configuration; - -public interface IConfigurationAccessor -{ - IConfiguration Configuration { get; } -} \ No newline at end of file diff --git a/src/BitMono.API/Configuration/JsonConfigurationAccessor.cs b/src/BitMono.API/Configuration/JsonConfigurationAccessor.cs deleted file mode 100644 index cb9a5f68..00000000 --- a/src/BitMono.API/Configuration/JsonConfigurationAccessor.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace BitMono.API.Configuration; - -public class JsonConfigurationAccessor : IConfigurationAccessor -{ - protected JsonConfigurationAccessor(string file) - { - Configuration = new ConfigurationBuilder() - .AddJsonFile(file, false, true) - .Build(); - } - - public IConfiguration Configuration { get; } -} \ No newline at end of file diff --git a/src/BitMono.API/GlobalUsings.cs b/src/BitMono.API/GlobalUsings.cs index c92a78db..b1375f86 100644 --- a/src/BitMono.API/GlobalUsings.cs +++ b/src/BitMono.API/GlobalUsings.cs @@ -1,6 +1,4 @@ global using AsmResolver.DotNet; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; global using NullGuard; global using System; global using System.Collections.Generic; @@ -8,4 +6,5 @@ global using System.Threading.Tasks; global using BitMono.API.Protections; global using JetBrains.Annotations; -global using IModule = Autofac.Core.IModule; \ No newline at end of file +global using BitMono.Shared.DependencyInjection; +global using BitMono.Shared.Logging; \ No newline at end of file diff --git a/src/BitMono.CLI/GlobalUsings.cs b/src/BitMono.CLI/GlobalUsings.cs index 68713f56..ef0ad594 100644 --- a/src/BitMono.CLI/GlobalUsings.cs +++ b/src/BitMono.CLI/GlobalUsings.cs @@ -1,28 +1,24 @@ -global using Autofac; global using BitMono.CLI.Modules; global using BitMono.Host; +global using BitMono.Host.Extensions; +global using BitMono.Host.Ioc; global using BitMono.Host.Modules; global using BitMono.Obfuscation; -global using System; -global using System.Diagnostics; -global using System.Diagnostics.CodeAnalysis; -global using System.IO; -global using System.Threading; -global using System.Threading.Tasks; -global using BitMono.Host.Extensions; -global using BitMono.Host.Configurations; global using BitMono.Obfuscation.Files; global using BitMono.Obfuscation.Starter; +global using BitMono.Shared; +global using BitMono.Shared.Configuration; +global using BitMono.Shared.DependencyInjection; +global using BitMono.Shared.Logging; global using BitMono.Shared.Models; global using BitMono.Utilities.Paths; global using CommandLine; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Options; -global using Microsoft.Extensions.Configuration; -global using Pocket.Extensions; -global using Serilog; +global using BitMono.Shared.Extensions; +global using System; global using System.Collections.Generic; +global using System.Diagnostics; +global using System.Diagnostics.CodeAnalysis; +global using System.IO; global using System.Linq; -global using BitMono.Shared; -global using Serilog.Configuration; -global using ILogger = Serilog.ILogger; \ No newline at end of file +global using System.Threading; +global using System.Threading.Tasks; diff --git a/src/BitMono.CLI/Modules/LoggerConfiguratorExtensions.cs b/src/BitMono.CLI/Modules/LoggerConfiguratorExtensions.cs deleted file mode 100644 index c3272c20..00000000 --- a/src/BitMono.CLI/Modules/LoggerConfiguratorExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BitMono.CLI.Modules; - -internal static class LoggerConfiguratorExtensions -{ - private const string OutputTemplate = - "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}][{SourceContext}] {Message:lj}{NewLine}{Exception}"; - public static LoggerConfiguration AddConsoleLogger(this LoggerSinkConfiguration source) - { - return source.Async(configure => configure.Console(outputTemplate: OutputTemplate)); - } -} \ No newline at end of file diff --git a/src/BitMono.CLI/Modules/OptionsObfuscationNeedsFactory.cs b/src/BitMono.CLI/Modules/OptionsObfuscationNeedsFactory.cs index ca87f31e..44f89c40 100644 --- a/src/BitMono.CLI/Modules/OptionsObfuscationNeedsFactory.cs +++ b/src/BitMono.CLI/Modules/OptionsObfuscationNeedsFactory.cs @@ -29,13 +29,11 @@ public OptionsObfuscationNeedsFactory(string[] args) { if (options.ObfuscationFile != null && File.Exists(options.ObfuscationFile)) { - var obfuscationConfig = new BitMonoObfuscationConfiguration(options.ObfuscationFile); - obfuscationSettings = obfuscationConfig.Configuration.Get(); + obfuscationSettings = SettingsLoader.Load(options.ObfuscationFile); } else if (File.Exists(KnownConfigNames.Obfuscation)) { - var obfuscationConfig = new BitMonoObfuscationConfiguration(); - obfuscationSettings = obfuscationConfig.Configuration.Get(); + obfuscationSettings = SettingsLoader.Load(KnownConfigNames.Obfuscation); } if (obfuscationSettings != null && options.NoWatermark) diff --git a/src/BitMono.CLI/Modules/ReadlineObfuscationNeedsFactory.cs b/src/BitMono.CLI/Modules/ReadlineObfuscationNeedsFactory.cs index a9ad4fcc..e261abed 100644 --- a/src/BitMono.CLI/Modules/ReadlineObfuscationNeedsFactory.cs +++ b/src/BitMono.CLI/Modules/ReadlineObfuscationNeedsFactory.cs @@ -36,14 +36,13 @@ public ObfuscationNeeds Create(CancellationToken cancellationToken) { if (File.Exists(obfuscationFile ?? KnownConfigNames.Obfuscation)) { - var obfuscationConfig = new BitMonoObfuscationConfiguration(obfuscationFile); - obfuscationSettings = obfuscationConfig.Configuration.Get(); + obfuscationSettings = SettingsLoader.Load(obfuscationFile ?? KnownConfigNames.Obfuscation); } if (File.Exists(KnownConfigNames.Protections)) { - var protectionsConfig = new BitMonoProtectionsConfiguration(); - protectionSettings = protectionsConfig.Configuration.Get()?.Protections; + var loadedSettings = SettingsLoader.Load(KnownConfigNames.Protections); + protectionSettings = loadedSettings?.Protections; } } catch (Exception ex) diff --git a/src/BitMono.CLI/Program.cs b/src/BitMono.CLI/Program.cs index 9c8b6a60..cb3b54ee 100644 --- a/src/BitMono.CLI/Program.cs +++ b/src/BitMono.CLI/Program.cs @@ -8,13 +8,13 @@ internal class Program $"BitMono v{FileVersionInfo.GetVersionInfo(typeof(Program).Assembly.Location).FileVersion}"; private static readonly string AsciiArt = $""" - ___ _ __ __ ___ - / _ )(_) /_/ |/ /__ ___ ___ - / _ / / __/ /|_/ / _ \/ _ \/ _ \ - /____/_/\__/_/ /_/\___/_//_/\___/ - https://github.com/sunnamed434/BitMono - {BitMonoFileVersionText} - """; + ___ _ __ __ ___ + / _ )(_) /_/ |/ /__ ___ ___ + / _ / / __/ /|_/ / _ \/ _ \/ _ \ + /____/_/\__/_/ /_/\___/_//_/\___/ + https://github.com/sunnamed434/BitMono + {BitMonoFileVersionText} + """; private static async Task Main(string[] args) { @@ -33,21 +33,17 @@ private static async Task Main(string[] args) } var module = new BitMonoModule( - configureContainer => configureContainer.AddProtections(), - configureServices => configureServices.AddConfigurations( - protectionSettings: needs.ProtectionSettings, - criticalsFile: needs.CriticalsFile, - obfuscationFile: needs.ObfuscationFile, - loggingFile: needs.LoggingFile, - protectionsFile: needs.ProtectionsFile, - obfuscationSettings: needs.ObfuscationSettings), - configureLogger => configureLogger.WriteTo.AddConsoleLogger(), - loggingFile: needs.LoggingFile); + configureContainer: container => container.AddProtections(), + obfuscationSettings: needs.ObfuscationSettings, + protectionSettings: needs.ProtectionSettings, + criticalsFile: needs.CriticalsFile, + obfuscationFile: needs.ObfuscationFile, + protectionsFile: needs.ProtectionsFile); var app = new BitMonoApplication().RegisterModule(module); - await using var serviceProvider = await app.BuildAsync(CancellationToken); + var serviceProvider = await app.BuildAsync(CancellationToken); - var obfuscation = serviceProvider.GetRequiredService>().Value; + var obfuscation = serviceProvider.GetRequiredService(); var logger = serviceProvider .GetRequiredService() .ForContext(); @@ -116,4 +112,4 @@ private static void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) CancellationTokenSource.Cancel(); e.Cancel = true; } -} \ No newline at end of file +} diff --git a/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs index 87fa276b..d2208ef0 100644 --- a/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs @@ -6,9 +6,9 @@ public class CriticalBaseTypesCriticalAnalyzer : ICriticalAnalyzer criticals) + public CriticalBaseTypesCriticalAnalyzer(CriticalsSettings criticalsSettings) { - _criticalsSettings = criticals.Value; + _criticalsSettings = criticalsSettings; } public bool NotCriticalToMakeChanges(TypeDefinition type) diff --git a/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs index 229bf131..ab9bea31 100644 --- a/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs @@ -4,9 +4,9 @@ public class CriticalInterfacesCriticalAnalyzer : ICriticalAnalyzer criticals) + public CriticalInterfacesCriticalAnalyzer(CriticalsSettings criticalsSettings) { - _criticalsSettings = criticals.Value; + _criticalsSettings = criticalsSettings; } public bool NotCriticalToMakeChanges(TypeDefinition type) diff --git a/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs index 4da58d8f..c341ce41 100644 --- a/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs @@ -4,9 +4,9 @@ public class CriticalMethodsCriticalAnalyzer : ICriticalAnalyzer criticals) + public CriticalMethodsCriticalAnalyzer(CriticalsSettings criticalsSettings) { - _criticalsSettings = criticals.Value; + _criticalsSettings = criticalsSettings; } public bool NotCriticalToMakeChanges(MethodDefinition method) diff --git a/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs index a7806891..d1038339 100644 --- a/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs @@ -4,9 +4,9 @@ public class CriticalMethodsStartsWithAnalyzer : ICriticalAnalyzer criticals) + public CriticalMethodsStartsWithAnalyzer(CriticalsSettings criticalsSettings) { - _criticalsSettings = criticals.Value; + _criticalsSettings = criticalsSettings; } public bool NotCriticalToMakeChanges(MethodDefinition method) diff --git a/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs index ad793399..8deb6e0e 100644 --- a/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs @@ -6,9 +6,9 @@ public class ModelAttributeCriticalAnalyzer : ICriticalAnalyzer criticals) + public ModelAttributeCriticalAnalyzer(CriticalsSettings criticalsSettings) { - _criticalsSettings = criticals.Value; + _criticalsSettings = criticalsSettings; } public bool NotCriticalToMakeChanges(IHasCustomAttribute customAttribute) diff --git a/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs index b911fd56..f705f5ab 100644 --- a/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs @@ -35,9 +35,9 @@ private enum MemberType All } - public ReflectionCriticalAnalyzer(IOptions obfuscation) + public ReflectionCriticalAnalyzer(ObfuscationSettings obfuscationSettings) { - _obfuscationSettings = obfuscation.Value; + _obfuscationSettings = obfuscationSettings; _argumentTracer = new ArgumentTracer(); _cachedMethods = []; _cachedFields = []; diff --git a/src/BitMono.Core/Analyzing/SerializableBitCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/SerializableBitCriticalAnalyzer.cs index 183a1d90..2db2bcd6 100644 --- a/src/BitMono.Core/Analyzing/SerializableBitCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/SerializableBitCriticalAnalyzer.cs @@ -5,9 +5,9 @@ public class SerializableBitCriticalAnalyzer : ICriticalAnalyzer { private readonly ObfuscationSettings _obfuscationSettings; - public SerializableBitCriticalAnalyzer(IOptions obfuscation) + public SerializableBitCriticalAnalyzer(ObfuscationSettings obfuscationSettings) { - _obfuscationSettings = obfuscation.Value; + _obfuscationSettings = obfuscationSettings; } public bool NotCriticalToMakeChanges(TypeDefinition type) diff --git a/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs index bb4188b9..5ab325b8 100644 --- a/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs @@ -4,9 +4,9 @@ public class SpecificNamespaceCriticalAnalyzer : ICriticalAnalyzer obfuscation) + public SpecificNamespaceCriticalAnalyzer(ObfuscationSettings obfuscationSettings) { - _obfuscationSettings = obfuscation.Value; + _obfuscationSettings = obfuscationSettings; } private static string? GetNamespace(IMetadataMember member) diff --git a/src/BitMono.Core/Configuration/JsonConfigurationExtensions.cs b/src/BitMono.Core/Configuration/JsonConfigurationExtensions.cs deleted file mode 100644 index c968e7ca..00000000 --- a/src/BitMono.Core/Configuration/JsonConfigurationExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BitMono.Core.Configuration; - -public static class JsonConfigurationExtensions -{ - public static IConfigurationBuilder AddJsonFileEx(this IConfigurationBuilder builder, - Action configure) - { - return builder.Add(configure); - } -} \ No newline at end of file diff --git a/src/BitMono.Core/Configuration/JsonConfigurationProviderEx.cs b/src/BitMono.Core/Configuration/JsonConfigurationProviderEx.cs deleted file mode 100644 index 4a38cf58..00000000 --- a/src/BitMono.Core/Configuration/JsonConfigurationProviderEx.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace BitMono.Core.Configuration; - -public class JsonConfigurationProviderEx : FileConfigurationProvider -{ - private readonly JsonConfigurationSourceEx _source; - private const string ParseMethodName = "Parse"; - private const string ParserTypeName = "Microsoft.Extensions.Configuration.Json.JsonConfigurationFileParser"; - private static readonly Type ParserType = typeof(JsonConfigurationProvider).Assembly.GetType(ParserTypeName); - private static readonly MethodInfo ParseMethodInfo = ParserType.GetMethod(ParseMethodName, - BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - - public JsonConfigurationProviderEx(JsonConfigurationSourceEx source) : base(source) - { - _source = source; - } - - public override void Load(Stream stream) - { - using var streamReader = new StreamReader(stream, true); - var text = streamReader.ReadToEnd(); - PreProcessJson(ref text); - using var memoryStream = new MemoryStream(); - using var streamWriter = new StreamWriter(memoryStream, streamReader.CurrentEncoding); - streamWriter.Write(text); - streamWriter.Flush(); - memoryStream.Seek(0L, SeekOrigin.Begin); - try - { - var parseMethod = (Func>)Delegate.CreateDelegate( - typeof(Func>), ParseMethodInfo); - Data = parseMethod.Invoke(memoryStream); - } - catch (JsonException ex) - { - throw new FormatException("Could not parse the JSON file", ex); - } - } - private void PreProcessJson(ref string json) - { - if (_source.Variables == null) - { - return; - } - json = _source.Variables.Aggregate(json, - (current, keyValuePair) => current.Replace("{{" + keyValuePair.Key + "}}", keyValuePair.Value)); - } -} \ No newline at end of file diff --git a/src/BitMono.Core/Configuration/JsonConfigurationSourceEx.cs b/src/BitMono.Core/Configuration/JsonConfigurationSourceEx.cs deleted file mode 100644 index 278f1320..00000000 --- a/src/BitMono.Core/Configuration/JsonConfigurationSourceEx.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace BitMono.Core.Configuration; - -public class JsonConfigurationSourceEx : FileConfigurationSource -{ - public IDictionary? Variables { get; set; } - - public override IConfigurationProvider Build(IConfigurationBuilder builder) - { - EnsureDefaults(builder); - return new JsonConfigurationProviderEx(this); - } -} \ No newline at end of file diff --git a/src/BitMono.Core/GlobalUsings.cs b/src/BitMono.Core/GlobalUsings.cs index cae1f8c2..445f476a 100644 --- a/src/BitMono.Core/GlobalUsings.cs +++ b/src/BitMono.Core/GlobalUsings.cs @@ -1,22 +1,11 @@ -global using AsmResolver; +global using AsmResolver; global using AsmResolver.DotNet; -global using AsmResolver.DotNet.Cloning; -global using AsmResolver.DotNet.Signatures; -global using AsmResolver.PE.DotNet.Cil; -global using BitMono.Core.Extensions; -global using BitMono.Shared.Models; -global using System; -global using System.Collections.Generic; -global using System.Diagnostics.CodeAnalysis; -global using System.IO; -global using System.Linq; -global using System.Reflection; -global using System.Runtime.CompilerServices; -global using System.Threading; -global using System.Threading.Tasks; global using AsmResolver.DotNet.Builder; +global using AsmResolver.DotNet.Cloning; global using AsmResolver.DotNet.Code.Cil; global using AsmResolver.DotNet.Serialized; +global using AsmResolver.DotNet.Signatures; +global using AsmResolver.PE.DotNet.Cil; global using AsmResolver.PE.File; global using BitMono.API; global using BitMono.API.Analyzing; @@ -25,20 +14,29 @@ global using BitMono.Core.Analyzing; global using BitMono.Core.Attributes; global using BitMono.Core.Contexts; +global using BitMono.Core.Extensions; global using BitMono.Core.Factories; global using BitMono.Core.Renaming; global using BitMono.Core.Resolvers; global using BitMono.Core.Services; +global using BitMono.Shared.DependencyInjection; +global using BitMono.Shared.Logging; +global using BitMono.Shared.Models; global using BitMono.Utilities.AsmResolver; global using Echo.DataFlow; global using Echo.DataFlow.Analysis; global using Echo.Platforms.AsmResolver; global using JetBrains.Annotations; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.Configuration.Json; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Options; global using Newtonsoft.Json; -global using Pocket.Extensions; +global using BitMono.Shared.Extensions; +global using System; +global using System.Collections.Generic; +global using System.Diagnostics.CodeAnalysis; +global using System.IO; +global using System.Linq; +global using System.Reflection; +global using System.Runtime.CompilerServices; +global using System.Threading; +global using System.Threading.Tasks; global using FieldAttributes = AsmResolver.PE.DotNet.Metadata.Tables.FieldAttributes; -global using TypeAttributes = AsmResolver.PE.DotNet.Metadata.Tables.TypeAttributes; \ No newline at end of file +global using TypeAttributes = AsmResolver.PE.DotNet.Metadata.Tables.TypeAttributes; diff --git a/src/BitMono.Core/PackerProtection.cs b/src/BitMono.Core/PackerProtection.cs index d5f372f7..2045e7f6 100644 --- a/src/BitMono.Core/PackerProtection.cs +++ b/src/BitMono.Core/PackerProtection.cs @@ -2,7 +2,7 @@ namespace BitMono.Core; public abstract class PackerProtection : ProtectionBase, IPacker { - protected PackerProtection(IServiceProvider serviceProvider) : base(serviceProvider) + protected PackerProtection(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } } \ No newline at end of file diff --git a/src/BitMono.Core/PhaseProtection.cs b/src/BitMono.Core/PhaseProtection.cs index 77fa15b4..e1a66fb2 100644 --- a/src/BitMono.Core/PhaseProtection.cs +++ b/src/BitMono.Core/PhaseProtection.cs @@ -2,7 +2,7 @@ namespace BitMono.Core; public abstract class PhaseProtection : ProtectionBase, IPhaseProtection { - protected PhaseProtection(IServiceProvider serviceProvider) : base(serviceProvider) + protected PhaseProtection(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } } \ No newline at end of file diff --git a/src/BitMono.Core/PipelineProtection.cs b/src/BitMono.Core/PipelineProtection.cs index e3db1c45..9585c2ba 100644 --- a/src/BitMono.Core/PipelineProtection.cs +++ b/src/BitMono.Core/PipelineProtection.cs @@ -2,7 +2,7 @@ namespace BitMono.Core; public abstract class PipelineProtection : ProtectionBase, IPipelineProtection { - protected PipelineProtection(IServiceProvider serviceProvider) : base(serviceProvider) + protected PipelineProtection(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Core/Protection.cs b/src/BitMono.Core/Protection.cs index 5379fbf3..8f17ce77 100644 --- a/src/BitMono.Core/Protection.cs +++ b/src/BitMono.Core/Protection.cs @@ -2,7 +2,7 @@ namespace BitMono.Core; public abstract class Protection : ProtectionBase { - protected Protection(IServiceProvider serviceProvider) : base(serviceProvider) + protected Protection(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } } \ No newline at end of file diff --git a/src/BitMono.Core/ProtectionBase.cs b/src/BitMono.Core/ProtectionBase.cs index c2a05af8..31c3b2f5 100644 --- a/src/BitMono.Core/ProtectionBase.cs +++ b/src/BitMono.Core/ProtectionBase.cs @@ -3,9 +3,9 @@ namespace BitMono.Core; public abstract class ProtectionBase : IProtection { protected ProtectionContext Context { get; } - protected IServiceProvider ServiceProvider { get; } + protected IBitMonoServiceProvider ServiceProvider { get; } - protected ProtectionBase(IServiceProvider serviceProvider) + protected ProtectionBase(IBitMonoServiceProvider serviceProvider) { ServiceProvider = serviceProvider; Context = ServiceProvider diff --git a/src/BitMono.Core/Renaming/Renamer.cs b/src/BitMono.Core/Renaming/Renamer.cs index 71621815..bf2841b3 100644 --- a/src/BitMono.Core/Renaming/Renamer.cs +++ b/src/BitMono.Core/Renaming/Renamer.cs @@ -11,12 +11,12 @@ public class Renamer public Renamer( NameCriticalAnalyzer nameCriticalAnalyzer, SpecificNamespaceCriticalAnalyzer specificNamespaceCriticalAnalyzer, - IOptions configuration, + ObfuscationSettings obfuscationSettings, RandomNext randomNext) { _nameCriticalAnalyzer = nameCriticalAnalyzer; _specificNamespaceCriticalAnalyzer = specificNamespaceCriticalAnalyzer; - _obfuscationSettings = configuration.Value; + _obfuscationSettings = obfuscationSettings; _randomNext = randomNext; } diff --git a/src/BitMono.Core/Resolvers/CriticalAttributeResolver.cs b/src/BitMono.Core/Resolvers/CriticalAttributeResolver.cs index 772dad42..36a2b750 100644 --- a/src/BitMono.Core/Resolvers/CriticalAttributeResolver.cs +++ b/src/BitMono.Core/Resolvers/CriticalAttributeResolver.cs @@ -4,9 +4,9 @@ public class CriticalAttributeResolver : AttributeResolver criticals) + public CriticalAttributeResolver(CriticalsSettings criticalsSettings) { - _criticalsSettings = criticals.Value; + _criticalsSettings = criticalsSettings; } public override bool Resolve(string? feature, IHasCustomAttribute from, diff --git a/src/BitMono.Core/Resolvers/NoInliningMethodMemberResolver.cs b/src/BitMono.Core/Resolvers/NoInliningMethodMemberResolver.cs index ac9e77ca..6685549a 100644 --- a/src/BitMono.Core/Resolvers/NoInliningMethodMemberResolver.cs +++ b/src/BitMono.Core/Resolvers/NoInliningMethodMemberResolver.cs @@ -4,9 +4,9 @@ public class NoInliningMethodMemberResolver : IMemberResolver { private readonly ObfuscationSettings _obfuscationSettings; - public NoInliningMethodMemberResolver(IOptions obfuscation) + public NoInliningMethodMemberResolver(ObfuscationSettings obfuscationSettings) { - _obfuscationSettings = obfuscation.Value; + _obfuscationSettings = obfuscationSettings; } public bool Resolve(IProtection? protection, IMetadataMember member) diff --git a/src/BitMono.Core/Resolvers/ObfuscateAssemblyAttributeResolver.cs b/src/BitMono.Core/Resolvers/ObfuscateAssemblyAttributeResolver.cs index 31eecd09..19861a6e 100644 --- a/src/BitMono.Core/Resolvers/ObfuscateAssemblyAttributeResolver.cs +++ b/src/BitMono.Core/Resolvers/ObfuscateAssemblyAttributeResolver.cs @@ -6,9 +6,9 @@ public class ObfuscateAssemblyAttributeResolver : AttributeResolver configuration) + public ObfuscateAssemblyAttributeResolver(ObfuscationSettings obfuscationSettings) { - _obfuscationSettings = configuration.Value; + _obfuscationSettings = obfuscationSettings; _attributeNamespace = typeof(ObfuscateAssemblyAttribute).Namespace!; _attributeName = nameof(ObfuscateAssemblyAttribute); } diff --git a/src/BitMono.Core/Resolvers/ObfuscationAttributeResolver.cs b/src/BitMono.Core/Resolvers/ObfuscationAttributeResolver.cs index e41746cb..e9868e7d 100644 --- a/src/BitMono.Core/Resolvers/ObfuscationAttributeResolver.cs +++ b/src/BitMono.Core/Resolvers/ObfuscationAttributeResolver.cs @@ -6,9 +6,9 @@ public class ObfuscationAttributeResolver : AttributeResolver configuration) + public ObfuscationAttributeResolver(ObfuscationSettings obfuscationSettings) { - _obfuscationSettings = configuration.Value; + _obfuscationSettings = obfuscationSettings; _attributeNamespace = typeof(ObfuscationAttribute).Namespace!; _attributeName = nameof(ObfuscationAttribute); } diff --git a/src/BitMono.GlobalTool/BitMono.GlobalTool.csproj b/src/BitMono.GlobalTool/BitMono.GlobalTool.csproj index 02f9db60..228316b0 100644 --- a/src/BitMono.GlobalTool/BitMono.GlobalTool.csproj +++ b/src/BitMono.GlobalTool/BitMono.GlobalTool.csproj @@ -16,9 +16,6 @@ Modules/ObfuscationNeeds.cs - - Modules\LoggerConfiguratorExtensions.cs - Modules\ObfuscationNeedsFactory.cs diff --git a/src/BitMono.GlobalTool/GlobalUsings.cs b/src/BitMono.GlobalTool/GlobalUsings.cs index b3f7375a..031511fa 100644 --- a/src/BitMono.GlobalTool/GlobalUsings.cs +++ b/src/BitMono.GlobalTool/GlobalUsings.cs @@ -1,29 +1,25 @@ -global using Autofac; +global using BitMono.CLI; global using BitMono.CLI.Modules; global using BitMono.Host; -global using BitMono.CLI; +global using BitMono.Host.Extensions; +global using BitMono.Host.Ioc; global using BitMono.Host.Modules; global using BitMono.Obfuscation; -global using System; -global using System.Diagnostics; -global using System.Diagnostics.CodeAnalysis; -global using System.IO; -global using System.Threading; -global using System.Threading.Tasks; -global using BitMono.Host.Extensions; -global using BitMono.Host.Configurations; global using BitMono.Obfuscation.Files; -global using BitMono.Shared; global using BitMono.Obfuscation.Starter; +global using BitMono.Shared; +global using BitMono.Shared.Configuration; +global using BitMono.Shared.DependencyInjection; +global using BitMono.Shared.Logging; global using BitMono.Shared.Models; global using BitMono.Utilities.Paths; global using CommandLine; -global using System.Linq; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Options; -global using Microsoft.Extensions.Configuration; -global using Pocket.Extensions; -global using Serilog; +global using BitMono.Shared.Extensions; +global using System; global using System.Collections.Generic; -global using Serilog.Configuration; -global using ILogger = Serilog.ILogger; \ No newline at end of file +global using System.Diagnostics; +global using System.Diagnostics.CodeAnalysis; +global using System.IO; +global using System.Linq; +global using System.Threading; +global using System.Threading.Tasks; diff --git a/src/BitMono.GlobalTool/Program.cs b/src/BitMono.GlobalTool/Program.cs index 8c642db5..eec43720 100644 --- a/src/BitMono.GlobalTool/Program.cs +++ b/src/BitMono.GlobalTool/Program.cs @@ -1,4 +1,4 @@ -namespace BitMono.GlobalTool; +namespace BitMono.GlobalTool; internal class Program { @@ -24,30 +24,26 @@ private static async Task Main(string[] args) try { Console.Title = BitMonoFileVersionText; - + needs = new ObfuscationNeedsFactory(args).Create(CancellationToken); if (needs == null) { statusCode = KnownReturnStatuses.Failure; return statusCode; } - + var module = new BitMonoModule( - configureContainer => configureContainer.AddProtections(), - configureServices => configureServices.AddConfigurations( - protectionSettings: needs.ProtectionSettings, - criticalsFile: needs.CriticalsFile, - obfuscationFile: needs.ObfuscationFile, - loggingFile: needs.LoggingFile, - protectionsFile: needs.ProtectionsFile, - obfuscationSettings: needs.ObfuscationSettings), - configureLogger => configureLogger.WriteTo.AddConsoleLogger(), - loggingFile: needs.LoggingFile); + configureContainer: container => container.AddProtections(), + obfuscationSettings: needs.ObfuscationSettings, + protectionSettings: needs.ProtectionSettings, + criticalsFile: needs.CriticalsFile, + obfuscationFile: needs.ObfuscationFile, + protectionsFile: needs.ProtectionsFile); var app = new BitMonoApplication().RegisterModule(module); - await using var serviceProvider = await app.BuildAsync(CancellationToken); + var serviceProvider = await app.BuildAsync(CancellationToken); - var obfuscation = serviceProvider.GetRequiredService>().Value; + var obfuscation = serviceProvider.GetRequiredService(); var logger = serviceProvider .GetRequiredService() .ForContext(); @@ -110,4 +106,4 @@ private static void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) CancellationTokenSource.Cancel(); e.Cancel = true; } -} \ No newline at end of file +} diff --git a/src/BitMono.Host/BitMonoApplication.cs b/src/BitMono.Host/BitMonoApplication.cs index 18f1e17e..93a14223 100644 --- a/src/BitMono.Host/BitMonoApplication.cs +++ b/src/BitMono.Host/BitMonoApplication.cs @@ -1,36 +1,41 @@ -namespace BitMono.Host; +using BitMono.Host.Ioc; +using BitMono.Shared.DependencyInjection; +namespace BitMono.Host; + +///

+/// Main application class for building the BitMono DI container. +/// public class BitMonoApplication : IApplication { - private readonly ContainerBuilder _containerBuilder; + private readonly Container _container; private readonly List _modules; public BitMonoApplication() { - _containerBuilder = new ContainerBuilder(); + _container = new Container(); _modules = []; } - public IApplication Populate(IEnumerable descriptors) - { - _containerBuilder.Populate(descriptors); - return this; - } + /// public IApplication RegisterModule(IModule module) { _modules.Add(module); return this; } - public Task BuildAsync(CancellationToken cancellationToken) + + /// + public Task BuildAsync(CancellationToken cancellationToken) { foreach (var module in _modules) { cancellationToken.ThrowIfCancellationRequested(); - - _containerBuilder.RegisterModule(module); + module.Load(_container); } - var container = _containerBuilder.Build(); - var provider = new AutofacServiceProvider(container.Resolve()); - return Task.FromResult(provider); + + // Register the container itself as the service provider + _container.Register(_container).AsSingleton(); + + return Task.FromResult(_container); } -} \ No newline at end of file +} diff --git a/src/BitMono.Host/Configurations/BitMonoCriticalsConfiguration.cs b/src/BitMono.Host/Configurations/BitMonoCriticalsConfiguration.cs deleted file mode 100644 index c0041950..00000000 --- a/src/BitMono.Host/Configurations/BitMonoCriticalsConfiguration.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BitMono.Host.Configurations; - -public class BitMonoCriticalsConfiguration : JsonConfigurationAccessor, IBitMonoCriticalsConfiguration -{ - public BitMonoCriticalsConfiguration(string? file = null) : base(file ?? "criticals.json") - { - } -} \ No newline at end of file diff --git a/src/BitMono.Host/Configurations/BitMonoObfuscationConfiguration.cs b/src/BitMono.Host/Configurations/BitMonoObfuscationConfiguration.cs deleted file mode 100644 index 1793f65a..00000000 --- a/src/BitMono.Host/Configurations/BitMonoObfuscationConfiguration.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BitMono.Host.Configurations; - -public class BitMonoObfuscationConfiguration : JsonConfigurationAccessor, IBitMonoObfuscationConfiguration -{ - public BitMonoObfuscationConfiguration(string? file = null) : base(file ?? "obfuscation.json") - { - } -} \ No newline at end of file diff --git a/src/BitMono.Host/Configurations/BitMonoProtectionsConfiguration.cs b/src/BitMono.Host/Configurations/BitMonoProtectionsConfiguration.cs deleted file mode 100644 index a650027d..00000000 --- a/src/BitMono.Host/Configurations/BitMonoProtectionsConfiguration.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BitMono.Host.Configurations; - -public class BitMonoProtectionsConfiguration : JsonConfigurationAccessor, IBitMonoProtectionsConfiguration -{ - public BitMonoProtectionsConfiguration(string? file = null) : base(file ?? "protections.json") - { - } -} \ No newline at end of file diff --git a/src/BitMono.Host/Extensions/AutofacContainerBuilderExtensions.cs b/src/BitMono.Host/Extensions/AutofacContainerBuilderExtensions.cs deleted file mode 100644 index 03e01f43..00000000 --- a/src/BitMono.Host/Extensions/AutofacContainerBuilderExtensions.cs +++ /dev/null @@ -1,72 +0,0 @@ -namespace BitMono.Host.Extensions; - -[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")] -[SuppressMessage("ReSharper", "IdentifierTypo")] -public static class AutofacContainerBuilderExtensions -{ - private const string ProtectionsFileName = "BitMono.Protections.dll"; - private const string UnityFileName = "BitMono.Unity.dll"; - - public static ContainerBuilder AddProtections(this ContainerBuilder source, string? file = null) - { - var protectionsFilePath = file ?? Path.Combine(AppContext.BaseDirectory, ProtectionsFileName); - var rawData = File.ReadAllBytes(file ?? protectionsFilePath); - Assembly.Load(rawData); - - var unityFilePath = Path.Combine(AppContext.BaseDirectory, UnityFileName); - if (File.Exists(unityFilePath)) - { - var unityRawData = File.ReadAllBytes(unityFilePath); - Assembly.Load(unityRawData); - } - - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - source.RegisterAssemblyTypes(assemblies) - .PublicOnly() - .Where(x => x.GetInterface(nameof(IPhaseProtection)) == null && x.GetInterface(nameof(IProtection)) != null) - .OwnedByLifetimeScope() - .AsImplementedInterfaces() - .SingleInstance(); - return source; - } - public static ServiceCollection AddConfigurations(this ServiceCollection source, - ProtectionSettings? protectionSettings = null, string? criticalsFile = null, string? obfuscationFile = null, string? loggingFile = null, string? protectionsFile = null, ObfuscationSettings? obfuscationSettings = null) - { - var criticals = new BitMonoCriticalsConfiguration(criticalsFile); - source.AddOptions() - .Configure(criticals.Configuration); - - if (obfuscationSettings != null) - { - source.Configure(configure => - { - configure.Watermark = obfuscationSettings.Watermark; - configure.ClearCLI = obfuscationSettings.ClearCLI; - configure.ForceObfuscation = obfuscationSettings.ForceObfuscation; - configure.ReferencesDirectoryName = obfuscationSettings.ReferencesDirectoryName; - configure.OutputDirectoryName = obfuscationSettings.OutputDirectoryName; - configure.OpenFileDestinationInFileExplorer = obfuscationSettings.OpenFileDestinationInFileExplorer; - }); - } - else - { - var obfuscation = new BitMonoObfuscationConfiguration(obfuscationFile); - source.Configure(obfuscation.Configuration); - } - - if (protectionSettings != null) - { - source.Configure(configure => - { - configure.Protections = protectionSettings.Protections; - }); - } - else - { - var protections = new BitMonoProtectionsConfiguration(protectionsFile); - source.Configure(protections.Configuration); - } - - return source; - } -} \ No newline at end of file diff --git a/src/BitMono.Host/Extensions/ContainerExtensions.cs b/src/BitMono.Host/Extensions/ContainerExtensions.cs new file mode 100644 index 00000000..e7d94e5f --- /dev/null +++ b/src/BitMono.Host/Extensions/ContainerExtensions.cs @@ -0,0 +1,92 @@ +using BitMono.API.Protections; +using BitMono.Shared.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace BitMono.Host.Extensions; + +/// +/// Extension methods for configuring the BitMono container. +/// +[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")] +[SuppressMessage("ReSharper", "IdentifierTypo")] +public static class BitMonoContainerExtensions +{ + private const string ProtectionsFileName = "BitMono.Protections.dll"; + private const string UnityFileName = "BitMono.Unity.dll"; + + /// + /// Loads and registers protection assemblies. + /// + /// Container to register protections in + /// Optional path to protections DLL + /// Container for chaining + public static Container AddProtections(this Container container, string? file = null) + { + var protectionsFilePath = file ?? Path.Combine(AppContext.BaseDirectory, ProtectionsFileName); + var rawData = File.ReadAllBytes(protectionsFilePath); + Assembly.Load(rawData); + + var unityFilePath = Path.Combine(AppContext.BaseDirectory, UnityFileName); + if (File.Exists(unityFilePath)) + { + var unityRawData = File.ReadAllBytes(unityFilePath); + Assembly.Load(unityRawData); + } + + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + // Collect all protection types (excluding IPhaseProtection) + var protectionTypes = new List(); + foreach (var assembly in assemblies) + { + Type[] types; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + types = ex.Types.Where(t => t != null).ToArray()!; + } + + foreach (var type in types) + { + if (!type.IsClass || type.IsAbstract || !type.IsPublic) + continue; + + // Skip IPhaseProtection, only register IProtection + if (type.GetInterface(nameof(IPhaseProtection)) != null) + continue; + + if (type.GetInterface(nameof(IProtection)) != null) + { + protectionTypes.Add(type); + // Register the concrete type + container.Register(type, type).AsSingleton(); + } + } + } + + // Register the collection factory + container.Register>(() => + { + var list = new List(); + foreach (var protType in protectionTypes) + { + var instance = container.GetService(protType); + if (instance is IProtection protection) + { + list.Add(protection); + } + } + return list; + }).AsSingleton(); + + return container; + } +} diff --git a/src/BitMono.Host/Extensions/PathMaskingOperator.cs b/src/BitMono.Host/Extensions/PathMaskingOperator.cs deleted file mode 100644 index f67482b4..00000000 --- a/src/BitMono.Host/Extensions/PathMaskingOperator.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace BitMono.Host.Extensions; - -public class PathMaskingOperator : RegexMaskingOperator -{ - private readonly IPathWrapper _pathWrapper; - private readonly bool _combineMaskWithPath; - private const string PathPattern = - @"^(?:[a-zA-Z]\:|\\\\[\w-]+\\[\w-]+\$?|[\/][^\/\0]+)+(\\[^\\/:*?""<>|]*)*(\\?)?$"; - - public PathMaskingOperator(IPathWrapper pathWrapper, bool combineMaskWithPath = true) : base(PathPattern) - { - _pathWrapper = pathWrapper; - _combineMaskWithPath = combineMaskWithPath; - } - public PathMaskingOperator(bool combineMaskWithPath = true) : this(new PathWrapper(), combineMaskWithPath) - { - } - public PathMaskingOperator() : this(combineMaskWithPath: true) - { - } - - [SuppressMessage("ReSharper", "InvertIf")] - protected override string PreprocessMask(string mask, Match match) - { - if (_combineMaskWithPath) - { - var value = match.Value; - return _pathWrapper.IsDirectory(value) - ? mask + _pathWrapper.GetDirectoryName(value) - : mask + _pathWrapper.GetFileName(value); - } - return mask; - } -} \ No newline at end of file diff --git a/src/BitMono.Host/GlobalUsings.cs b/src/BitMono.Host/GlobalUsings.cs index 2a8d3f0d..8fcf3027 100644 --- a/src/BitMono.Host/GlobalUsings.cs +++ b/src/BitMono.Host/GlobalUsings.cs @@ -1,30 +1,23 @@ -global using Autofac; -global using Autofac.Core; -global using Autofac.Extensions.DependencyInjection; -global using BitMono.API.Configuration; -global using BitMono.Host.Configurations; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Serilog; -global using System; -global using System.Collections.Generic; -global using System.Diagnostics.CodeAnalysis; -global using System.IO; -global using System.Reflection; -global using System.Text.RegularExpressions; -global using System.Threading; -global using System.Threading.Tasks; global using BitMono.API; global using BitMono.API.Analyzing; global using BitMono.API.Protections; global using BitMono.API.Resolvers; -global using BitMono.Core.Configuration; global using BitMono.Core.Factories; global using BitMono.Core.Renaming; global using BitMono.Core.Resolvers; global using BitMono.Core.Services; -global using BitMono.Shared; global using BitMono.Host.Ioc; +global using BitMono.Shared; +global using BitMono.Shared.Configuration; +global using BitMono.Shared.DependencyInjection; +global using BitMono.Shared.Logging; global using BitMono.Shared.Models; -global using Serilog.Enrichers.Sensitive; -global using Module = Autofac.Module; \ No newline at end of file +global using System; +global using System.Collections.Generic; +global using System.Diagnostics.CodeAnalysis; +global using System.IO; +global using System.Linq; +global using System.Reflection; +global using System.Text.RegularExpressions; +global using System.Threading; +global using System.Threading.Tasks; diff --git a/src/BitMono.Host/Ioc/IApplication.cs b/src/BitMono.Host/Ioc/IApplication.cs index 9ab5c692..0db13c01 100644 --- a/src/BitMono.Host/Ioc/IApplication.cs +++ b/src/BitMono.Host/Ioc/IApplication.cs @@ -1,8 +1,23 @@ -namespace BitMono.Host.Ioc; +using BitMono.Shared.DependencyInjection; +namespace BitMono.Host.Ioc; + +/// +/// Interface for the application builder. +/// public interface IApplication { - IApplication Populate(IEnumerable descriptors); + /// + /// Registers a module with the application. + /// + /// Module to register + /// This application for chaining IApplication RegisterModule(IModule module); - Task BuildAsync(CancellationToken cancellationToken); -} \ No newline at end of file + + /// + /// Builds the container and returns the service provider. + /// + /// Cancellation token + /// Service provider + Task BuildAsync(CancellationToken cancellationToken); +} diff --git a/src/BitMono.Host/Ioc/IModule.cs b/src/BitMono.Host/Ioc/IModule.cs new file mode 100644 index 00000000..142ab937 --- /dev/null +++ b/src/BitMono.Host/Ioc/IModule.cs @@ -0,0 +1,15 @@ +using BitMono.Shared.DependencyInjection; + +namespace BitMono.Host.Ioc; + +/// +/// Interface for modules that configure the DI container. +/// +public interface IModule +{ + /// + /// Configures service registrations in the container. + /// + /// The container to configure + void Load(Container container); +} diff --git a/src/BitMono.Host/Modules/BitMonoModule.cs b/src/BitMono.Host/Modules/BitMonoModule.cs index d66a6b17..e8779c16 100644 --- a/src/BitMono.Host/Modules/BitMonoModule.cs +++ b/src/BitMono.Host/Modules/BitMonoModule.cs @@ -1,99 +1,94 @@ -namespace BitMono.Host.Modules; - -public class BitMonoModule : Module +using BitMono.API; +using BitMono.API.Analyzing; +using BitMono.API.Resolvers; +using BitMono.Core.Factories; +using BitMono.Core.Renaming; +using BitMono.Core.Services; +using BitMono.Host.Ioc; +using BitMono.Shared.Configuration; +using BitMono.Shared.DependencyInjection; +using BitMono.Shared.Logging; +using BitMono.Shared.Models; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using LoggerConfiguration = BitMono.Shared.Logging.LoggerConfiguration; +using ILogger = BitMono.Shared.Logging.ILogger; + +namespace BitMono.Host.Modules; + +public class BitMonoModule : IModule { - private const string DateVariableName = "date"; private const string DateTimeFormat = "yyyy-MM-dd-HH-mm-ss"; - private readonly Action? _configureContainer; - private readonly Action? _configureServices; + + private readonly Action? _configureContainer; private readonly Action? _configureLogger; - private readonly string? _loggingFile; + private readonly ObfuscationSettings? _obfuscationSettings; + private readonly ProtectionSettings? _protectionSettings; + private readonly string? _criticalsFile; + private readonly string? _obfuscationFile; + private readonly string? _protectionsFile; public BitMonoModule( - Action? configureContainer = null, - Action? configureServices = null, + Action? configureContainer = null, Action? configureLogger = null, - string? loggingFile = null) + ObfuscationSettings? obfuscationSettings = null, + ProtectionSettings? protectionSettings = null, + string? criticalsFile = null, + string? obfuscationFile = null, + string? protectionsFile = null) { _configureContainer = configureContainer; - _configureServices = configureServices; _configureLogger = configureLogger; - _loggingFile = loggingFile; + _obfuscationSettings = obfuscationSettings; + _protectionSettings = protectionSettings; + _criticalsFile = criticalsFile; + _obfuscationFile = obfuscationFile; + _protectionsFile = protectionsFile; } [SuppressMessage("ReSharper", "IdentifierTypo")] - protected override void Load(ContainerBuilder containerBuilder) + public void Load(Container container) { - _configureContainer?.Invoke(containerBuilder); + _configureContainer?.Invoke(container); - var loggingConfigurationRoot = new ConfigurationBuilder().AddJsonFileEx(configure => + var loggerConfiguration = new LoggerConfiguration { - configure.Path = _loggingFile ?? KnownConfigNames.Logging; - configure.Optional = false; - configure.Variables = new Dictionary - { - { - DateVariableName, - DateTime.Now.ToString(DateTimeFormat) - } - }; - configure.ResolveFileProvider(); - }).Build(); - - var loggerConfiguration = new LoggerConfiguration() - .ReadFrom.Configuration(loggingConfigurationRoot); - + WriteToConsole = true, + WriteToFile = true, + LogFilePath = Path.Combine("logs", $"bitmono-{DateTime.Now.ToString(DateTimeFormat)}.log"), + MinimumLevel = LogLevel.Debug + }; _configureLogger?.Invoke(loggerConfiguration); - var logger = loggerConfiguration.CreateLogger(); - containerBuilder.Register(_ => logger); - - var serviceCollection = new ServiceCollection(); - _configureServices?.Invoke(serviceCollection); - - containerBuilder.RegisterType() - .As() - .OwnedByLifetimeScope() - .SingleInstance(); + var logger = new Logger(loggerConfiguration); + container.Register(logger).AsSingleton(); - containerBuilder.RegisterType() - .AsSelf() - .OwnedByLifetimeScope() - .SingleInstance(); + var criticalsSettings = SettingsLoader.Load( + _criticalsFile ?? KnownConfigNames.Criticals); + container.Register(criticalsSettings).AsSingleton(); - containerBuilder.RegisterType() - .AsSelf() - .OwnedByLifetimeScope() - .SingleInstance(); + var obfuscationSettings = _obfuscationSettings ?? + SettingsLoader.Load(_obfuscationFile ?? KnownConfigNames.Obfuscation); + container.Register(obfuscationSettings).AsSingleton(); - containerBuilder.Register(_ => RandomService.RandomNext) - .OwnedByLifetimeScope() - .SingleInstance(); + var protectionSettings = _protectionSettings ?? + SettingsLoader.Load(_protectionsFile ?? KnownConfigNames.Protections); + container.Register(protectionSettings).AsSingleton(); - containerBuilder.RegisterType() - .OwnedByLifetimeScope() - .SingleInstance(); + container.Register().AsSingleton(); + container.Register().AsSingleton(); + container.Register().AsSingleton(); + container.Register(() => RandomService.RandomNext).AsSingleton(); + container.Register().AsSingleton(); var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - containerBuilder.RegisterAssemblyTypes(assemblies) - .PublicOnly() - .Where(t => t.GetInterface(nameof(IMemberResolver)) != null) - .AsImplementedInterfaces() - .OwnedByLifetimeScope() - .SingleInstance(); - containerBuilder.RegisterAssemblyTypes(assemblies) - .PublicOnly() - .AsClosedTypesOf(typeof(ICriticalAnalyzer<>)) - .OwnedByLifetimeScope() - .SingleInstance(); + container.RegisterAssemblyTypes(assemblies, typeof(IMemberResolver)); - containerBuilder.RegisterAssemblyTypes(assemblies) - .PublicOnly() - .AsClosedTypesOf(typeof(IAttributeResolver<>)) - .OwnedByLifetimeScope() - .SingleInstance(); + container.RegisterClosedTypesOf(assemblies, typeof(ICriticalAnalyzer<>)); - containerBuilder.Populate(serviceCollection); + container.RegisterClosedTypesOf(assemblies, typeof(IAttributeResolver<>)); } -} \ No newline at end of file +} diff --git a/src/BitMono.Host/logging.json b/src/BitMono.Host/logging.json deleted file mode 100644 index 503cf98f..00000000 --- a/src/BitMono.Host/logging.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "Serilog": { - "Using": [ - "Serilog", - "Serilog.Sinks.Console", - "Serilog.Sinks.File", - "Serilog.Sinks.Async", - "Serilog.Enrichers.Sensitive" - ], - "WriteTo": [ - { - "Name": "Async", - "Args": { - "configure": [ - { - "Name": "File", - "Args": { - "path": "logs/bitmono-{{date}}.log", - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}][{SourceContext}] {Message:lj}{NewLine}{Exception}" - } - } - ] - } - } - ], - "Enrich": [ - { - "Name": "WithSensitiveDataMasking", - "Args": { - "options": { - "MaskValue": "***\\", - "MaskProperties": [ "path", "directory", "file" ], - "MaskingOperators": [ "BitMono.Host.Extensions.PathMaskingOperator, BitMono.Host" ] - } - } - }, - - "FromLogContext" - ], - "MinimumLevel": "Debug" - } -} \ No newline at end of file diff --git a/src/BitMono.Obfuscation/BitMonoObfuscator.cs b/src/BitMono.Obfuscation/BitMonoObfuscator.cs index b90b37a7..9aa5d0d3 100644 --- a/src/BitMono.Obfuscation/BitMonoObfuscator.cs +++ b/src/BitMono.Obfuscation/BitMonoObfuscator.cs @@ -1,8 +1,8 @@ -namespace BitMono.Obfuscation; +namespace BitMono.Obfuscation; public class BitMonoObfuscator { - private readonly IServiceProvider _serviceProvider; + private readonly IBitMonoServiceProvider _serviceProvider; private readonly StarterContext _context; private readonly IDataWriter _dataWriter; private readonly ObfuscationSettings _obfuscationSettings; @@ -19,7 +19,7 @@ public class BitMonoObfuscator private long _startTime; public BitMonoObfuscator( - IServiceProvider serviceProvider, + IBitMonoServiceProvider serviceProvider, StarterContext context, IDataWriter dataWriter, ObfuscationSettings obfuscationSettings, @@ -117,7 +117,7 @@ private void OutputCompatibilityIssues() } private void SortProtections() { - var protectionSettings = _serviceProvider.GetRequiredService>().Value.Protections!; + var protectionSettings = _serviceProvider.GetRequiredService().Protections!; var protections = _serviceProvider .GetRequiredService>() .ToList(); @@ -283,7 +283,7 @@ private async Task WriteModuleAsync() fileBuilder .CreateFile(_imageBuild!.ConstructedImage!) .Write(memoryStream); - + if (!string.IsNullOrEmpty(_obfuscationSettings.StrongNameKeyFile) && File.Exists(_obfuscationSettings.StrongNameKeyFile)) { _logger.Information("Applying strong name signature to PE image"); @@ -291,7 +291,7 @@ private async Task WriteModuleAsync() var signer = new StrongNameSigner(privateKey); signer.SignImage(memoryStream, _context.Module.Assembly.HashAlgorithm); } - + await _dataWriter.WriteAsync(_context.BitMonoContext.OutputFile, memoryStream.ToArray()); _logger.Information("The protected module was saved in {0}", _context.BitMonoContext.OutputDirectoryName); } @@ -323,4 +323,4 @@ private Task OnFailHandleAsync() _logger.Fatal("Obfuscation stopped! Something went wrong!"); return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/src/BitMono.Obfuscation/GlobalUsings.cs b/src/BitMono.Obfuscation/GlobalUsings.cs index f09040d2..9cf846a6 100644 --- a/src/BitMono.Obfuscation/GlobalUsings.cs +++ b/src/BitMono.Obfuscation/GlobalUsings.cs @@ -1,19 +1,7 @@ -global using AsmResolver; +global using AsmResolver; global using AsmResolver.DotNet; global using AsmResolver.DotNet.Builder; global using AsmResolver.DotNet.Serialized; -global using BitMono.Core.Extensions; -global using BitMono.Shared.Models; -global using System; -global using System.Collections.Generic; -global using System.Diagnostics; -global using System.Diagnostics.CodeAnalysis; -global using System.IO; -global using System.IO.Compression; -global using System.Linq; -global using System.Text; -global using System.Threading; -global using System.Threading.Tasks; global using AsmResolver.DotNet.Signatures; global using AsmResolver.PE; global using AsmResolver.PE.Builder; @@ -25,6 +13,7 @@ global using BitMono.API.Protections; global using BitMono.Core.Attributes; global using BitMono.Core.Contexts; +global using BitMono.Core.Extensions; global using BitMono.Core.Pipeline; global using BitMono.Core.Resolvers; global using BitMono.Core.Services; @@ -36,10 +25,20 @@ global using BitMono.Obfuscation.Referencing; global using BitMono.Obfuscation.Stripping; global using BitMono.Runtime; +global using BitMono.Shared.DependencyInjection; +global using BitMono.Shared.Logging; +global using BitMono.Shared.Models; global using BitMono.Utilities.AsmResolver; global using BitMono.Utilities.Runtime; global using BitMono.Utilities.Time; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Options; -global using Pocket.Extensions; -global using ILogger = Serilog.ILogger; \ No newline at end of file +global using BitMono.Shared.Extensions; +global using System; +global using System.Collections.Generic; +global using System.Diagnostics; +global using System.Diagnostics.CodeAnalysis; +global using System.IO; +global using System.IO.Compression; +global using System.Linq; +global using System.Text; +global using System.Threading; +global using System.Threading.Tasks; diff --git a/src/BitMono.Obfuscation/Starter/BitMonoStarter.cs b/src/BitMono.Obfuscation/Starter/BitMonoStarter.cs index 3801b0ae..c98fc502 100644 --- a/src/BitMono.Obfuscation/Starter/BitMonoStarter.cs +++ b/src/BitMono.Obfuscation/Starter/BitMonoStarter.cs @@ -1,16 +1,16 @@ -namespace BitMono.Obfuscation.Starter; +namespace BitMono.Obfuscation.Starter; public class BitMonoStarter { - private readonly IServiceProvider _serviceProvider; + private readonly IBitMonoServiceProvider _serviceProvider; private readonly ObfuscationSettings _obfuscationSettings; private readonly IEngineContextAccessor _engineContextAccessor; private readonly ILogger _logger; - public BitMonoStarter(IServiceProvider serviceProvider) + public BitMonoStarter(IBitMonoServiceProvider serviceProvider) { _serviceProvider = serviceProvider; - _obfuscationSettings = serviceProvider.GetRequiredService>().Value; + _obfuscationSettings = serviceProvider.GetRequiredService(); _engineContextAccessor = serviceProvider.GetRequiredService(); _logger = serviceProvider .GetRequiredService() @@ -51,4 +51,4 @@ public Task StartAsync(IncompleteFileInfo info, CancellationToken cancella new ModuleFactory(File.ReadAllBytes(info.FilePath), _obfuscationSettings, new LogErrorListener(_logger, _obfuscationSettings), _logger), new FileDataWriter(), new AutomaticPathReferencesDataResolver(info.ReferencesDirectoryPath), cancellationToken); } -} \ No newline at end of file +} diff --git a/src/BitMono.Protections/AntiDe4dot.cs b/src/BitMono.Protections/AntiDe4dot.cs index 68601d4c..e7eaf7ec 100644 --- a/src/BitMono.Protections/AntiDe4dot.cs +++ b/src/BitMono.Protections/AntiDe4dot.cs @@ -2,7 +2,7 @@ public class AntiDe4dot : Protection { - public AntiDe4dot(IServiceProvider serviceProvider) : base(serviceProvider) + public AntiDe4dot(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/AntiDebugBreakpoints.cs b/src/BitMono.Protections/AntiDebugBreakpoints.cs index bd6ee1c6..33e98154 100644 --- a/src/BitMono.Protections/AntiDebugBreakpoints.cs +++ b/src/BitMono.Protections/AntiDebugBreakpoints.cs @@ -3,7 +3,7 @@ [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class AntiDebugBreakpoints : Protection { - public AntiDebugBreakpoints(IServiceProvider serviceProvider) : base(serviceProvider) + public AntiDebugBreakpoints(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/AntiDecompiler.cs b/src/BitMono.Protections/AntiDecompiler.cs index 94b784de..34a337b5 100644 --- a/src/BitMono.Protections/AntiDecompiler.cs +++ b/src/BitMono.Protections/AntiDecompiler.cs @@ -3,7 +3,7 @@ [RuntimeMonikerMono] public class AntiDecompiler : PipelineProtection { - public AntiDecompiler(IServiceProvider serviceProvider) : base(serviceProvider) + public AntiDecompiler(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } @@ -19,7 +19,7 @@ public override IEnumerable PopulatePipeline() [ProtectionName(nameof(AntiDnSpyAnalyzer))] public class AntiDnSpyAnalyzer : PhaseProtection { - public AntiDnSpyAnalyzer(IServiceProvider serviceProvider) : base(serviceProvider) + public AntiDnSpyAnalyzer(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/AntiILdasm.cs b/src/BitMono.Protections/AntiILdasm.cs index ad00cb7d..ea364c60 100644 --- a/src/BitMono.Protections/AntiILdasm.cs +++ b/src/BitMono.Protections/AntiILdasm.cs @@ -2,7 +2,7 @@ public class AntiILdasm : Protection { - public AntiILdasm(IServiceProvider serviceProvider) : base(serviceProvider) + public AntiILdasm(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/BillionNops.cs b/src/BitMono.Protections/BillionNops.cs index 427b2c09..363b7575 100644 --- a/src/BitMono.Protections/BillionNops.cs +++ b/src/BitMono.Protections/BillionNops.cs @@ -4,7 +4,7 @@ public class BillionNops : Protection { private readonly Renamer _renamer; - public BillionNops(Renamer renamer, IServiceProvider serviceProvider) : base(serviceProvider) + public BillionNops(Renamer renamer, IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { _renamer = renamer; } diff --git a/src/BitMono.Protections/BitDecompiler.cs b/src/BitMono.Protections/BitDecompiler.cs index 5a35fa6e..a34822d7 100644 --- a/src/BitMono.Protections/BitDecompiler.cs +++ b/src/BitMono.Protections/BitDecompiler.cs @@ -3,7 +3,7 @@ namespace BitMono.Protections; [RuntimeMonikerMono] public class BitDecompiler : PackerProtection { - public BitDecompiler(IServiceProvider serviceProvider) : base(serviceProvider) + public BitDecompiler(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/BitDotNet.cs b/src/BitMono.Protections/BitDotNet.cs index 7916e0d5..a6f977ad 100644 --- a/src/BitMono.Protections/BitDotNet.cs +++ b/src/BitMono.Protections/BitDotNet.cs @@ -5,7 +5,7 @@ public class BitDotNet : PackerProtection { private const int PEHeaderWithExtraByteHex = 0x00014550; - public BitDotNet(IServiceProvider serviceProvider) : base(serviceProvider) + public BitDotNet(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/BitMethodDotnet.cs b/src/BitMono.Protections/BitMethodDotnet.cs index 7c1be061..19f2c29a 100644 --- a/src/BitMono.Protections/BitMethodDotnet.cs +++ b/src/BitMono.Protections/BitMethodDotnet.cs @@ -5,7 +5,7 @@ public class BitMethodDotnet : Protection { private readonly RandomNext _randomNext; - public BitMethodDotnet(RandomNext randomNext, IServiceProvider serviceProvider) : base(serviceProvider) + public BitMethodDotnet(RandomNext randomNext, IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { _randomNext = randomNext; } diff --git a/src/BitMono.Protections/BitMono.cs b/src/BitMono.Protections/BitMono.cs index 65851378..05530b09 100644 --- a/src/BitMono.Protections/BitMono.cs +++ b/src/BitMono.Protections/BitMono.cs @@ -3,7 +3,7 @@ namespace BitMono.Protections; [RuntimeMonikerMono] public class BitMono : PackerProtection { - public BitMono(IServiceProvider serviceProvider) : base(serviceProvider) + public BitMono(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/BitTimeDateStamp.cs b/src/BitMono.Protections/BitTimeDateStamp.cs index 47283077..0b6648df 100644 --- a/src/BitMono.Protections/BitTimeDateStamp.cs +++ b/src/BitMono.Protections/BitTimeDateStamp.cs @@ -2,7 +2,7 @@ public class BitTimeDateStamp : PackerProtection { - public BitTimeDateStamp(IServiceProvider serviceProvider) : base(serviceProvider) + public BitTimeDateStamp(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/CallToCalli.cs b/src/BitMono.Protections/CallToCalli.cs index 6ac61994..676cf5c7 100644 --- a/src/BitMono.Protections/CallToCalli.cs +++ b/src/BitMono.Protections/CallToCalli.cs @@ -3,7 +3,7 @@ [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class CallToCalli : Protection { - public CallToCalli(IServiceProvider serviceProvider) : base(serviceProvider) + public CallToCalli(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/DotNetHook.cs b/src/BitMono.Protections/DotNetHook.cs index 6da52a43..b8e85dc4 100644 --- a/src/BitMono.Protections/DotNetHook.cs +++ b/src/BitMono.Protections/DotNetHook.cs @@ -5,7 +5,7 @@ public class DotNetHook : Protection private readonly Renamer _renamer; private readonly RandomNext _randomNext; - public DotNetHook(Renamer renamer, RandomNext randomNext, IServiceProvider serviceProvider) : base(serviceProvider) + public DotNetHook(Renamer renamer, RandomNext randomNext, IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { _renamer = renamer; _randomNext = randomNext; diff --git a/src/BitMono.Protections/FullRenamer.cs b/src/BitMono.Protections/FullRenamer.cs index 72dfff91..5ef7198f 100644 --- a/src/BitMono.Protections/FullRenamer.cs +++ b/src/BitMono.Protections/FullRenamer.cs @@ -5,7 +5,7 @@ public class FullRenamer : Protection { private readonly Renamer _renamer; - public FullRenamer(Renamer renamer, IServiceProvider serviceProvider) : base(serviceProvider) + public FullRenamer(Renamer renamer, IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { _renamer = renamer; } diff --git a/src/BitMono.Protections/GlobalUsings.cs b/src/BitMono.Protections/GlobalUsings.cs index 4984bc2c..a96b2b64 100644 --- a/src/BitMono.Protections/GlobalUsings.cs +++ b/src/BitMono.Protections/GlobalUsings.cs @@ -19,6 +19,7 @@ global using BitMono.API.Protections; global using BitMono.Core; global using BitMono.Core.Attributes; +global using BitMono.Shared.DependencyInjection; global using BitMono.Core.Injection; global using BitMono.Core.Renaming; global using BitMono.Core.Services; diff --git a/src/BitMono.Protections/NoNamespaces.cs b/src/BitMono.Protections/NoNamespaces.cs index 94337749..2660982f 100644 --- a/src/BitMono.Protections/NoNamespaces.cs +++ b/src/BitMono.Protections/NoNamespaces.cs @@ -3,7 +3,7 @@ [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class NoNamespaces : Protection { - public NoNamespaces(IServiceProvider serviceProvider) : base(serviceProvider) + public NoNamespaces(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/ObjectReturnType.cs b/src/BitMono.Protections/ObjectReturnType.cs index 34285f8e..cc355dd0 100644 --- a/src/BitMono.Protections/ObjectReturnType.cs +++ b/src/BitMono.Protections/ObjectReturnType.cs @@ -3,7 +3,7 @@ [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class ObjectReturnType : Protection { - public ObjectReturnType(IServiceProvider serviceProvider) : base(serviceProvider) + public ObjectReturnType(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Protections/StringsEncryption.cs b/src/BitMono.Protections/StringsEncryption.cs index 4930490d..df3ac266 100644 --- a/src/BitMono.Protections/StringsEncryption.cs +++ b/src/BitMono.Protections/StringsEncryption.cs @@ -5,7 +5,7 @@ public class StringsEncryption : Protection { private readonly Renamer _renamer; - public StringsEncryption(Renamer renamer, IServiceProvider serviceProvider) : base(serviceProvider) + public StringsEncryption(Renamer renamer, IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { _renamer = renamer; } diff --git a/src/BitMono.Protections/UnmanagedString.cs b/src/BitMono.Protections/UnmanagedString.cs index 5ffee8d4..4b1acd06 100644 --- a/src/BitMono.Protections/UnmanagedString.cs +++ b/src/BitMono.Protections/UnmanagedString.cs @@ -5,7 +5,7 @@ [RuntimeMonikerNETFramework] public class UnmanagedString : Protection { - public UnmanagedString(IServiceProvider serviceProvider) : base(serviceProvider) + public UnmanagedString(IBitMonoServiceProvider serviceProvider) : base(serviceProvider) { } diff --git a/src/BitMono.Shared/BitMono.Shared.csproj b/src/BitMono.Shared/BitMono.Shared.csproj index 43233e53..2038417c 100644 --- a/src/BitMono.Shared/BitMono.Shared.csproj +++ b/src/BitMono.Shared/BitMono.Shared.csproj @@ -10,123 +10,18 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/BitMono.Shared/Configuration/SettingsLoader.cs b/src/BitMono.Shared/Configuration/SettingsLoader.cs new file mode 100644 index 00000000..f78f14b1 --- /dev/null +++ b/src/BitMono.Shared/Configuration/SettingsLoader.cs @@ -0,0 +1,68 @@ +using Newtonsoft.Json; + +namespace BitMono.Shared.Configuration; + +/// +/// Simple JSON-based settings loader. +/// +public static class SettingsLoader +{ + /// + /// Loads settings from a JSON file. + /// + /// Settings type + /// Path to the JSON file + /// Deserialized settings object, or new instance if file doesn't exist + public static T Load(string jsonPath) where T : new() + { + if (!File.Exists(jsonPath)) + return new T(); + + var json = File.ReadAllText(jsonPath); + return JsonConvert.DeserializeObject(json) ?? new T(); + } + + /// + /// Loads settings from a JSON file with variable substitution. + /// Variables in format {{variableName}} will be replaced. + /// + /// Settings type + /// Path to the JSON file + /// Dictionary of variable names and values to substitute + /// Deserialized settings object, or new instance if file doesn't exist + public static T LoadWithVariables(string jsonPath, Dictionary? variables = null) + where T : new() + { + if (!File.Exists(jsonPath)) + return new T(); + + var json = File.ReadAllText(jsonPath); + + if (variables != null) + { + foreach (var kvp in variables) + { + json = json.Replace("{{" + kvp.Key + "}}", kvp.Value); + } + } + + return JsonConvert.DeserializeObject(json) ?? new T(); + } + + /// + /// Saves settings to a JSON file. + /// + /// Settings type + /// Settings object to save + /// Path to save the JSON file + /// Whether to format with indentation + public static void Save(T settings, string jsonPath, bool indented = true) + { + var dir = Path.GetDirectoryName(jsonPath); + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + var json = JsonConvert.SerializeObject(settings, indented ? Formatting.Indented : Formatting.None); + File.WriteAllText(jsonPath, json); + } +} diff --git a/src/BitMono.Shared/DependencyInjection/Container.cs b/src/BitMono.Shared/DependencyInjection/Container.cs new file mode 100644 index 00000000..7f7ab24b --- /dev/null +++ b/src/BitMono.Shared/DependencyInjection/Container.cs @@ -0,0 +1,358 @@ +using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reflection; + +namespace BitMono.Shared.DependencyInjection; + +/// +/// Lightweight inversion of control container for dependency injection. +/// Ported from Microsoft MinIoC with enhancements. +/// +public class Container : Container.IScope +{ + #region Public interfaces + /// + /// Represents a scope in which per-scope objects are instantiated a single time + /// + public interface IScope : IDisposable, IBitMonoServiceProvider + { + } + + /// + /// IRegisteredType is returned by Container.Register and allows further configuration for the registration + /// + public interface IRegisteredType + { + /// + /// Make registered type a singleton + /// + void AsSingleton(); + + /// + /// Make registered type a per-scope type (single instance within a Scope) + /// + void PerScope(); + } + #endregion + + // Map of registered types + private readonly Dictionary> _registeredTypes = new(); + + // Lifetime management + private readonly ContainerLifetime _lifetime; + + /// + /// Creates a new instance of IoC Container + /// + public Container() => _lifetime = new ContainerLifetime(t => _registeredTypes[t]); + + /// + /// Registers a factory function which will be called to resolve the specified interface + /// + /// Interface to register + /// Factory function + /// + public IRegisteredType Register(Type @interface, Func factory) + => RegisterType(@interface, _ => factory()); + + /// + /// Registers an implementation type for the specified interface + /// + /// Interface to register + /// Implementing type + /// + public IRegisteredType Register(Type @interface, Type implementation) + { + // Handle self-registration (concrete type used as interface) + if (@interface == implementation) + { + return RegisterType(@interface, FactoryFromType(implementation)); + } + + var interfaceRegistration = RegisterType(@interface, FactoryFromType(implementation)); + + // Only register concrete type if not already registered + if (!_registeredTypes.ContainsKey(implementation)) + { + var concreteRegistration = RegisterType(implementation, FactoryFromType(implementation)); + return new CompositeRegisteredType(interfaceRegistration, concreteRegistration); + } + + return interfaceRegistration; + } + + /// + /// Registers an implementation type for the specified interface + /// + /// + public IRegisteredType Register() + => Register(typeof(TInterface), typeof(TImpl)); + + /// + /// Registers an implementation type for itself and all of its implemented interfaces. + /// + /// Implementing type + /// + public IRegisteredType Register(Type implementation) + { + var factory = FactoryFromType(implementation); + var concreteRegistration = RegisterType(implementation, factory); + var interfaces = implementation.GetInterfaces(); + foreach (var interfaceType in interfaces) + { + RegisterType(interfaceType, factory); + } + return concreteRegistration; + } + + /// + /// Registers an implementation type for the specified type + /// + /// + public IRegisteredType Register() + { + return Register(typeof(TImpl)); + } + + /// + /// Registers a factory function which will be called to resolve the specified interface + /// + /// Factory function + /// + public IRegisteredType Register(Func factory) + => RegisterType(typeof(TImpl), _ => factory()); + + /// + /// Registers the given instance as the service for T. + /// You can still call .AsSingleton() or .PerScope() on the returned IRegisteredType, + /// but since it's a concrete instance, you will almost always want AsSingleton(). + /// + public IRegisteredType Register(T instance) + { + return Register(typeof(T), () => instance!); + } + + /// + /// Checks if a type is registered in the container + /// + public bool IsRegistered(Type type) => _registeredTypes.ContainsKey(type); + + /// + /// Checks if a type is registered in the container + /// + public bool IsRegistered() => IsRegistered(typeof(T)); + + private IRegisteredType RegisterType(Type itemType, Func factory) + => new RegisteredType(itemType, f => _registeredTypes[itemType] = f, factory); + + /// + /// Returns the object registered for the given type, if registered + /// + /// Type as registered with the container + /// Instance of the registered type, if registered; otherwise null + public object? GetService(Type type) + { + if (!_registeredTypes.TryGetValue(type, out var registeredType)) + { + return null; + } + return registeredType(_lifetime); + } + + /// + /// Creates a new scope + /// + /// Scope object + public IScope CreateScope() => new ScopeLifetime(_lifetime); + + /// + /// Disposes any IDisposable objects owned by this container. + /// + public void Dispose() => _lifetime.Dispose(); + + #region Lifetime management + // ILifetime management adds resolution strategies to an IScope + interface ILifetime : IScope + { + object GetServiceAsSingleton(Type type, Func factory); + object GetServicePerScope(Type type, Func factory); + } + + // ObjectCache provides common caching logic for lifetimes + abstract class ObjectCache + { + // Instance cache + private readonly ConcurrentDictionary _instanceCache = []; + + // Track types currently being resolved to detect circular dependencies + private readonly ThreadLocal> _resolutionStack = new(() => []); + + // Get from cache or create and cache object + protected object GetCached(Type type, Func factory, ILifetime lifetime) + => _instanceCache.GetOrAdd(type, _ => factory(lifetime)); + + // Circular dependency detection methods + public void EnterResolution(Type type) + { + if (!_resolutionStack.Value!.Add(type)) + { + var circularChain = string.Join(" -> ", _resolutionStack.Value.Select(x => x.Name)); + throw new InvalidOperationException($"Circular dependency: {circularChain} -> {type.Name}"); + } + } + + public void ExitResolution(Type type) + { + _resolutionStack.Value!.Remove(type); + } + + public void Dispose() + { + foreach (var obj in _instanceCache.Values) + { + if (obj is IBitMonoServiceProvider or IScope) + continue; + (obj as IDisposable)?.Dispose(); + } + _resolutionStack.Dispose(); + } + } + + // Container lifetime management + class ContainerLifetime : ObjectCache, ILifetime + { + // Retrieves the factory function from the given type, provided by owning container + public Func> GetFactory { get; private set; } + + public ContainerLifetime(Func> getFactory) => GetFactory = getFactory; + + public object GetService(Type type) + { + try + { + EnterResolution(type); + return GetFactory(type)(this); + } + finally + { + ExitResolution(type); + } + } + + // Singletons get cached per container + public object GetServiceAsSingleton(Type type, Func factory) + => GetCached(type, factory, this); + + // At container level, per-scope items are equivalent to singletons + public object GetServicePerScope(Type type, Func factory) + => GetServiceAsSingleton(type, factory); + } + + // Per-scope lifetime management + class ScopeLifetime : ObjectCache, ILifetime + { + // Singletons come from parent container's lifetime + private readonly ContainerLifetime _parentLifetime; + + public ScopeLifetime(ContainerLifetime parentContainer) => _parentLifetime = parentContainer; + + public object GetService(Type type) + { + try + { + EnterResolution(type); + return _parentLifetime.GetFactory(type)(this); + } + finally + { + ExitResolution(type); + } + } + + // Singleton resolution is delegated to parent lifetime + public object GetServiceAsSingleton(Type type, Func factory) + => _parentLifetime.GetServiceAsSingleton(type, factory); + + // Per-scope objects get cached + public object GetServicePerScope(Type type, Func factory) + => GetCached(type, factory, this); + } + #endregion + + #region Container items + // Compiles a lambda that calls the given type's first constructor resolving arguments + private static Func FactoryFromType(Type itemType) + { + // Get first constructor for the type + var constructors = itemType.GetConstructors(); + if (constructors.Length == 0) + { + // If no public constructor found, search for an internal constructor + constructors = itemType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic); + } + var constructor = constructors.First(); + + // Compile constructor call as a lambda expression + var arg = Expression.Parameter(typeof(ILifetime)); + return (Func)Expression.Lambda( + Expression.New(constructor, constructor.GetParameters().Select( + param => + { + var resolve = new Func( + lifetime => lifetime.GetService(param.ParameterType)!); + return Expression.Convert( + Expression.Call(Expression.Constant(resolve.Target), resolve.Method, arg), + param.ParameterType); + })), + arg).Compile(); + } + + // RegisteredType is supposed to be a short lived object tying an item to its container + // and allowing users to mark it as a singleton or per-scope item + class RegisteredType : IRegisteredType + { + private readonly Type _itemType; + private readonly Action> _registerFactory; + private readonly Func _factory; + + public RegisteredType(Type itemType, Action> registerFactory, Func factory) + { + _itemType = itemType; + _registerFactory = registerFactory; + _factory = factory; + + registerFactory(_factory); + } + + public void AsSingleton() + => _registerFactory(lifetime => lifetime.GetServiceAsSingleton(_itemType, _factory)); + + public void PerScope() + => _registerFactory(lifetime => lifetime.GetServicePerScope(_itemType, _factory)); + } + + class CompositeRegisteredType : IRegisteredType + { + private readonly IRegisteredType _first; + private readonly IRegisteredType _second; + + public CompositeRegisteredType(IRegisteredType first, IRegisteredType second) + { + _first = first; + _second = second; + } + + public void AsSingleton() + { + _first.AsSingleton(); + _second.AsSingleton(); + } + + public void PerScope() + { + _first.PerScope(); + _second.PerScope(); + } + } + #endregion +} diff --git a/src/BitMono.Shared/DependencyInjection/ContainerExtensions.cs b/src/BitMono.Shared/DependencyInjection/ContainerExtensions.cs new file mode 100644 index 00000000..e841b070 --- /dev/null +++ b/src/BitMono.Shared/DependencyInjection/ContainerExtensions.cs @@ -0,0 +1,233 @@ +using System.Reflection; + +namespace BitMono.Shared.DependencyInjection; + +/// +/// Extension methods for Container +/// +public static class ContainerExtensions +{ + /// + /// Returns an implementation of the specified interface + /// + /// Interface type + /// Service provider instance + /// Object implementing the interface + public static T Resolve(this IBitMonoServiceProvider provider) + => (T)provider.GetService(typeof(T))!; + + /// + /// Returns an implementation of the specified interface, or throws if not found + /// + /// Interface type + /// Service provider instance + /// Object implementing the interface + /// Thrown when service is not registered + public static T GetRequiredService(this IBitMonoServiceProvider provider) + { + var service = provider.GetService(typeof(T)); + if (service == null) + throw new InvalidOperationException($"Service of type '{typeof(T).FullName}' is not registered."); + return (T)service; + } + + /// + /// Returns an implementation of the specified interface, or default if not found + /// + /// Interface type + /// Service provider instance + /// Object implementing the interface, or default + public static T? GetService(this IBitMonoServiceProvider provider) + { + var service = provider.GetService(typeof(T)); + return service == null ? default : (T)service; + } + + /// + /// Registers an implementation type for the specified interface + /// + /// Interface to register + /// This container instance + /// Implementing type + /// IRegisteredType object + public static Container.IRegisteredType Register(this Container container, Type type) + => container.Register(typeof(T), type); + + /// + /// Registers an implementation type for the specified interface + /// + /// Interface to register + /// Implementing type + /// This container instance + /// IRegisteredType object + public static Container.IRegisteredType Register(this Container container) + where TImplementation : TInterface + => container.Register(typeof(TInterface), typeof(TImplementation)); + + /// + /// Registers a factory function which will be called to resolve the specified interface + /// + /// Interface to register + /// This container instance + /// Factory method + /// IRegisteredType object + public static Container.IRegisteredType Register(this Container container, Func factory) + => container.Register(typeof(T), () => factory()!); + + /// + /// Registers all public types in assemblies implementing the specified interface. + /// + /// Container instance + /// Assemblies to scan + /// Interface type to look for + /// Optional filter function + /// Container for chaining + public static Container RegisterAssemblyTypes( + this Container container, + Assembly[] assemblies, + Type interfaceType, + Func? filter = null) + { + foreach (var assembly in assemblies) + { + Type[] types; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + types = ex.Types.Where(t => t != null).ToArray()!; + } + + foreach (var type in types) + { + if (!type.IsClass || type.IsAbstract || !type.IsPublic) + continue; + + if (filter != null && !filter(type)) + continue; + + if (type.GetInterface(interfaceType.Name) != null) + { + container.Register(interfaceType, type).AsSingleton(); + } + } + } + return container; + } + + /// + /// Registers all closed types of an open generic interface. + /// For example, registers all implementations of ICriticalAnalyzer<T>. + /// + /// Container instance + /// Assemblies to scan + /// Open generic type (e.g. typeof(ICriticalAnalyzer<>)) + /// Optional filter function + /// Container for chaining + public static Container RegisterClosedTypesOf( + this Container container, + Assembly[] assemblies, + Type openGenericType, + Func? filter = null) + { + if (!openGenericType.IsGenericTypeDefinition) + throw new ArgumentException($"Type {openGenericType.Name} must be an open generic type definition."); + + foreach (var assembly in assemblies) + { + Type[] types; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + types = ex.Types.Where(t => t != null).ToArray()!; + } + + foreach (var type in types) + { + if (!type.IsClass || type.IsAbstract || !type.IsPublic) + continue; + + if (filter != null && !filter(type)) + continue; + + // Find all interfaces that are closed versions of the open generic type + var implementedInterfaces = type.GetInterfaces() + .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == openGenericType); + + foreach (var closedInterface in implementedInterfaces) + { + container.Register(closedInterface, type).AsSingleton(); + } + } + } + return container; + } + + /// + /// Registers all types implementing an interface and allows resolving them as a collection. + /// + /// Interface type + /// Container instance + /// Assemblies to scan + /// Optional filter function + /// Container for chaining + public static Container RegisterCollection( + this Container container, + Assembly[] assemblies, + Func? filter = null) where T : class + { + var interfaceType = typeof(T); + var implementations = new List(); + + foreach (var assembly in assemblies) + { + Type[] types; + try + { + types = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + types = ex.Types.Where(t => t != null).ToArray()!; + } + + foreach (var type in types) + { + if (!type.IsClass || type.IsAbstract || !type.IsPublic) + continue; + + if (filter != null && !filter(type)) + continue; + + if (interfaceType.IsAssignableFrom(type)) + { + implementations.Add(type); + // Register individual type as well + container.Register(type, type).AsSingleton(); + } + } + } + + // Register the collection factory + container.Register>(() => + { + var list = new List(); + foreach (var implType in implementations) + { + var instance = container.GetService(implType); + if (instance is T typedInstance) + { + list.Add(typedInstance); + } + } + return list; + }).AsSingleton(); + + return container; + } +} diff --git a/src/BitMono.Shared/DependencyInjection/IBitMonoServiceProvider.cs b/src/BitMono.Shared/DependencyInjection/IBitMonoServiceProvider.cs new file mode 100644 index 00000000..a9a0946e --- /dev/null +++ b/src/BitMono.Shared/DependencyInjection/IBitMonoServiceProvider.cs @@ -0,0 +1,14 @@ +namespace BitMono.Shared.DependencyInjection; + +/// +/// Custom service provider interface to resolve services from the container. +/// +public interface IBitMonoServiceProvider +{ + /// + /// Returns the object registered for the given type, if registered. + /// + /// Type as registered with the container + /// Instance of the registered type, if registered; otherwise null + object? GetService(Type serviceType); +} diff --git a/src/BitMono.Shared/Extensions/CollectionExtensions.cs b/src/BitMono.Shared/Extensions/CollectionExtensions.cs new file mode 100644 index 00000000..9c0475b3 --- /dev/null +++ b/src/BitMono.Shared/Extensions/CollectionExtensions.cs @@ -0,0 +1,27 @@ +namespace BitMono.Shared.Extensions; + +public static class CollectionExtensions +{ + public static bool IsEmpty(this IEnumerable source) + { + return source switch + { + ICollection collection => collection.Count == 0, + IReadOnlyCollection readOnly => readOnly.Count == 0, + _ => !source.Any() + }; + } + + public static bool IsNullOrEmpty(this IEnumerable? source) + { + return source is null || source.IsEmpty(); + } + + public static void ForEach(this IEnumerable source, Action action) + { + foreach (var item in source) + { + action(item); + } + } +} diff --git a/src/BitMono.Shared/GlobalUsings.cs b/src/BitMono.Shared/GlobalUsings.cs index 27f92b0c..639c40b6 100644 --- a/src/BitMono.Shared/GlobalUsings.cs +++ b/src/BitMono.Shared/GlobalUsings.cs @@ -1,3 +1,8 @@ -global using Newtonsoft.Json; +global using System; global using System.Collections.Generic; -global using NullGuard; \ No newline at end of file +global using System.IO; +global using System.Linq; +global using System.Threading; +global using Newtonsoft.Json; +global using NullGuard; +global using BitMono.Shared.Extensions; \ No newline at end of file diff --git a/src/BitMono.Shared/Logging/ILogger.cs b/src/BitMono.Shared/Logging/ILogger.cs new file mode 100644 index 00000000..50c0c086 --- /dev/null +++ b/src/BitMono.Shared/Logging/ILogger.cs @@ -0,0 +1,72 @@ +namespace BitMono.Shared.Logging; + +/// +/// Lightweight logging interface compatible with Serilog's common patterns. +/// +public interface ILogger +{ + /// + /// Logs a debug message. + /// + /// Message template with optional placeholders + /// Arguments to substitute into placeholders + void Debug(string messageTemplate, params object[] args); + + /// + /// Logs an informational message. + /// + /// Message template with optional placeholders + /// Arguments to substitute into placeholders + void Information(string messageTemplate, params object[] args); + + /// + /// Logs a warning message. + /// + /// Message template with optional placeholders + /// Arguments to substitute into placeholders + void Warning(string messageTemplate, params object[] args); + + /// + /// Logs an error message. + /// + /// Message template with optional placeholders + /// Arguments to substitute into placeholders + void Error(string messageTemplate, params object[] args); + + /// + /// Logs an error message with an exception. + /// + /// The exception to log + /// Message template with optional placeholders + /// Arguments to substitute into placeholders + void Error(Exception exception, string messageTemplate, params object[] args); + + /// + /// Logs a fatal error message. + /// + /// Message template with optional placeholders + /// Arguments to substitute into placeholders + void Fatal(string messageTemplate, params object[] args); + + /// + /// Logs a fatal error message with an exception. + /// + /// The exception to log + /// Message template with optional placeholders + /// Arguments to substitute into placeholders + void Fatal(Exception exception, string messageTemplate, params object[] args); + + /// + /// Creates a new logger with the specified source context. + /// + /// Type to use as context + /// Logger with source context + ILogger ForContext(); + + /// + /// Creates a new logger with the specified source context. + /// + /// Type to use as context + /// Logger with source context + ILogger ForContext(Type type); +} diff --git a/src/BitMono.Shared/Logging/LogLevel.cs b/src/BitMono.Shared/Logging/LogLevel.cs new file mode 100644 index 00000000..b1aa9c5b --- /dev/null +++ b/src/BitMono.Shared/Logging/LogLevel.cs @@ -0,0 +1,32 @@ +namespace BitMono.Shared.Logging; + +/// +/// Specifies the severity level of a log message. +/// +public enum LogLevel +{ + /// + /// Debug-level messages for development troubleshooting. + /// + Debug = 0, + + /// + /// Informational messages that track the general flow of the application. + /// + Information = 1, + + /// + /// Warning messages for abnormal or unexpected events. + /// + Warning = 2, + + /// + /// Error messages for failures within the current operation. + /// + Error = 3, + + /// + /// Fatal messages for unrecoverable application errors. + /// + Fatal = 4 +} diff --git a/src/BitMono.Shared/Logging/Logger.cs b/src/BitMono.Shared/Logging/Logger.cs new file mode 100644 index 00000000..1692878e --- /dev/null +++ b/src/BitMono.Shared/Logging/Logger.cs @@ -0,0 +1,166 @@ +using System.Text.RegularExpressions; + +namespace BitMono.Shared.Logging; + +/// +/// Lightweight logger implementation with console and file output. +/// +public class Logger : ILogger +{ + private readonly string? _context; + private readonly LoggerConfiguration _configuration; + private static readonly object ConsoleLock = new(); + private static readonly object FileLock = new(); + + /// + /// Creates a new logger with the specified configuration. + /// + /// Logger configuration + /// Optional source context name + public Logger(LoggerConfiguration configuration, string? context = null) + { + _configuration = configuration; + _context = context; + } + + /// + /// Creates a new logger with default configuration (console output only). + /// + public Logger() : this(new LoggerConfiguration()) + { + } + + /// + public ILogger ForContext() => ForContext(typeof(T)); + + /// + public ILogger ForContext(Type type) => new Logger(_configuration, type.Name); + + /// + public void Debug(string messageTemplate, params object[] args) + => Log(LogLevel.Debug, null, messageTemplate, args); + + /// + public void Information(string messageTemplate, params object[] args) + => Log(LogLevel.Information, null, messageTemplate, args); + + /// + public void Warning(string messageTemplate, params object[] args) + => Log(LogLevel.Warning, null, messageTemplate, args); + + /// + public void Error(string messageTemplate, params object[] args) + => Log(LogLevel.Error, null, messageTemplate, args); + + /// + public void Error(Exception exception, string messageTemplate, params object[] args) + => Log(LogLevel.Error, exception, messageTemplate, args); + + /// + public void Fatal(string messageTemplate, params object[] args) + => Log(LogLevel.Fatal, null, messageTemplate, args); + + /// + public void Fatal(Exception exception, string messageTemplate, params object[] args) + => Log(LogLevel.Fatal, exception, messageTemplate, args); + + private void Log(LogLevel level, Exception? exception, string messageTemplate, object[] args) + { + if (level < _configuration.MinimumLevel) + return; + + var message = FormatMessage(messageTemplate, args); + var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + var levelStr = GetLevelString(level); + var contextStr = _context ?? "Program"; + + var logLine = $"[{timestamp} {levelStr}][{contextStr}] {message}"; + + if (exception != null) + logLine += Environment.NewLine + exception; + + if (_configuration.WriteToConsole) + WriteToConsole(level, logLine); + + if (_configuration.WriteToFile && !string.IsNullOrEmpty(_configuration.LogFilePath)) + WriteToFile(logLine); + } + + private static string FormatMessage(string template, object[] args) + { + if (args.Length == 0) + return template; + + var result = template; + + // Support {0}, {1}, etc. style placeholders + for (int i = 0; i < args.Length; i++) + { + var pattern = $"{{{i}}}"; + result = result.Replace(pattern, args[i]?.ToString() ?? "null"); + } + + // Support {Name} style placeholders (Serilog-style) - replace with positional args in order + var namedPattern = new Regex(@"\{([^{}0-9]+)\}"); + var matches = namedPattern.Matches(result); + int argIndex = 0; + foreach (Match match in matches) + { + if (argIndex < args.Length) + { + result = result.Replace(match.Value, args[argIndex]?.ToString() ?? "null"); + argIndex++; + } + } + + return result; + } + + private static string GetLevelString(LogLevel level) => level switch + { + LogLevel.Debug => "DBG", + LogLevel.Information => "INF", + LogLevel.Warning => "WRN", + LogLevel.Error => "ERR", + LogLevel.Fatal => "FTL", + _ => "???" + }; + + private static void WriteToConsole(LogLevel level, string message) + { + lock (ConsoleLock) + { + var originalColor = Console.ForegroundColor; + Console.ForegroundColor = level switch + { + LogLevel.Debug => ConsoleColor.Gray, + LogLevel.Information => ConsoleColor.White, + LogLevel.Warning => ConsoleColor.Yellow, + LogLevel.Error => ConsoleColor.Red, + LogLevel.Fatal => ConsoleColor.DarkRed, + _ => ConsoleColor.Gray + }; + Console.WriteLine(message); + Console.ForegroundColor = originalColor; + } + } + + private void WriteToFile(string message) + { + try + { + lock (FileLock) + { + var dir = Path.GetDirectoryName(_configuration.LogFilePath); + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + File.AppendAllText(_configuration.LogFilePath!, message + Environment.NewLine); + } + } + catch + { + // Silently fail file logging to avoid disrupting application + } + } +} diff --git a/src/BitMono.Shared/Logging/LoggerConfiguration.cs b/src/BitMono.Shared/Logging/LoggerConfiguration.cs new file mode 100644 index 00000000..5c74dcfc --- /dev/null +++ b/src/BitMono.Shared/Logging/LoggerConfiguration.cs @@ -0,0 +1,27 @@ +namespace BitMono.Shared.Logging; + +/// +/// Configuration options for the logger. +/// +public class LoggerConfiguration +{ + /// + /// Minimum log level to output. Messages below this level are ignored. + /// + public LogLevel MinimumLevel { get; set; } = LogLevel.Debug; + + /// + /// Whether to write log messages to the console. + /// + public bool WriteToConsole { get; set; } = true; + + /// + /// Whether to write log messages to a file. + /// + public bool WriteToFile { get; set; } + + /// + /// Path to the log file. Required if WriteToFile is true. + /// + public string? LogFilePath { get; set; } +} diff --git a/test/BitMono.Core.Tests/Analyzing/CriticalMethodsCriticalAnalyzerTest.cs b/test/BitMono.Core.Tests/Analyzing/CriticalMethodsCriticalAnalyzerTest.cs index 00bb45d2..372b2c69 100644 --- a/test/BitMono.Core.Tests/Analyzing/CriticalMethodsCriticalAnalyzerTest.cs +++ b/test/BitMono.Core.Tests/Analyzing/CriticalMethodsCriticalAnalyzerTest.cs @@ -29,8 +29,7 @@ public void WhenMethodCriticalAnalyzing_AndMethodIsCritical_ThenShouldBeFalse(st methodName } }; - var options = Options.Create(criticals); - var criticalAnalyzer = Setup.CriticalMethodsCriticalAnalyzer(options); + var criticalAnalyzer = Setup.CriticalMethodsCriticalAnalyzer(criticals); var module = ModuleDefinition.FromFile(typeof(CriticalMethods).Assembly.Location); var type = module.TopLevelTypes.First(t => t.Name == nameof(CriticalMethods)); var method = type.Methods.First(m => m.Name == methodName); @@ -53,8 +52,7 @@ public void WhenMethodCriticalAnalyzing_AndMethodIsNotCritical_ThenShouldBeTrue( methodName } }; - var options = Options.Create(criticals); - var criticalAnalyzer = Setup.CriticalMethodsCriticalAnalyzer(options); + var criticalAnalyzer = Setup.CriticalMethodsCriticalAnalyzer(criticals); var module = ModuleDefinition.FromFile(typeof(CriticalMethods).Assembly.Location); var type = module.TopLevelTypes.First(t => t.Name == nameof(CriticalMethods)); var method = type.Methods.First(m => m.Name == nameof(CriticalMethods.VoidMethod)); diff --git a/test/BitMono.Core.Tests/Analyzing/CriticalMethodsStartsWithCriticalAnalyzerTest.cs b/test/BitMono.Core.Tests/Analyzing/CriticalMethodsStartsWithCriticalAnalyzerTest.cs index bfb9a3af..1473d466 100644 --- a/test/BitMono.Core.Tests/Analyzing/CriticalMethodsStartsWithCriticalAnalyzerTest.cs +++ b/test/BitMono.Core.Tests/Analyzing/CriticalMethodsStartsWithCriticalAnalyzerTest.cs @@ -28,8 +28,7 @@ public void WhenMethodCriticalAnalyzing_AndMethodIsCritical_ThenShouldBeFalse(st methodName } }; - var options = Options.Create(criticals); - var criticalAnalyzer = Setup.CriticalMethodsStartsWithCriticalAnalyzer(options); + var criticalAnalyzer = Setup.CriticalMethodsStartsWithCriticalAnalyzer(criticals); var module = ModuleDefinition.FromFile(typeof(CriticalMethods).Assembly.Location); var type = module.TopLevelTypes.First(t => t.Name == nameof(CriticalMethods)); var method = type.Methods.First(m => m.Name == methodName); @@ -52,8 +51,7 @@ public void WhenMethodCriticalAnalyzing_AndMethodIsNotCritical_ThenShouldBeTrue( methodName } }; - var options = Options.Create(criticals); - var criticalAnalyzer = Setup.CriticalMethodsStartsWithCriticalAnalyzer(options); + var criticalAnalyzer = Setup.CriticalMethodsStartsWithCriticalAnalyzer(criticals); var module = ModuleDefinition.FromFile(typeof(CriticalMethods).Assembly.Location); var type = module.TopLevelTypes.First(t => t.Name == nameof(CriticalMethods)); var method = type.Methods.First(m => m.Name == nameof(CriticalMethods.VoidMethod)); diff --git a/test/BitMono.Core.Tests/Analyzing/ReflectionCriticalAnalyzerTest.cs b/test/BitMono.Core.Tests/Analyzing/ReflectionCriticalAnalyzerTest.cs index ccb718cb..ba5d7a16 100644 --- a/test/BitMono.Core.Tests/Analyzing/ReflectionCriticalAnalyzerTest.cs +++ b/test/BitMono.Core.Tests/Analyzing/ReflectionCriticalAnalyzerTest.cs @@ -8,7 +8,7 @@ private static ReflectionCriticalAnalyzer CreateAnalyzer(bool reflectionEnabled { ReflectionMembersObfuscationExclude = reflectionEnabled }; - return Setup.ReflectionCriticalAnalyzer(Options.Create(obfuscation)); + return Setup.ReflectionCriticalAnalyzer(obfuscation); } private static (ModuleDefinition module, TypeDefinition type) GetTestData() diff --git a/test/BitMono.Core.Tests/Analyzing/SerializableBitCriticalAnalyzerTest.cs b/test/BitMono.Core.Tests/Analyzing/SerializableBitCriticalAnalyzerTest.cs index 9a5466f5..46e3cef6 100644 --- a/test/BitMono.Core.Tests/Analyzing/SerializableBitCriticalAnalyzerTest.cs +++ b/test/BitMono.Core.Tests/Analyzing/SerializableBitCriticalAnalyzerTest.cs @@ -9,7 +9,7 @@ public void WhenTypeSerializableBitCriticalAnalyzing_AndTypeHasSerializableBit_T { SerializableBitObfuscationExclude = true }; - var criticalAnalyzer = Setup.SerializableBitCriticalAnalyzer(Options.Create(obfuscation)); + var criticalAnalyzer = Setup.SerializableBitCriticalAnalyzer(obfuscation); var module = ModuleDefinition.FromFile(typeof(SerializableTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(SerializableTypes)); var type = types.NestedTypes.First(n => n.Name == nameof(SerializableTypes.SerializableBit)); @@ -25,13 +25,13 @@ public void WhenTypeSerializableBitCriticalAnalyzing_AndTypeHasNoSerializableBit { SerializableBitObfuscationExclude = true }; - var criticalAnalyzer = Setup.SerializableBitCriticalAnalyzer(Options.Create(obfuscation)); + var criticalAnalyzer = Setup.SerializableBitCriticalAnalyzer(obfuscation); var module = ModuleDefinition.FromFile(typeof(SerializableTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(SerializableTypes)); var type = types.NestedTypes.First(n => n.Name == nameof(SerializableTypes.NoSerializableBit)); - + var result = criticalAnalyzer.NotCriticalToMakeChanges(type); result.Should().BeTrue(); } -} \ No newline at end of file +} diff --git a/test/BitMono.Core.Tests/GlobalUsings.cs b/test/BitMono.Core.Tests/GlobalUsings.cs index be542c17..c19de27a 100644 --- a/test/BitMono.Core.Tests/GlobalUsings.cs +++ b/test/BitMono.Core.Tests/GlobalUsings.cs @@ -15,4 +15,3 @@ global using FluentAssertions; global using Xunit; global using BitMono.Protections; -global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/test/BitMono.Core.Tests/Resolvers/NoInliningMethodMemberResolverTest.cs b/test/BitMono.Core.Tests/Resolvers/NoInliningMethodMemberResolverTest.cs index cce11a17..7cfdbff8 100644 --- a/test/BitMono.Core.Tests/Resolvers/NoInliningMethodMemberResolverTest.cs +++ b/test/BitMono.Core.Tests/Resolvers/NoInliningMethodMemberResolverTest.cs @@ -9,8 +9,7 @@ public void WhenNoInliningMethodResolving_AndMethodHasInliningBit_ThenShouldBeFa { NoInliningMethodObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.NoInliningMethodMemberResolver(options); + var resolver = Setup.NoInliningMethodMemberResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(NoInliningMethods).Assembly.Location); var type = module.TopLevelTypes.First(t => t.Name == nameof(NoInliningMethods)); var method = type.Methods.First(m => m.Name == nameof(NoInliningMethods.NoInliningMethod)); @@ -28,8 +27,7 @@ public void WhenNoInliningMethodResolving_AndMethodHasNoInliningBit_ThenShouldBe { NoInliningMethodObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.NoInliningMethodMemberResolver(options); + var resolver = Setup.NoInliningMethodMemberResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(NoInliningMethods).Assembly.Location); var type = module.TopLevelTypes.First(t => t.Name == nameof(NoInliningMethods)); var method = type.Methods.First(m => m.Name == nameof(NoInliningMethods.VoidMethod)); diff --git a/test/BitMono.Core.Tests/Resolvers/ObfuscateAssemblyAttributeResolverTest.cs b/test/BitMono.Core.Tests/Resolvers/ObfuscateAssemblyAttributeResolverTest.cs index 31b9b830..7ddb79cb 100644 --- a/test/BitMono.Core.Tests/Resolvers/ObfuscateAssemblyAttributeResolverTest.cs +++ b/test/BitMono.Core.Tests/Resolvers/ObfuscateAssemblyAttributeResolverTest.cs @@ -9,8 +9,7 @@ public void WhenObfuscateAssemblyAttributeResolving_AndAssemblyHasObfuscateAssem { ObfuscateAssemblyAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscateAssemblyAttributeResolver(options); + var resolver = Setup.ObfuscateAssemblyAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(CustomAttributesInstance).Assembly.Location); var result = resolver.Resolve(module.Assembly); diff --git a/test/BitMono.Core.Tests/Resolvers/ObfuscationAttributeResolverTest.cs b/test/BitMono.Core.Tests/Resolvers/ObfuscationAttributeResolverTest.cs index 0b3e0227..74db817c 100644 --- a/test/BitMono.Core.Tests/Resolvers/ObfuscationAttributeResolverTest.cs +++ b/test/BitMono.Core.Tests/Resolvers/ObfuscationAttributeResolverTest.cs @@ -10,8 +10,7 @@ public void ShouldReturnFalse_WhenExcludeIsFalse(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -32,8 +31,7 @@ public void ShouldReturnTrue_WhenExcludeIsTrue(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => n.Name == nameof(ObfuscationTypes.ObfuscationAttributeCallToCalli)); @@ -52,8 +50,7 @@ public void ShouldReturnFalse_WhenVoidAttributeWithEmptyFeature() { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => n.Name == nameof(ObfuscationTypes.VoidObfuscationAttribute)); @@ -72,8 +69,7 @@ public void ShouldReturnFalse_WhenMethodHasVoidAttributeWithEmptyFeature() { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationMethods).Assembly.Location); var type = module.TopLevelTypes.First(n => n.Name == nameof(ObfuscationMethods)); var method = type.Methods.First(m => m.Name == nameof(ObfuscationMethods.VoidObfuscationAttribute)); @@ -93,8 +89,7 @@ public void ShouldReturnTrue_WhenApplyToMembersIsFalse(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -118,8 +113,7 @@ public void ShouldReturnTrue_WhenStripAfterObfuscationIsFalse(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -143,8 +137,7 @@ public void ShouldReturnTrue_WhenFeatureMatches(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -163,8 +156,7 @@ public void ShouldReturnFalse_WhenFeatureDoesNotMatch(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -182,8 +174,7 @@ public void ShouldReturnTrue_WhenEmptyFeatureMatchesEmptySearch() { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -202,8 +193,7 @@ public void ShouldReturnFalse_WhenEmptyFeatureDoesNotMatchSearch(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -222,8 +212,7 @@ public void ShouldReturnTrue_WhenNoFeatureSpecified(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -246,8 +235,7 @@ public void ShouldReturnTrue_WhenAllPropertiesAreTrue(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => @@ -271,8 +259,7 @@ public void ShouldReturnFalse_WhenResolverIsDisabled() { ObfuscationAttributeObfuscationExclude = false, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => n.Name == nameof(ObfuscationTypes.ObfuscationAttributeCallToCalli)); @@ -289,8 +276,7 @@ public void ShouldReturnFalse_WhenNoAttributePresent() { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationTypes).Assembly.Location); var types = module.TopLevelTypes.First(t => t.Name == nameof(ObfuscationTypes)); var type = types.NestedTypes.First(n => n.Name == nameof(ObfuscationTypes.NoObfuscationAttribute)); @@ -308,8 +294,7 @@ public void ShouldReturnTrue_WhenMethodHasExcludeTrue(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationMethods).Assembly.Location); var type = module.TopLevelTypes.First(n => n.Name == nameof(ObfuscationMethods)); var method = type.Methods.First(m => m.Name == nameof(ObfuscationMethods.ObfuscationAttributeFeatureCallToCalliExcludeTrue)); @@ -331,8 +316,7 @@ public void ShouldReturnFalse_WhenMethodHasExcludeFalse(string feature) { ObfuscationAttributeObfuscationExclude = true, }; - var options = Options.Create(obfuscation); - var resolver = Setup.ObfuscationAttributeResolver(options); + var resolver = Setup.ObfuscationAttributeResolver(obfuscation); var module = ModuleDefinition.FromFile(typeof(ObfuscationMethods).Assembly.Location); var type = module.TopLevelTypes.First(n => n.Name == nameof(ObfuscationMethods)); var method = type.Methods.First(m => m.Name == nameof(ObfuscationMethods.ObfuscationAttributeFeatureCallToCalliExcludeFalse)); diff --git a/test/BitMono.Core.Tests/Setup.cs b/test/BitMono.Core.Tests/Setup.cs index ed431caa..45261644 100644 --- a/test/BitMono.Core.Tests/Setup.cs +++ b/test/BitMono.Core.Tests/Setup.cs @@ -2,37 +2,36 @@ namespace BitMono.Core.Tests; public static class Setup { - public static ModelAttributeCriticalAnalyzer ModelAttributeCriticalAnalyzer(IOptions criticals) + public static ModelAttributeCriticalAnalyzer ModelAttributeCriticalAnalyzer(CriticalsSettings criticals) { return new ModelAttributeCriticalAnalyzer(criticals); } - public static CriticalMethodsCriticalAnalyzer CriticalMethodsCriticalAnalyzer(IOptions criticals) + public static CriticalMethodsCriticalAnalyzer CriticalMethodsCriticalAnalyzer(CriticalsSettings criticals) { return new CriticalMethodsCriticalAnalyzer(criticals); } - public static CriticalMethodsStartsWithAnalyzer CriticalMethodsStartsWithCriticalAnalyzer(IOptions criticals) + public static CriticalMethodsStartsWithAnalyzer CriticalMethodsStartsWithCriticalAnalyzer(CriticalsSettings criticals) { return new CriticalMethodsStartsWithAnalyzer(criticals); } - public static NoInliningMethodMemberResolver NoInliningMethodMemberResolver(IOptions obfuscation) + public static NoInliningMethodMemberResolver NoInliningMethodMemberResolver(ObfuscationSettings obfuscation) { return new NoInliningMethodMemberResolver(obfuscation); } - public static ObfuscationAttributeResolver ObfuscationAttributeResolver(IOptions obfuscation) + public static ObfuscationAttributeResolver ObfuscationAttributeResolver(ObfuscationSettings obfuscation) { return new ObfuscationAttributeResolver(obfuscation); } - public static ObfuscateAssemblyAttributeResolver ObfuscateAssemblyAttributeResolver( - IOptions obfuscation) + public static ObfuscateAssemblyAttributeResolver ObfuscateAssemblyAttributeResolver(ObfuscationSettings obfuscation) { return new ObfuscateAssemblyAttributeResolver(obfuscation); } - public static SerializableBitCriticalAnalyzer SerializableBitCriticalAnalyzer(IOptions obfuscation) + public static SerializableBitCriticalAnalyzer SerializableBitCriticalAnalyzer(ObfuscationSettings obfuscation) { return new SerializableBitCriticalAnalyzer(obfuscation); } - public static ReflectionCriticalAnalyzer ReflectionCriticalAnalyzer(IOptions obfuscation) + public static ReflectionCriticalAnalyzer ReflectionCriticalAnalyzer(ObfuscationSettings obfuscation) { return new ReflectionCriticalAnalyzer(obfuscation); } -} \ No newline at end of file +}