diff --git a/BitMono.sln.DotSettings b/BitMono.sln.DotSettings index e5f2c9a3..d6f14c6a 100644 --- a/BitMono.sln.DotSettings +++ b/BitMono.sln.DotSettings @@ -5,8 +5,10 @@ JS NET PE + True True True True True + True True \ No newline at end of file diff --git a/README.md b/README.md index 37026c7e..c0680fd7 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ ## BitMono -[![Codefactor][image_codefactor]][codefactor] -[![DeepSource][image_deepsource]][deepsource] [![MIT License][image_license]][license] [![BitMono Discord][image_bitmono_discord]][bitmono_discord] @@ -73,6 +71,7 @@ Read the **[docs][bitmono_docs]** to read protection, functionality, and more. * FullRenamer * AntiDebugBreakpoints * AntiDecompiler +* BitDecompiler (fixed version of BitDotNet for newer Unity Versions) * BitDateTimeStamp * BitMono * BillionNops @@ -182,8 +181,6 @@ Credits **[drakonia][author_drakonia]** for her **[costura decompressor][simple_costura_decompressor_source]**. -[codefactor]: https://www.codefactor.io/repository/github/sunnamed434/bitmono/overview/main -[deepsource]: https://deepsource.io/gh/sunnamed434/BitMono/?ref=repository-badge [license]: https://github.com/sunnamed434/BitMono/blob/main/LICENSE [previews]: https://github.com/sunnamed434/BitMono/blob/main/PREVIEWS.md [asmresolver]: https://github.com/Washi1337/AsmResolver diff --git a/docs/source/configuration/protections.rst b/docs/source/configuration/protections.rst index faf5c711..cc741b0c 100644 --- a/docs/source/configuration/protections.rst +++ b/docs/source/configuration/protections.rst @@ -1,16 +1,22 @@ -Configure Protections -===================== +Protections +=========== Let's say you want to execute the same protection twice or even execute protections in a different orders, etc. +Keep in mind: + +- The order of execution is determined by the position of each protection in the ``protections.json`` file within the configuration file. For example, AntiILdasm is executed first (because this protection is first in configuration) and Packers always run last after all protections, even if you set this ``Packer`` protection as a first one in configuration it will anyway gonna be called last. + Use ``protections.json`` - by default all protections are configured as they should, if something works not as intentionally you always may disable something, enable and even remove. +Default config (it can be different with new updates, but just let's see how it looks like): + .. code-block:: json { "Protections": [ { - "Name": "AntiILdasm", + "Name": "AntiILdasm", // This is going to be executed first "Enabled": false }, { @@ -54,15 +60,15 @@ Use ``protections.json`` - by default all protections are configured as they sho "Enabled": false }, { - "Name": "BitTimeDateStamp", + "Name": "BitTimeDateStamp", // Packer "Enabled": false }, { - "Name": "BitDotNet", + "Name": "BitDotNet", // Packer "Enabled": true }, { - "Name": "BitMono", + "Name": "BitMono", // This is going to be executed last because its a Packer, even if you put this before AntiILdasm it going to be called last anyway. "Enabled": true } ] diff --git a/docs/source/developers/do-not-resolve-members.rst b/docs/source/developers/do-not-resolve-members.rst index e07d17dc..1f453826 100644 --- a/docs/source/developers/do-not-resolve-members.rst +++ b/docs/source/developers/do-not-resolve-members.rst @@ -32,7 +32,6 @@ Add attribute ``[DoNotResolve(MemberInclusionFlags.Reflection)]`` with ``MemberI .. code-block:: csharp - [UsedImplicitly] // This is not intentional, but suppresses warnings by ReSharper [DoNotResolve(MemberInclusionFlags.Reflection)] public class MagicProtection : Protection @@ -43,7 +42,6 @@ You can specify multiple inclusion flags. .. code-block:: csharp - [UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime | MemberInclusionFlags.Reflection)] public class MagicProtection : Protection diff --git a/docs/source/developers/first-protection.rst b/docs/source/developers/first-protection.rst index d4c04d56..aab4d762 100644 --- a/docs/source/developers/first-protection.rst +++ b/docs/source/developers/first-protection.rst @@ -6,19 +6,15 @@ Creating your first Protection BitMono provides a lot of examples in source code with existing protection and maximum functional usage, you can find them in BitMono.Protections project. -Always create your protection ONLY in BitMono.Protections, DI (dependency injection) container will catch all of your protections automatically and Obfuscation Engine of BitMono will automatically call your protection depending on which protection is it. +Create your protection in the ``BitMono.Protections`` namespace. + +- The Dependency Injection (DI) container will automatically register your protections. +- The BitMono Obfuscation Engine will invoke your protection based on its type and the order specified in the configuration file. +- The order of execution is determined by the position of each protection in the ``protections.json`` file within the configuration file. For example, AntiILdasm is executed first (because this protection is first in configuration) and Packers always run last after all protections, even if you set this ``Packer`` protection as a first one in configuration it will anyway gonna be called last. .. code-block:: csharp - // Mark the Protection as [UsedImplicitly] because for JetBrains Rider or ReSharper users protection will look kinda is not used, - // and other developers might delete it as an unnecessary class in the project, - // because protections are instantiated via DI container, so, its invisible for JetBrains Rider and ReSharper, - // the goal is remove weird warning, - // In simple words, saying to the rider: - // "It's ok, protection created somewhere else but you can't see it, - // please don't worry, and remove your warnings" - [UsedImplicitly] public class StandardProtection : Protection { // Inject services right here diff --git a/docs/source/developers/obfuscation-execution-order.rst b/docs/source/developers/obfuscation-execution-order.rst index 0f42b714..5e4436c8 100644 --- a/docs/source/developers/obfuscation-execution-order.rst +++ b/docs/source/developers/obfuscation-execution-order.rst @@ -4,13 +4,14 @@ Obfuscation Engine Execution Order BitMono uses its own obfuscation execution order which is good to be known, and it reminds ConfuserEx a lot, if you're familiar with it you can be easier with it. 1. Output Loaded Module Info -2. Sort Protections -3. Basic output information about Protections -4. Elapsed time counter -5. Output Information of Running Framework -6. Resolve References -7. Expand Macros -8. Run Protection, PipelineProtection and child pipeline protections +2. Output Information about BitMono (example, is it intended for .NET Core or Mono or .NET Framework, etc.) and running OS, etc. +3. Output Compatibility Issues in case of module is built for .NET Framework, but BitMono is running on .NET Core, or vice versa. +4. Sort Protections +5. Basic output information about Protections +6. Elapsed time counter +7. Resolve References +8. Expand Macros +10. Run Protection, PipelineProtection and child pipeline protections .. code-block:: csharp @@ -20,11 +21,11 @@ BitMono uses its own obfuscation execution order which is good to be known, and public class Pipeline : PipelineProtection -9. Optimize Macros -10. [ObfuscationAttribute] cleanup -11. Create PE Image -12. Write Module -13. Run Packers +11. Optimize Macros +12. [ObfuscationAttribute] cleanup +13. Create PE Image +14. Write Module +15. Run Packers .. code-block:: csharp @@ -32,4 +33,4 @@ BitMono uses its own obfuscation execution order which is good to be known, and public class Packer : PackerProtection -14. Output Elapsed Time since obfuscation \ No newline at end of file +16. Output Elapsed Time since obfuscation \ No newline at end of file diff --git a/docs/source/developers/protection-runtime-moniker.rst b/docs/source/developers/protection-runtime-moniker.rst index d3291965..662596c9 100644 --- a/docs/source/developers/protection-runtime-moniker.rst +++ b/docs/source/developers/protection-runtime-moniker.rst @@ -6,7 +6,6 @@ By default BitMono provides an opportunity to talk with the users, to warn them, .. code-block:: csharp - [UsedImplicitly] [RuntimeMonikerMono] // Add this Attribute which says this protections works only with Mono Runtime public class MonoPacker : Packer @@ -69,7 +68,6 @@ Specify Rust Runtime Moniker Attribute. .. code-block:: csharp - [UsedImplicitly] [RuntimeMonikerRust] // Add this Attribute which says this protections works only with Rust Runtime public class RustPacker : Packer // or instead use Protection or PipelineProtection diff --git a/docs/source/index.rst b/docs/source/index.rst index 802d98d5..ad3245bd 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -18,6 +18,7 @@ Table of Contents: protections/antiildasm protections/antide4dot protections/bitdotnet + protections/bitdecompiler protections/bitmono protections/bittimedatestamp protections/bitmethoddotnet diff --git a/docs/source/protections/bitdecompiler.rst b/docs/source/protections/bitdecompiler.rst new file mode 100644 index 00000000..daea83c7 --- /dev/null +++ b/docs/source/protections/bitdecompiler.rst @@ -0,0 +1,11 @@ +BitDecompiler +============= + +How it works? +------------- +This protection works the same as BitDotnet protection, but with some fixes. However, since after Unity 2021 and higher it stopped working correctly and since many of users asked to figure something out we made this protection as a solution =) + + +.. warning:: + + This protection compatible only with Mono. \ No newline at end of file diff --git a/src/BitMono.API/Analyzing/ICriticalAnalyzer.cs b/src/BitMono.API/Analyzing/ICriticalAnalyzer.cs index ff81cda4..c8ea0887 100644 --- a/src/BitMono.API/Analyzing/ICriticalAnalyzer.cs +++ b/src/BitMono.API/Analyzing/ICriticalAnalyzer.cs @@ -1,5 +1,6 @@ namespace BitMono.API.Analyzing; +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public interface ICriticalAnalyzer { bool NotCriticalToMakeChanges(TObject @object); diff --git a/src/BitMono.API/GlobalUsings.cs b/src/BitMono.API/GlobalUsings.cs index 6ce7f2e7..c92a78db 100644 --- a/src/BitMono.API/GlobalUsings.cs +++ b/src/BitMono.API/GlobalUsings.cs @@ -7,4 +7,5 @@ global using System.Diagnostics.CodeAnalysis; 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 diff --git a/src/BitMono.API/Protections/IProtection.cs b/src/BitMono.API/Protections/IProtection.cs index 114d343e..8e7d49de 100644 --- a/src/BitMono.API/Protections/IProtection.cs +++ b/src/BitMono.API/Protections/IProtection.cs @@ -1,5 +1,6 @@ namespace BitMono.API.Protections; +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public interface IProtection { Task ExecuteAsync(); diff --git a/src/BitMono.CLI/Modules/CLIBitMonoModuleFileResolver.cs b/src/BitMono.CLI/Modules/CLIBitMonoModuleFileResolver.cs deleted file mode 100644 index a317a2ab..00000000 --- a/src/BitMono.CLI/Modules/CLIBitMonoModuleFileResolver.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace BitMono.CLI.Modules; - -internal static class CLIBitMonoModuleFileResolver -{ - public static string? Resolve(string[] args) - { - string? file = null; - if (args.IsEmpty() == false) - { - file = PathFormatterUtility.Format(args[0]); - } - return file; - } -} \ No newline at end of file diff --git a/src/BitMono.CLI/Modules/CLIObfuscationNeedsFactory.cs b/src/BitMono.CLI/Modules/CLIObfuscationNeedsFactory.cs index 2b22de88..59f6eeb2 100644 --- a/src/BitMono.CLI/Modules/CLIObfuscationNeedsFactory.cs +++ b/src/BitMono.CLI/Modules/CLIObfuscationNeedsFactory.cs @@ -14,15 +14,18 @@ public CLIObfuscationNeedsFactory(string[] args, ObfuscationSettings obfuscation _logger = logger.ForContext(); } - public ObfuscationNeeds Create() + public ObfuscationNeeds Create(CancellationToken cancellationToken) { - var fileName = CLIBitMonoModuleFileResolver.Resolve(_args); + var fileName = GetFileName(_args); var specifyingFile = true; while (specifyingFile) { try { _logger.Information("Please, specify file or drag-and-drop in BitMono CLI"); + + cancellationToken.ThrowIfCancellationRequested(); + fileName = PathFormatterUtility.Format(Console.ReadLine()); if (string.IsNullOrWhiteSpace(fileName) == false) { @@ -41,6 +44,10 @@ public ObfuscationNeeds Create() _logger.Warning("Unable to specify empty null or whitespace file, please, try again!"); } } + catch (OperationCanceledException) + { + throw; + } catch (Exception ex) { _logger.Warning("Something went wrong while specifying the file: " + ex); @@ -66,6 +73,8 @@ public ObfuscationNeeds Create() { try { + cancellationToken.ThrowIfCancellationRequested(); + if (Directory.Exists(dependenciesDirectoryName)) { _logger.Information("Dependencies (libs) successfully found automatically: {0}!", @@ -95,10 +104,13 @@ public ObfuscationNeeds Create() _logger.Information("Unable to specify empty (libs), please, try again!"); } } + catch (OperationCanceledException) + { + throw; + } catch (Exception ex) { - _logger.Information("Something went wrong while specifying the dependencies (libs) path: " + - ex); + _logger.Error(ex, "Something went wrong while specifying the dependencies (libs) path"); } } } @@ -119,4 +131,14 @@ public ObfuscationNeeds Create() OutputPath = outputDirectoryName }; } + + private string GetFileName(string[] args) + { + string? file = null; + if (args.IsEmpty() == false) + { + file = PathFormatterUtility.Format(args[0]); + } + return file; + } } \ No newline at end of file diff --git a/src/BitMono.CLI/Modules/CLIOptionsObfuscationNeedsFactory.cs b/src/BitMono.CLI/Modules/CLIOptionsObfuscationNeedsFactory.cs index f28598c5..72d89c86 100644 --- a/src/BitMono.CLI/Modules/CLIOptionsObfuscationNeedsFactory.cs +++ b/src/BitMono.CLI/Modules/CLIOptionsObfuscationNeedsFactory.cs @@ -1,6 +1,5 @@ namespace BitMono.CLI.Modules; -[SuppressMessage("ReSharper", "InconsistentNaming")] public class CLIOptionsObfuscationNeedsFactory { private readonly string[] _args; @@ -16,7 +15,7 @@ public CLIOptionsObfuscationNeedsFactory(string[] args, } [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] - public ObfuscationNeeds? Create() + public ObfuscationNeeds? Create(CancellationToken cancellationToken) { var parser = new Parser(with => { diff --git a/src/BitMono.CLI/Modules/ObfuscationNeedsFactory.cs b/src/BitMono.CLI/Modules/ObfuscationNeedsFactory.cs index a307b404..47fc8d67 100644 --- a/src/BitMono.CLI/Modules/ObfuscationNeedsFactory.cs +++ b/src/BitMono.CLI/Modules/ObfuscationNeedsFactory.cs @@ -14,10 +14,10 @@ public ObfuscationNeedsFactory(string[] args, _logger = logger; } - public ObfuscationNeeds? Create() + public ObfuscationNeeds? Create(CancellationToken cancellationToken) { return _args.IsEmpty() - ? new CLIObfuscationNeedsFactory(_args, _obfuscationSettings, _logger).Create() - : new CLIOptionsObfuscationNeedsFactory(_args, _obfuscationSettings, _logger).Create(); + ? new CLIObfuscationNeedsFactory(_args, _obfuscationSettings, _logger).Create(cancellationToken) + : new CLIOptionsObfuscationNeedsFactory(_args, _obfuscationSettings, _logger).Create(cancellationToken); } } \ No newline at end of file diff --git a/src/BitMono.CLI/Program.cs b/src/BitMono.CLI/Program.cs index c91239fd..b75f7745 100644 --- a/src/BitMono.CLI/Program.cs +++ b/src/BitMono.CLI/Program.cs @@ -3,6 +3,7 @@ namespace BitMono.CLI; internal class Program { private static readonly CancellationTokenSource CancellationTokenSource = new(); + private static CancellationToken CancellationToken => CancellationTokenSource.Token; private static readonly string BitMonoFileVersionText = $"BitMono v{FileVersionInfo.GetVersionInfo(typeof(Program).Assembly.Location).FileVersion}"; private static readonly string AsciiArt = @$" @@ -33,13 +34,15 @@ private static async Task Main(string[] args) var logger = serviceProvider .GetRequiredService() .ForContext(); - var needs = new ObfuscationNeedsFactory(args, obfuscation, logger).Create(); + var needs = new ObfuscationNeedsFactory(args, obfuscation, logger).Create(CancellationToken); if (needs == null) { statusCode = KnownReturnStatuses.Failure; return statusCode; } + CancellationToken.ThrowIfCancellationRequested(); + if (obfuscation.ClearCLI) { Console.Clear(); @@ -62,7 +65,14 @@ private static async Task Main(string[] args) if (obfuscation.OpenFileDestinationInFileExplorer) { - Process.Start(needs.OutputPath); + try + { + Process.Start(needs.OutputPath); + } + catch (Exception ex) + { + logger.Error(ex, "An error occured while opening the destination file in explorer!"); + } } } catch (OperationCanceledException) @@ -83,7 +93,7 @@ private static async Task Main(string[] args) return statusCode; } - private static void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e) + private static void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) { CancellationTokenSource.Cancel(); e.Cancel = true; diff --git a/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs index 85edad93..a0f977f7 100644 --- a/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalBaseTypesCriticalAnalyzer.cs @@ -1,6 +1,5 @@ namespace BitMono.Core.Analyzing; -[UsedImplicitly] [SuppressMessage("ReSharper", "InvertIf")] public class CriticalBaseTypesCriticalAnalyzer : ICriticalAnalyzer { diff --git a/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs index 448aac87..229bf131 100644 --- a/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalInterfacesCriticalAnalyzer.cs @@ -1,7 +1,5 @@ namespace BitMono.Core.Analyzing; -[UsedImplicitly] -[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")] public class CriticalInterfacesCriticalAnalyzer : ICriticalAnalyzer { private readonly CriticalsSettings _criticalsSettings; @@ -18,7 +16,7 @@ public bool NotCriticalToMakeChanges(TypeDefinition type) return true; } var criticalInterfaces = _criticalsSettings.CriticalInterfaces; - if (type.Interfaces.Any(i => criticalInterfaces.FirstOrDefault(c => c.Equals(i.Interface?.Name)) != null)) + if (type.Interfaces.Any(x => criticalInterfaces.FirstOrDefault(xx => xx.Equals(x.Interface?.Name)) != null)) { return false; } diff --git a/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs index 9ead842b..2ca6e9a4 100644 --- a/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalMethodsCriticalAnalyzer.cs @@ -17,6 +17,6 @@ public bool NotCriticalToMakeChanges(MethodDefinition method) return true; } var criticalMethodNames = _criticalsSettings.CriticalMethods; - return criticalMethodNames.Any(c => c.Equals(method.Name)) == false; + return criticalMethodNames.Any(x => x.Equals(method.Name)) == false; } } \ No newline at end of file diff --git a/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs b/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs index 5c407387..a7806891 100644 --- a/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/CriticalMethodsStartsWithAnalyzer.cs @@ -1,6 +1,5 @@ namespace BitMono.Core.Analyzing; -[UsedImplicitly] public class CriticalMethodsStartsWithAnalyzer : ICriticalAnalyzer { private readonly CriticalsSettings _criticalsSettings; diff --git a/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs index b04e23a8..ad793399 100644 --- a/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/ModelAttributeCriticalAnalyzer.cs @@ -1,6 +1,5 @@ namespace BitMono.Core.Analyzing; -[UsedImplicitly] [SuppressMessage("ReSharper", "ForCanBeConvertedToForeach")] [SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")] public class ModelAttributeCriticalAnalyzer : ICriticalAnalyzer diff --git a/src/BitMono.Core/Analyzing/NameCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/NameCriticalAnalyzer.cs index 4776a157..0e42bff4 100644 --- a/src/BitMono.Core/Analyzing/NameCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/NameCriticalAnalyzer.cs @@ -1,6 +1,5 @@ namespace BitMono.Core.Analyzing; -[UsedImplicitly] [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")] public class NameCriticalAnalyzer : ICriticalAnalyzer, diff --git a/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs index 89f4bbca..9ec0b673 100644 --- a/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/ReflectionCriticalAnalyzer.cs @@ -4,23 +4,23 @@ namespace BitMono.Core.Analyzing; public class ReflectionCriticalAnalyzer : ICriticalAnalyzer { private readonly ObfuscationSettings _obfuscationSettings; - private readonly List m_CachedMethods; + private readonly List _cachedMethods; private static readonly string[] ReflectionMethods = - { + [ nameof(Type.GetMethod), nameof(Type.GetField), nameof(Type.GetProperty), nameof(Type.GetEvent), - nameof(Type.GetMember), - }; + nameof(Type.GetMember) + ]; public ReflectionCriticalAnalyzer(IOptions obfuscation) { _obfuscationSettings = obfuscation.Value; - m_CachedMethods = new List(); + _cachedMethods = new List(); } - public IReadOnlyList CachedMethods => m_CachedMethods.AsReadOnly(); + public IReadOnlyList CachedMethods => _cachedMethods.AsReadOnly(); public bool NotCriticalToMakeChanges(MethodDefinition method) { @@ -28,7 +28,7 @@ public bool NotCriticalToMakeChanges(MethodDefinition method) { return true; } - if (m_CachedMethods.FirstOrDefault(r => r.Name.Equals(method.Name)) != null) + if (_cachedMethods.FirstOrDefault(x => x.Name.Equals(method.Name)) != null) { return false; } @@ -52,9 +52,9 @@ public bool NotCriticalToMakeChanges(MethodDefinition method) foreach (var possibleMethod in method.Module .FindMembers() .OfType() - .Where(m => m.Name.Equals(traceMethodName))) + .Where(x => x.Name.Equals(traceMethodName))) { - m_CachedMethods.Add(possibleMethod); + _cachedMethods.Add(possibleMethod); return false; } } diff --git a/src/BitMono.Core/Analyzing/RuntimeCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/RuntimeCriticalAnalyzer.cs index b53070d1..5a18f4c2 100644 --- a/src/BitMono.Core/Analyzing/RuntimeCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/RuntimeCriticalAnalyzer.cs @@ -1,6 +1,5 @@ namespace BitMono.Core.Analyzing; -[UsedImplicitly] [SuppressMessage("ReSharper", "MergeIntoPattern")] public class RuntimeCriticalAnalyzer : ICriticalAnalyzer { diff --git a/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs b/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs index 0aaa78d1..33b209d5 100644 --- a/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs +++ b/src/BitMono.Core/Analyzing/SpecificNamespaceCriticalAnalyzer.cs @@ -1,6 +1,5 @@ namespace BitMono.Core.Analyzing; -[UsedImplicitly] [SuppressMessage("ReSharper", "ConvertIfStatementToSwitchStatement")] public class SpecificNamespaceCriticalAnalyzer : ICriticalAnalyzer { diff --git a/src/BitMono.Core/Attributes/RuntimeMonikerAttribute.cs b/src/BitMono.Core/Attributes/RuntimeMonikerAttribute.cs index 8f20160e..b0205e75 100644 --- a/src/BitMono.Core/Attributes/RuntimeMonikerAttribute.cs +++ b/src/BitMono.Core/Attributes/RuntimeMonikerAttribute.cs @@ -2,8 +2,8 @@ namespace BitMono.Core.Attributes; /// /// Represents a mechanism that specifies the runtime moniker of the protection. -/// i.e if you see this attribute on protection then only specified attributes are the supported runtime monikers for the protection. -/// If you don't see any of the attributes then it works everywhere, also, users will get a message via +/// i.e if you see this attribute on protection, then only specified attributes are the supported runtime monikers for the protection. +/// If you don't see any of the attributes, then it works everywhere, also, users will get a message via /// [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] public abstract class RuntimeMonikerAttribute : Attribute diff --git a/src/BitMono.Core/Configuration/JsonConfigurationProviderEx.cs b/src/BitMono.Core/Configuration/JsonConfigurationProviderEx.cs index 007950b4..4a38cf58 100644 --- a/src/BitMono.Core/Configuration/JsonConfigurationProviderEx.cs +++ b/src/BitMono.Core/Configuration/JsonConfigurationProviderEx.cs @@ -16,27 +16,23 @@ public JsonConfigurationProviderEx(JsonConfigurationSourceEx source) : base(sour public override void Load(Stream stream) { - using (var streamReader = new StreamReader(stream, true)) + 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 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.Message + ".", ex); - } - } + 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) diff --git a/src/BitMono.Core/Contexts/ProtectionContext.cs b/src/BitMono.Core/Contexts/ProtectionContext.cs index c5963f54..3a53173d 100644 --- a/src/BitMono.Core/Contexts/ProtectionContext.cs +++ b/src/BitMono.Core/Contexts/ProtectionContext.cs @@ -26,4 +26,27 @@ public void ThrowIfCancellationTokenRequested() { CancellationToken.ThrowIfCancellationRequested(); } + /// + /// This is necessary to make native code work inside the assembly. + /// See more here: https://docs.washi.dev/asmresolver/guides/dotnet/unmanaged-method-bodies.html + /// However, sometimes it causes issues with the assembly like `System.BadImageFormatException` + /// at the end when running the protected file, so that's why it's here but not at some startup point. + /// + public void ConfigureForNativeCode() + { + Module.IsILOnly = false; + var x64 = Module.MachineType == MachineType.Amd64; + if (x64) + { + Module.PEKind = OptionalHeaderMagic.PE32Plus; + Module.MachineType = MachineType.Amd64; + Module.IsBit32Required = false; + } + else + { + Module.PEKind = OptionalHeaderMagic.PE32; + Module.MachineType = MachineType.I386; + Module.IsBit32Required = true; + } + } } \ No newline at end of file diff --git a/src/BitMono.Core/Resolvers/SafeToMakeChangesMemberResolver.cs b/src/BitMono.Core/Resolvers/SafeToMakeChangesMemberResolver.cs index 6d295cbb..905c8fce 100644 --- a/src/BitMono.Core/Resolvers/SafeToMakeChangesMemberResolver.cs +++ b/src/BitMono.Core/Resolvers/SafeToMakeChangesMemberResolver.cs @@ -4,11 +4,11 @@ [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")] public class SafeToMakeChangesMemberResolver : IMemberResolver { - private readonly ObfuscationAttributeResolver m_ObfuscationAttributeResolver; - private readonly ObfuscateAssemblyAttributeResolver m_ObfuscateAssemblyAttributeResolver; - private readonly CriticalAttributeResolver m_CriticalAttributeResolver; - private readonly SerializableBitCriticalAnalyzer m_SerializableBitCriticalAnalyzer; - private readonly SpecificNamespaceCriticalAnalyzer m_SpecificNamespaceCriticalAnalyzer; + private readonly ObfuscationAttributeResolver _obfuscationAttributeResolver; + private readonly ObfuscateAssemblyAttributeResolver _obfuscateAssemblyAttributeResolver; + private readonly CriticalAttributeResolver _criticalAttributeResolver; + private readonly SerializableBitCriticalAnalyzer _serializableBitCriticalAnalyzer; + private readonly SpecificNamespaceCriticalAnalyzer _specificNamespaceCriticalAnalyzer; public SafeToMakeChangesMemberResolver( ObfuscationAttributeResolver obfuscationAttributeResolver, @@ -17,11 +17,11 @@ public SafeToMakeChangesMemberResolver( SerializableBitCriticalAnalyzer serializableBitCriticalAnalyzer, SpecificNamespaceCriticalAnalyzer specificNamespaceCriticalAnalyzer) { - m_ObfuscationAttributeResolver = obfuscationAttributeResolver; - m_ObfuscateAssemblyAttributeResolver = obfuscateAssemblyAttributeResolver; - m_CriticalAttributeResolver = criticalAttributeResolver; - m_SerializableBitCriticalAnalyzer = serializableBitCriticalAnalyzer; - m_SpecificNamespaceCriticalAnalyzer = specificNamespaceCriticalAnalyzer; + _obfuscationAttributeResolver = obfuscationAttributeResolver; + _obfuscateAssemblyAttributeResolver = obfuscateAssemblyAttributeResolver; + _criticalAttributeResolver = criticalAttributeResolver; + _serializableBitCriticalAnalyzer = serializableBitCriticalAnalyzer; + _specificNamespaceCriticalAnalyzer = specificNamespaceCriticalAnalyzer; } public bool Resolve(IProtection protection, IMetadataMember member) @@ -29,33 +29,33 @@ public bool Resolve(IProtection protection, IMetadataMember member) if (member is IHasCustomAttribute customAttribute) { var feature = protection.GetName(); - if (m_ObfuscationAttributeResolver.Resolve(feature, customAttribute, out var obfuscationAttributeData)) + if (_obfuscationAttributeResolver.Resolve(feature, customAttribute, out var obfuscationAttributeData)) { if (obfuscationAttributeData!.Exclude) { return false; } } - if (m_ObfuscateAssemblyAttributeResolver.Resolve(null, customAttribute, out var obfuscateAssemblyAttributeData)) + if (_obfuscateAssemblyAttributeResolver.Resolve(null, customAttribute, out var obfuscateAssemblyAttributeData)) { if (obfuscateAssemblyAttributeData!.AssemblyIsPrivate) { return false; } } - if (m_CriticalAttributeResolver.Resolve(feature, customAttribute)) + if (_criticalAttributeResolver.Resolve(feature, customAttribute)) { return false; } } if (member is TypeDefinition type) { - if (m_SerializableBitCriticalAnalyzer.NotCriticalToMakeChanges(type) == false) + if (_serializableBitCriticalAnalyzer.NotCriticalToMakeChanges(type) == false) { return false; } } - if (m_SpecificNamespaceCriticalAnalyzer.NotCriticalToMakeChanges(member) == false) + if (_specificNamespaceCriticalAnalyzer.NotCriticalToMakeChanges(member) == false) { return false; } diff --git a/src/BitMono.Host/obfuscation.json b/src/BitMono.Host/obfuscation.json index 05fe6e6b..3f5bbe26 100644 --- a/src/BitMono.Host/obfuscation.json +++ b/src/BitMono.Host/obfuscation.json @@ -70,9 +70,6 @@ // Opens directory with protected file in file explorer in top of the screen, if set to true "OpenFileDestinationInFileExplorer": true, - // Allow changes to next properties like: bits, machine type to the module (sometimes it cause issues so disabling it may help) - "AllowPotentialBreakingChangesToModule": true, - // Set to true to enable, in cases when you want to obfuscate only the specific namespace(s) "SpecificNamespacesObfuscationOnly": false, "SpecificNamespaces": [ diff --git a/src/BitMono.Host/protections.json b/src/BitMono.Host/protections.json index 4eacce98..ee9e0f30 100644 --- a/src/BitMono.Host/protections.json +++ b/src/BitMono.Host/protections.json @@ -2,7 +2,7 @@ "Protections": [ { "Name": "AntiILdasm", - "Enabled": false + "Enabled": true }, { "Name": "AntiDe4dot", @@ -52,6 +52,10 @@ "Name": "BitMethodDotnet", "Enabled": false }, + { + "Name": "BitDecompiler", + "Enabled": false + }, { "Name": "BitTimeDateStamp", "Enabled": false diff --git a/src/BitMono.Obfuscation/BitMonoObfuscator.cs b/src/BitMono.Obfuscation/BitMonoObfuscator.cs index 43f0f6aa..6cb73525 100644 --- a/src/BitMono.Obfuscation/BitMonoObfuscator.cs +++ b/src/BitMono.Obfuscation/BitMonoObfuscator.cs @@ -1,6 +1,5 @@ namespace BitMono.Obfuscation; -[SuppressMessage("ReSharper", "ForCanBeConvertedToForeach")] [SuppressMessage("ReSharper", "InvertIf")] [SuppressMessage("ReSharper", "ParameterTypeCanBeEnumerable.Local")] public class BitMonoObfuscator @@ -49,10 +48,11 @@ public async Task ProtectAsync() _invokablePipeline.OnFail += OnFailHandleAsync; await _invokablePipeline.InvokeAsync(OutputLoadedModuleAsync); + await _invokablePipeline.InvokeAsync(OutputBitMonoInfoAsync); + await _invokablePipeline.InvokeAsync(OutputCompatibilityIssuesAsync); await _invokablePipeline.InvokeAsync(SortProtectionsAsync); await _invokablePipeline.InvokeAsync(OutputProtectionsAsync); await _invokablePipeline.InvokeAsync(StartTimeCounterAsync); - await _invokablePipeline.InvokeAsync(OutputFrameworkInformationAsync); await _invokablePipeline.InvokeAsync(ResolveDependenciesAsync); await _invokablePipeline.InvokeAsync(ExpandMacrosAsync); await _invokablePipeline.InvokeAsync(RunProtectionsAsync); @@ -66,21 +66,56 @@ public async Task ProtectAsync() private Task OutputLoadedModuleAsync() { - var targetFrameworkName = "unknown"; - if (_context.Module.Assembly!.TryGetTargetFramework(out var info)) + var targetFrameworkText = "unknown"; + var module = _context.Module; + var assembly = module.Assembly; + if (assembly!.TryGetTargetFramework(out var info)) { - targetFrameworkName = info.Name; + targetFrameworkText = $"{info.Name} {info.Version}"; } - var assemblyInfo = _context.Module.Assembly.ToString(); - var culture = _context.Module.Assembly.Culture ?? "unknown"; - var timeDateStamp = _context.Module.ToPEImage().TimeDateStamp; + var assemblyInfo = assembly.ToString(); + var culture = assembly.Culture?.ToString() ?? "unknown"; + var timeDateStamp = module.ToPEImage().TimeDateStamp; _logger.Information("Module {0}", assemblyInfo); - _logger.Information("Module Target Framework: {0}", targetFrameworkName); - _logger.Information("PE TimeDateStamp: {0}", timeDateStamp); + _logger.Information("Module Target Framework: {0}", targetFrameworkText); + _logger.Information("Module PE TimeDateStamp: {0}", timeDateStamp); _logger.Information("Module culture: {0}", culture); return Task.FromResult(true); } + private Task OutputBitMonoInfoAsync() + { + _logger.Information(EnvironmentRuntimeInformation.Create().ToString()); + return Task.FromResult(true); + } + /// + /// Outputs information in case of module is built for .NET Framework, + /// but BitMono is running on .NET Core, or vice versa. + /// See more info: https://bitmono.readthedocs.io/en/latest/obfuscationissues/corlib-not-found.html + /// + private Task OutputCompatibilityIssuesAsync() + { + if (_context.Module.Assembly!.TryGetTargetFramework(out var targetAssemblyRuntime) == false) + { + return Task.FromResult(true); + } + if (targetAssemblyRuntime.IsNetCoreApp && DotNetRuntimeInfoEx.IsNetFramework()) + { + _logger.Warning( + "The module is built for .NET (Core), but you're using a version of BitMono intended for .NET Framework." + + " To avoid potential issues, ensure the target framework matches the BitMono framework, " + + "or switch to a .NET Core build of BitMono."); + return Task.FromResult(true); + } + if (targetAssemblyRuntime.IsNetFramework && DotNetRuntimeInfoEx.IsNetCoreOrLater()) + { + _logger.Warning( + "The module is built for .NET Framework, but you're using a version of BitMono intended for .NET (Core)." + + " To avoid potential issues, ensure the target framework matches the BitMono framework, " + + "or switch to a .NET Framework build of BitMono."); + } + return Task.FromResult(true); + } private Task SortProtectionsAsync() { var protectionSettings = _serviceProvider.GetRequiredService>().Value.Protections!; @@ -111,11 +146,6 @@ private Task StartTimeCounterAsync() _startTime = Stopwatch.GetTimestamp(); return Task.FromResult(true); } - private Task OutputFrameworkInformationAsync() - { - _logger.Information(RuntimeUtilities.GetFrameworkInformation().ToString()); - return Task.FromResult(true); - } private Task ResolveDependenciesAsync() { _logger.Information("Starting resolving dependencies..."); @@ -137,7 +167,10 @@ private Task ResolveDependenciesAsync() { if (_obfuscationSettings.FailOnNoRequiredDependency) { - _logger.Fatal("Please, specify needed dependencies, or set in {0} FailOnNoRequiredDependency to false", "obfuscation.json"); + _logger.Fatal("Please, specify needed dependencies, or set in {0} FailOnNoRequiredDependency to false", + "obfuscation.json"); + _logger.Warning( + "Unresolved dependencies aren't a major issue, but keep in mind they can cause problems or might result in some parts being missed during obfuscation."); return Task.FromResult(false); } } diff --git a/src/BitMono.Obfuscation/Modules/ModuleFactory.cs b/src/BitMono.Obfuscation/Modules/ModuleFactory.cs index cf22857e..3225ca5a 100644 --- a/src/BitMono.Obfuscation/Modules/ModuleFactory.cs +++ b/src/BitMono.Obfuscation/Modules/ModuleFactory.cs @@ -23,22 +23,6 @@ public ModuleFactoryResult Create() PEReaderParameters = new PEReaderParameters(_errorListener) }; var module = ModuleDefinition.FromBytes(_bytes, moduleReaderParameters); - if (_obfuscationSettings.AllowPotentialBreakingChangesToModule) - { - module.Attributes &= ~DotNetDirectoryFlags.ILOnly; - var x86 = module.MachineType == MachineType.I386; - if (x86) - { - module.PEKind = OptionalHeaderMagic.PE32; - module.MachineType = MachineType.I386; - module.Attributes |= DotNetDirectoryFlags.Bit32Required; - } - else - { - module.PEKind = OptionalHeaderMagic.PE32Plus; - module.MachineType = MachineType.Amd64; - } - } var managedPEImageBuilder = new ManagedPEImageBuilder(new DotNetDirectoryFactory(_metadataBuilderFlags), _errorListener); diff --git a/src/BitMono.Obfuscation/Notifiers/ProtectionsNotifier.cs b/src/BitMono.Obfuscation/Notifiers/ProtectionsNotifier.cs index 88c22b03..07e67dea 100644 --- a/src/BitMono.Obfuscation/Notifiers/ProtectionsNotifier.cs +++ b/src/BitMono.Obfuscation/Notifiers/ProtectionsNotifier.cs @@ -11,8 +11,6 @@ public ProtectionsNotifier(ObfuscationSettings obfuscationSettings, ILogger logg _logger = logger.ForContext(); } - [SuppressMessage("ReSharper", "InvertIf")] - [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] public void Notify(ProtectionsSort protectionsSort) { if (_obfuscationSettings.NotifyProtections) @@ -45,7 +43,7 @@ public void Notify(ProtectionsSort protectionsSort) if (protectionsSort.ProtectionsResolve.DisabledProtections.Any()) { var disabledProtectionsCount = protectionsSort.ProtectionsResolve.DisabledProtections.Count; - _logger.Warning("({0}) Disabled protection(s): {1}", disabledProtectionsCount, string.Join(", ", protectionsSort.ProtectionsResolve.DisabledProtections.Select(p => p ?? "Unnamed Protection"))); + _logger.Information("({0}) Disabled protection(s): {1}", disabledProtectionsCount, string.Join(", ", protectionsSort.ProtectionsResolve.DisabledProtections.Select(p => p ?? "Unnamed Protection"))); } if (protectionsSort.ProtectionsResolve.UnknownProtections.Any()) { @@ -53,7 +51,7 @@ public void Notify(ProtectionsSort protectionsSort) } if (protectionsSort.ObfuscationAttributeExcludeProtections.Any()) { - _logger.Warning("Skip protection(s) with obfuscation attribute: {0}", string.Join(", ", protectionsSort.ObfuscationAttributeExcludeProtections.Select(p => p?.GetName()))); + _logger.Information("Skip protection(s) with obfuscation attribute: {0}", string.Join(", ", protectionsSort.ObfuscationAttributeExcludeProtections.Select(p => p?.GetName()))); } } } diff --git a/src/BitMono.Protections/AntiDe4dot.cs b/src/BitMono.Protections/AntiDe4dot.cs index 5662da98..b352e800 100644 --- a/src/BitMono.Protections/AntiDe4dot.cs +++ b/src/BitMono.Protections/AntiDe4dot.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] public class AntiDe4dot : Protection { public AntiDe4dot(IServiceProvider serviceProvider) : base(serviceProvider) diff --git a/src/BitMono.Protections/AntiDebugBreakpoints.cs b/src/BitMono.Protections/AntiDebugBreakpoints.cs index 1040784c..07e58f80 100644 --- a/src/BitMono.Protections/AntiDebugBreakpoints.cs +++ b/src/BitMono.Protections/AntiDebugBreakpoints.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class AntiDebugBreakpoints : Protection { diff --git a/src/BitMono.Protections/AntiDecompiler.cs b/src/BitMono.Protections/AntiDecompiler.cs index 97c91544..94b784de 100644 --- a/src/BitMono.Protections/AntiDecompiler.cs +++ b/src/BitMono.Protections/AntiDecompiler.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [RuntimeMonikerMono] public class AntiDecompiler : PipelineProtection { diff --git a/src/BitMono.Protections/AntiILdasm.cs b/src/BitMono.Protections/AntiILdasm.cs index 4635d10e..ad00cb7d 100644 --- a/src/BitMono.Protections/AntiILdasm.cs +++ b/src/BitMono.Protections/AntiILdasm.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] public class AntiILdasm : Protection { public AntiILdasm(IServiceProvider serviceProvider) : base(serviceProvider) diff --git a/src/BitMono.Protections/BillionNops.cs b/src/BitMono.Protections/BillionNops.cs index ee500f3b..34526017 100644 --- a/src/BitMono.Protections/BillionNops.cs +++ b/src/BitMono.Protections/BillionNops.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] public class BillionNops : Protection { private readonly Renamer _renamer; @@ -21,6 +20,8 @@ public override Task ExecuteAsync() var body = method.CilMethodBody = new CilMethodBody(method); for (var i = 0; i < 100000; i++) { + Context.ThrowIfCancellationTokenRequested(); + body.Instructions.Insert(0, new CilInstruction(CilOpCodes.Nop)); } body.Instructions.Add(new CilInstruction(CilOpCodes.Ret)); diff --git a/src/BitMono.Protections/BitDecompiler.cs b/src/BitMono.Protections/BitDecompiler.cs new file mode 100644 index 00000000..28ea5b81 --- /dev/null +++ b/src/BitMono.Protections/BitDecompiler.cs @@ -0,0 +1,54 @@ +namespace BitMono.Protections; + +[RuntimeMonikerMono] +public class BitDecompiler : PackerProtection +{ + public BitDecompiler(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + public override Task ExecuteAsync() + { + using (var stream = File.Open(Context.BitMonoContext.OutputFile, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) + using (var reader = new BinaryReader(stream)) + using (var writer = new BinaryWriter(stream)) + { + stream.Position = 0x3C; + var peHeader = reader.ReadUInt32(); + stream.Position = peHeader; + + //stream.Position += 0x2; + stream.Position += 0x6; + var numberOfSections = reader.ReadUInt16(); + + stream.Position += 0x10; + var x64PEOptionsHeader = reader.ReadUInt16() == 0x20B; + + stream.Position += x64PEOptionsHeader ? 0x38 : 0x28 + 0xA6; + var dotNetVirtualAddress = reader.ReadUInt32(); + + uint dotNetPointerRaw = 0; + stream.Position += 0xC; + for (var i = 0; i < numberOfSections; i++) + { + stream.Position += 0xC; + var virtualAddress = reader.ReadUInt32(); + var sizeOfRawData = reader.ReadUInt32(); + var pointerToRawData = reader.ReadUInt32(); + stream.Position += 0x10; + + if (dotNetVirtualAddress >= virtualAddress && dotNetVirtualAddress < virtualAddress + sizeOfRawData && dotNetPointerRaw == 0) + { + dotNetPointerRaw = dotNetVirtualAddress + pointerToRawData - virtualAddress; + } + } + + stream.Position = dotNetPointerRaw; + writer.Write(0); + writer.Write(0); + stream.Position += 0x4; + writer.Write(0); + } + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/BitMono.Protections/BitDotNet.cs b/src/BitMono.Protections/BitDotNet.cs index 6d9a5e2e..7916e0d5 100644 --- a/src/BitMono.Protections/BitDotNet.cs +++ b/src/BitMono.Protections/BitDotNet.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [RuntimeMonikerMono] public class BitDotNet : PackerProtection { diff --git a/src/BitMono.Protections/BitMethodDotnet.cs b/src/BitMono.Protections/BitMethodDotnet.cs index 45c9fcce..e4e93208 100644 --- a/src/BitMono.Protections/BitMethodDotnet.cs +++ b/src/BitMono.Protections/BitMethodDotnet.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class BitMethodDotnet : Protection { @@ -11,8 +10,6 @@ public BitMethodDotnet(RandomNext randomNext, IServiceProvider serviceProvider) _randomNext = randomNext; } - [SuppressMessage("ReSharper", "InvertIf")] - [SuppressMessage("ReSharper", "ConvertIfStatementToConditionalTernaryExpression")] public override Task ExecuteAsync() { foreach (var method in Context.Parameters.Members.OfType()) diff --git a/src/BitMono.Protections/BitMono.cs b/src/BitMono.Protections/BitMono.cs index f39510ce..65851378 100644 --- a/src/BitMono.Protections/BitMono.cs +++ b/src/BitMono.Protections/BitMono.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [RuntimeMonikerMono] public class BitMono : PackerProtection { diff --git a/src/BitMono.Protections/BitTimeDateStamp.cs b/src/BitMono.Protections/BitTimeDateStamp.cs index f62dc31c..47283077 100644 --- a/src/BitMono.Protections/BitTimeDateStamp.cs +++ b/src/BitMono.Protections/BitTimeDateStamp.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] public class BitTimeDateStamp : PackerProtection { public BitTimeDateStamp(IServiceProvider serviceProvider) : base(serviceProvider) diff --git a/src/BitMono.Protections/CallToCalli.cs b/src/BitMono.Protections/CallToCalli.cs index 39b1907b..dd3b1c39 100644 --- a/src/BitMono.Protections/CallToCalli.cs +++ b/src/BitMono.Protections/CallToCalli.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class CallToCalli : Protection { diff --git a/src/BitMono.Protections/DotNetHook.cs b/src/BitMono.Protections/DotNetHook.cs index 390009db..4d07e6cf 100644 --- a/src/BitMono.Protections/DotNetHook.cs +++ b/src/BitMono.Protections/DotNetHook.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] public class DotNetHook : Protection { private readonly Renamer _renamer; @@ -12,12 +11,10 @@ public DotNetHook(Renamer renamer, RandomNext randomNext, IServiceProvider servi _randomNext = randomNext; } - [SuppressMessage("ReSharper", "ForCanBeConvertedToForeach")] - [SuppressMessage("ReSharper", "InvertIf")] public override Task ExecuteAsync() { var runtimeHookingType = Context.RuntimeModule.ResolveOrThrow(typeof(Hooking)); - var runtimeRedirectStubMethod = runtimeHookingType.Methods.Single(c => c.Name!.Equals(nameof(Hooking.RedirectStub))); + var runtimeRedirectStubMethod = runtimeHookingType.Methods.Single(x => x.Name!.Equals(nameof(Hooking.RedirectStub))); var listener = new ModifyInjectTypeClonerListener(ModifyFlags.All, _renamer, Context.Module); var memberCloneResult = new MemberCloner(Context.Module, listener) .Include(runtimeHookingType) diff --git a/src/BitMono.Protections/FullRenamer.cs b/src/BitMono.Protections/FullRenamer.cs index 33967b2b..d31b3e7f 100644 --- a/src/BitMono.Protections/FullRenamer.cs +++ b/src/BitMono.Protections/FullRenamer.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime | MemberInclusionFlags.Model | MemberInclusionFlags.Reflection)] public class FullRenamer : Protection { diff --git a/src/BitMono.Protections/GlobalUsings.cs b/src/BitMono.Protections/GlobalUsings.cs index a3ed4d5b..4984bc2c 100644 --- a/src/BitMono.Protections/GlobalUsings.cs +++ b/src/BitMono.Protections/GlobalUsings.cs @@ -15,6 +15,7 @@ global using System.Threading; global using System.Threading.Tasks; global using AsmResolver.DotNet.Code.Native; +global using AsmResolver.PE.File; global using BitMono.API.Protections; global using BitMono.Core; global using BitMono.Core.Attributes; diff --git a/src/BitMono.Protections/NoNamespaces.cs b/src/BitMono.Protections/NoNamespaces.cs index daa3dd97..cc42b763 100644 --- a/src/BitMono.Protections/NoNamespaces.cs +++ b/src/BitMono.Protections/NoNamespaces.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class NoNamespaces : Protection { diff --git a/src/BitMono.Protections/ObjectReturnType.cs b/src/BitMono.Protections/ObjectReturnType.cs index 2937cbd1..c681b9db 100644 --- a/src/BitMono.Protections/ObjectReturnType.cs +++ b/src/BitMono.Protections/ObjectReturnType.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class ObjectReturnType : Protection { diff --git a/src/BitMono.Protections/StringsEncryption.cs b/src/BitMono.Protections/StringsEncryption.cs index 325b858d..82e0fca1 100644 --- a/src/BitMono.Protections/StringsEncryption.cs +++ b/src/BitMono.Protections/StringsEncryption.cs @@ -1,6 +1,5 @@ namespace BitMono.Protections; -[UsedImplicitly] [DoNotResolve(MemberInclusionFlags.SpecialRuntime)] public class StringsEncryption : Protection { diff --git a/src/BitMono.Protections/UnmanagedString.cs b/src/BitMono.Protections/UnmanagedString.cs index 603e1f6f..348bb308 100644 --- a/src/BitMono.Protections/UnmanagedString.cs +++ b/src/BitMono.Protections/UnmanagedString.cs @@ -1,11 +1,7 @@ namespace BitMono.Protections; -[UsedImplicitly] -[SuppressMessage("ReSharper", "InvertIf")] [RuntimeMonikerNETCore] [RuntimeMonikerNETFramework] -[SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")] -[SuppressMessage("ReSharper", "ForCanBeConvertedToForeach")] public class UnmanagedString : Protection { public UnmanagedString(IServiceProvider serviceProvider) : base(serviceProvider) @@ -14,6 +10,8 @@ public UnmanagedString(IServiceProvider serviceProvider) : base(serviceProvider) public override Task ExecuteAsync() { + Context.ConfigureForNativeCode(); + var moduleImporter = Context.ModuleImporter; var stringSbytePointerCtor = moduleImporter.ImportMethod(typeof(string).GetConstructor(new[] { typeof(sbyte*) })!); diff --git a/src/BitMono.Shared/Models/ObfuscationSettings.cs b/src/BitMono.Shared/Models/ObfuscationSettings.cs index aa7421e7..ee882634 100644 --- a/src/BitMono.Shared/Models/ObfuscationSettings.cs +++ b/src/BitMono.Shared/Models/ObfuscationSettings.cs @@ -18,7 +18,6 @@ public class ObfuscationSettings public bool FailOnNoRequiredDependency { get; set; } public bool OutputRuntimeMonikerWarnings { get; set; } public bool OpenFileDestinationInFileExplorer { get; set; } - public bool AllowPotentialBreakingChangesToModule { get; set; } public bool SpecificNamespacesObfuscationOnly { get; set; } public string[]? SpecificNamespaces { get; set; } public string[]? RandomStrings { get; set; } diff --git a/src/BitMono.Utilities/GlobalUsings.cs b/src/BitMono.Utilities/GlobalUsings.cs index c4f50a8e..a94b5ef5 100644 --- a/src/BitMono.Utilities/GlobalUsings.cs +++ b/src/BitMono.Utilities/GlobalUsings.cs @@ -13,6 +13,9 @@ global using System.Linq; global using System.Reflection; global using System.Runtime.CompilerServices; +global using System.Runtime.InteropServices; +global using System.Runtime.Versioning; +global using System.Text; global using AsmResolver.DotNet.Signatures; global using AsmResolver.DotNet.Signatures.Parsing; global using TypeAttributes = AsmResolver.PE.DotNet.Metadata.Tables.TypeAttributes; \ No newline at end of file diff --git a/src/BitMono.Utilities/Runtime/DotNetRuntimeInfoEx.cs b/src/BitMono.Utilities/Runtime/DotNetRuntimeInfoEx.cs new file mode 100644 index 00000000..b46751ec --- /dev/null +++ b/src/BitMono.Utilities/Runtime/DotNetRuntimeInfoEx.cs @@ -0,0 +1,85 @@ +namespace BitMono.Utilities.Runtime; + +public static class DotNetRuntimeInfoEx +{ + /// + /// Retrieves the description of the .NET runtime (e.g., ".NET Core 3.1", ".NET 5.0"). + /// + public static string GetFrameworkDescription() + { + return RuntimeInformation.FrameworkDescription; + } + /// + /// Try to get the .NET Framework version (only works if running on .NET Framework). + /// + public static Version? GetNetFrameworkVersion() + { + if (Type.GetType("System.Runtime.Versioning.TargetFrameworkAttribute") == null) + { + return null; + } + var targetFrameworkAttribute = (TargetFrameworkAttribute)Assembly + .GetEntryAssembly() + ?.GetCustomAttribute(typeof(TargetFrameworkAttribute)); + if (targetFrameworkAttribute == null) + { + return null; + } + var version = targetFrameworkAttribute.FrameworkName; + if (version.Contains('v') == false) + { + return null; + } + return new Version(version.Substring(version.IndexOf('v') + 1)); + } + /// + /// Checks if Runtime is .NET (Core). + /// + public static bool IsNetCoreOrLater() + { + var frameworkDescription = GetFrameworkDescription(); + return frameworkDescription.StartsWith(".NET Core") || + frameworkDescription.StartsWith(".NET ") && char.IsDigit(frameworkDescription[5]); + } + /// + /// Checks if Runtime is .NET Framework. + /// + public static bool IsNetFramework() + { + var frameworkDescription = GetFrameworkDescription(); + return frameworkDescription.StartsWith(".NET Framework"); + } + /// + /// Checks if the application is running on Mono. + /// + public static bool IsRunningOnMono() + { + return Type.GetType("Mono.Runtime") != null; + } + public static string? GetMonoDisplayName() + { + if (IsRunningOnMono() == false) + { + return null; + } + var monoType = Type.GetType("Mono.Runtime"); + if (monoType == null) + { + return null; + } + var displayNameMethod = monoType.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); + if (displayNameMethod == null) + { + return null; + } + return displayNameMethod.Invoke(null, null)?.ToString(); + } + public static int GetArchitectureBits() + { + return IntPtr.Size * 8; + } + public static OperatingSystem GetOperatingSystem() + { + return Environment.OSVersion; + } +} \ No newline at end of file diff --git a/src/BitMono.Utilities/Runtime/EnvironmentRuntimeInformation.cs b/src/BitMono.Utilities/Runtime/EnvironmentRuntimeInformation.cs index 770208d5..ef5eaf82 100644 --- a/src/BitMono.Utilities/Runtime/EnvironmentRuntimeInformation.cs +++ b/src/BitMono.Utilities/Runtime/EnvironmentRuntimeInformation.cs @@ -6,11 +6,37 @@ public class EnvironmentRuntimeInformation public OperatingSystem? OperatingSystem { get; set; } public int? Bits { get; set; } public bool HasMono { get; set; } - public Type? MonoType { get; set; } public string? MonoDisplayName { get; set; } + public static EnvironmentRuntimeInformation Create() + { + return new EnvironmentRuntimeInformation + { + NetFrameworkVersion = DotNetRuntimeInfoEx.GetNetFrameworkVersion(), + OperatingSystem = DotNetRuntimeInfoEx.GetOperatingSystem(), + Bits = DotNetRuntimeInfoEx.GetArchitectureBits(), + HasMono = DotNetRuntimeInfoEx.IsRunningOnMono(), + MonoDisplayName = DotNetRuntimeInfoEx.GetMonoDisplayName() + }; + } + /// + /// Retrieves detailed information about BitMono runtime, like running OS, .NET Framework/.NET Core version, etc. + /// + /// public override string ToString() { - return $"Running on {OperatingSystem}, {(HasMono == false ? $"{DotNetRuntimeInfo.NetFramework} v{NetFrameworkVersion}" : $"{MonoDisplayName}")}, x{Bits} bits"; + var stringBuilder = new StringBuilder(); + stringBuilder.Append("BitMono running on: "); + stringBuilder.Append($"OS: {OperatingSystem}, "); + if (HasMono) + { + stringBuilder.Append($"{MonoDisplayName} "); + } + else + { + stringBuilder.Append($"{DotNetRuntimeInfoEx.GetFrameworkDescription()} v{NetFrameworkVersion}, "); + } + stringBuilder.Append($"x{Bits} bits"); + return stringBuilder.ToString(); } } \ No newline at end of file diff --git a/src/BitMono.Utilities/Runtime/KnownMonoRuntimes.cs b/src/BitMono.Utilities/Runtime/KnownMonoRuntimes.cs deleted file mode 100644 index 51a40cad..00000000 --- a/src/BitMono.Utilities/Runtime/KnownMonoRuntimes.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace BitMono.Utilities.Runtime; - -public static class KnownMonoRuntimes -{ - public const string MonoTypeName = "Mono.Runtime"; - public const string GetDisplayName = "GetDisplayName"; -} \ No newline at end of file diff --git a/src/BitMono.Utilities/Runtime/RuntimeUtilities.cs b/src/BitMono.Utilities/Runtime/RuntimeUtilities.cs deleted file mode 100644 index 552456fc..00000000 --- a/src/BitMono.Utilities/Runtime/RuntimeUtilities.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace BitMono.Utilities.Runtime; - -public static class RuntimeUtilities -{ - private static EnvironmentRuntimeInformation _lastRuntimeInformation; - - public static EnvironmentRuntimeInformation GetFrameworkInformation() - { - if (_lastRuntimeInformation != null) - { - return _lastRuntimeInformation; - } - var dotnetFrameworkVersion = Environment.Version; - var operatingSystem = Environment.OSVersion; - var bits = IntPtr.Size * 8; - var monoType = Type.GetType(KnownMonoRuntimes.MonoTypeName); - var hasMono = monoType != null; - string? monoDisplayName = null; - if (hasMono) - { - var displayNameMethod = monoType.GetMethod(KnownMonoRuntimes.GetDisplayName, BindingFlags.NonPublic | BindingFlags.Static); - if (displayNameMethod != null) - { - monoDisplayName = displayNameMethod.Invoke(null, null) as string; - } - } - return _lastRuntimeInformation = new EnvironmentRuntimeInformation - { - OperatingSystem = operatingSystem, - NetFrameworkVersion = dotnetFrameworkVersion, - Bits = bits, - HasMono = hasMono, - MonoType = monoType, - MonoDisplayName = monoDisplayName - }; - } -} \ No newline at end of file diff --git a/test/BitMono.Benchmarks/RuntimeFrameworkInformationBenchmark.cs b/test/BitMono.Benchmarks/RuntimeFrameworkInformationBenchmark.cs index f52eb2f8..0c7fd8d4 100644 --- a/test/BitMono.Benchmarks/RuntimeFrameworkInformationBenchmark.cs +++ b/test/BitMono.Benchmarks/RuntimeFrameworkInformationBenchmark.cs @@ -6,6 +6,6 @@ public class RuntimeFrameworkInformationBenchmark [Benchmark] public void RuntimeInformation() { - RuntimeUtilities.GetFrameworkInformation(); + EnvironmentRuntimeInformation.Create(); } } \ No newline at end of file