Skip to content

Commit

Permalink
Merge pull request #103 from sunnamed434/feature/costura-fody-support
Browse files Browse the repository at this point in the history
Implement feature for costura fody resources as references
  • Loading branch information
sunnamed434 authored Feb 27, 2023
2 parents 97a3ace + f127f6d commit 365187b
Show file tree
Hide file tree
Showing 20 changed files with 178 additions and 87 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ Credits

**[Kao and his blogs][author_kao_blog]** thanks a lot of these blogs.

**[drakonia][author_drakonia]** for her **[costura decompressor][simple_costura_decompressor_source]**.

[test]: https://ci.appveyor.com/project/sunnamed434/bitmono/branch/main/tests
[codefactor]: https://www.codefactor.io/repository/github/sunnamed434/bitmono/overview/main
[deepsource]: https://deepsource.io/gh/sunnamed434/BitMono/?ref=repository-badge
Expand All @@ -154,11 +156,13 @@ Credits
[bitmethoddotnet_source]: https://github.com/sunnamed434/BitMethodDotnet
[dotnethook_source]: https://github.com/Elliesaur/DotNetHook
[confuserex_source]: https://github.com/yck1509/ConfuserEx
[simple_costura_decompressor_source]: https://github.com/dr4k0nia/Simple-Costura-Decompressor
[author_0x59r11]: https://github.com/0x59R11
[author_gazzi]: https://github.com/GazziFX
[author_ellisaur]: https://github.com/Elliesaur
[author_naweka]: https://github.com/naweka
[author_kao_blog]: https://lifeinhex.com/
[author_drakonia]: https://github.com/dr4k0nia
[author_sunnamed434]: https://github.com/sunnamed434
[appveyor_main_build]: https://ci.appveyor.com/project/sunnamed434/bitmono/branch/main
[appveyor_dev_build]: https://ci.appveyor.com/project/sunnamed434/bitmono/branch/dev
Expand Down
4 changes: 4 additions & 0 deletions docs/faq/costura-support.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Does BitMono provides Costura-Fody Support?
===========================================

Indeed, by default BitMono provides support for the Costura-Fody, resources will be resolved automatically, in this case, you don't need to have copies of your .DLLs or even set ``DisableCleanup`` to ``true`` in ``FodyWeavers.xml``.
10 changes: 9 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,12 @@ Table of Contents:

configuration/exclude-obfuscation
configuration/third-party-issues
configuration/protections
configuration/protections


.. toctree::
:maxdepth: 1
:caption: Frequently Asked Questions
:name: sec-faq

faq/costura-support
2 changes: 1 addition & 1 deletion src/BitMono.CLI/Modules/CLIObfuscationNeedsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public ObfuscationNeeds Create()
{
FileName = fileName,
FileBaseDirectory = fileBaseDirectory,
DependenciesDirectoryName = dependenciesDirectoryName,
ReferencesDirectoryName = dependenciesDirectoryName,
OutputDirectoryName = outputDirectoryName
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/BitMono.CLI/Modules/CLIOptionsObfuscationNeedsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public CLIOptionsObfuscationNeedsFactory(string[] args, ILogger logger)
{
FileName = options.File,
FileBaseDirectory = fileBaseDirectory,
DependenciesDirectoryName = options.Libraries.IsNullOrEmpty() == false
ReferencesDirectoryName = options.Libraries.IsNullOrEmpty() == false
? options.Libraries
: Path.Combine(fileBaseDirectory, "libs"),
OutputDirectoryName = options.Output.IsNullOrEmpty() == false
Expand All @@ -46,7 +46,7 @@ public CLIOptionsObfuscationNeedsFactory(string[] args, ILogger logger)
};

Directory.CreateDirectory(needs.OutputDirectoryName);
Directory.CreateDirectory(needs.DependenciesDirectoryName);
Directory.CreateDirectory(needs.ReferencesDirectoryName);
return needs;
}
}
2 changes: 1 addition & 1 deletion src/BitMono.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private static async Task Main(string[] args)

Console.Clear();
logger.Information("File: {0}", needs.FileName);
logger.Information("Dependencies (libs): {0}", needs.DependenciesDirectoryName);
logger.Information("Dependencies (libs): {0}", needs.ReferencesDirectoryName);
logger.Information("Everything is seems to be ok, starting obfuscation..");
logger.Information(AsciiArt);

Expand Down
7 changes: 4 additions & 3 deletions src/BitMono.Core/Resolvers/AssemblyResolver.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#pragma warning disable CS8602
namespace BitMono.Core.Resolvers;

public class AssemblyResolver
public static class AssemblyResolver
{
public AssemblyResolve Resolve(IEnumerable<byte[]> dependenciesData, ProtectionContext context)
public static AssemblyResolve Resolve(IEnumerable<byte[]> dependenciesData, ProtectionContext context)
{
context.ThrowIfCancellationRequested();

Expand All @@ -28,8 +28,9 @@ public AssemblyResolve Resolve(IEnumerable<byte[]> dependenciesData, ProtectionC
context.AssemblyResolver.AddToCache(originalReference, definition);
}
}
catch (BadImageFormatException)
catch (BadImageFormatException ex)
{
Console.WriteLine("originalRef: " + originalReference.Name + ", " + ex.ToString());
badImageReferences.Add(originalReference);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/BitMono.GUI/Pages/Obfuscation/Protect.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ public async Task ObfuscateAsync()
dependeciesData.Add(File.ReadAllBytes(dependencies[i]));
}

var dataResolver = new DependenciesDataResolver(_dependenciesDirectoryName);
var bitMonoContextFactory = new BitMonoContextFactory(dataResolver, obfuscation);
var bitMonoContext = bitMonoContextFactory.Create(_outputDirectoryName, _obfuscationFile.Name);
var dataResolver = new ReferencesDataResolver(_dependenciesDirectoryName);
//var bitMonoContextFactory = new BitMonoContextFactory(dataResolver, obfuscation);
//var bitMonoContext = bitMonoContextFactory.Create(_outputDirectoryName, _obfuscationFile.Name);
//var engine = new BitMonoEngine(obfuscationAttributeResolver, obfuscation, StoringProtections.Protections, MemberResolvers.ToList(), Protections.ToList(), Logger);
//await engine.StartAsync();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace BitMono.Obfuscation.Abstractions;

public class AutomaticReferencesDataResolver : IReferencesDataResolver
{
private readonly string _referencesDirectoryName;

public AutomaticReferencesDataResolver(string referencesDirectoryName)
{
_referencesDirectoryName = referencesDirectoryName;
}

[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
public IEnumerable<byte[]> Resolve(ModuleDefinition module)
{
var referencesData = new ReferencesDataResolver(_referencesDirectoryName).Resolve(module);
var costuraReferencesData = new CosturaReferencesDataResolver().Resolve(module);
if (costuraReferencesData.IsEmpty() == false)
{
return referencesData.Concat(costuraReferencesData);
}
return referencesData;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace BitMono.Obfuscation.Abstractions;

public class CosturaReferencesDataResolver : IReferencesDataResolver
{
private const string CosturaResourceNameStart = "costura.";
private const string CosturaResourceNameEnd = ".dll.compressed";
private const int MinCosturaResourceNameCharactersLenght = 19;

[SuppressMessage("ReSharper", "ForCanBeConvertedToForeach")]
[SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")]
[SuppressMessage("ReSharper", "InvertIf")]
public IEnumerable<byte[]> Resolve(ModuleDefinition module)
{
for (var i = 0; i < module.Resources.Count; i++)
{
var resource = module.Resources[i];
if (resource.IsEmbedded)
{
if (Utf8String.IsNullOrEmpty(resource.Name) == false)
{
var name = resource.Name.Value;
if (name.Length > MinCosturaResourceNameCharactersLenght)
{
if (name.StartsWith(CosturaResourceNameStart) && name.EndsWith(CosturaResourceNameEnd))
{
var data = resource.GetData();
if (data != null)
{
data = Decompress(data);
yield return data;
}
}
}
}
}
}
}

private static byte[] Decompress(byte[] data)
{
using var input = new MemoryStream(data);
using var output = new MemoryStream();
using var deflateStream = new DeflateStream(input, CompressionMode.Decompress);
deflateStream.CopyTo(output);
return output.ToArray();
}
}
21 changes: 0 additions & 21 deletions src/BitMono.Obfuscation/Abstractions/DependenciesDataResolver.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/BitMono.Obfuscation/Abstractions/ObfuscationNeeds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ public class ObfuscationNeeds
{
public string? FileName { get; set; }
public string? FileBaseDirectory { get; set; }
public string? DependenciesDirectoryName { get; set; }
public string? ReferencesDirectoryName { get; set; }
public string? OutputDirectoryName { get; set; }
}
21 changes: 21 additions & 0 deletions src/BitMono.Obfuscation/Abstractions/ReferencesDataResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace BitMono.Obfuscation.Abstractions;

public class ReferencesDataResolver : IReferencesDataResolver
{
private readonly string _referencesDirectoryName;

public ReferencesDataResolver(string referencesDirectoryName)
{
_referencesDirectoryName = referencesDirectoryName;
}

[SuppressMessage("ReSharper", "ForCanBeConvertedToForeach")]
public IEnumerable<byte[]> Resolve(ModuleDefinition module)
{
var dependencies = Directory.GetFiles(_referencesDirectoryName);
for (var i = 0; i < dependencies.Length; i++)
{
yield return File.ReadAllBytes(dependencies[i]);
}
}
}
49 changes: 24 additions & 25 deletions src/BitMono.Obfuscation/BitMonoEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@ namespace BitMono.Obfuscation;
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public class BitMonoEngine
{
private readonly ILifetimeScope m_LifetimeScope;
private readonly ObfuscationAttributeResolver m_ObfuscationAttributeResolver;
private readonly ObfuscateAssemblyAttributeResolver m_ObfuscateAssemblyAttributeResolver;
private readonly Shared.Models.Obfuscation m_Obfuscation;
private readonly List<ProtectionSetting> m_ProtectionSettings;
private readonly List<IMemberResolver> m_MemberResolvers;
private readonly ILogger m_Logger;
private readonly ILifetimeScope _lifetimeScope;
private readonly ObfuscationAttributeResolver _obfuscationAttributeResolver;
private readonly ObfuscateAssemblyAttributeResolver _obfuscateAssemblyAttributeResolver;
private readonly Shared.Models.Obfuscation _obfuscation;
private readonly List<ProtectionSetting> _protectionSettings;
private readonly List<IMemberResolver> _memberResolvers;
private readonly ILogger _logger;

public BitMonoEngine(ILifetimeScope lifetimeScope)
{
m_LifetimeScope = lifetimeScope;
m_ObfuscationAttributeResolver = m_LifetimeScope.Resolve<ObfuscationAttributeResolver>();
m_ObfuscateAssemblyAttributeResolver = m_LifetimeScope.Resolve<ObfuscateAssemblyAttributeResolver>();
m_Obfuscation = m_LifetimeScope.Resolve<IOptions<Shared.Models.Obfuscation>>().Value;
m_ProtectionSettings = m_LifetimeScope.Resolve<IOptions<ProtectionSettings>>().Value.Protections;
m_MemberResolvers = m_LifetimeScope
_lifetimeScope = lifetimeScope;
_obfuscationAttributeResolver = _lifetimeScope.Resolve<ObfuscationAttributeResolver>();
_obfuscateAssemblyAttributeResolver = _lifetimeScope.Resolve<ObfuscateAssemblyAttributeResolver>();
_obfuscation = _lifetimeScope.Resolve<IOptions<Shared.Models.Obfuscation>>().Value;
_protectionSettings = _lifetimeScope.Resolve<IOptions<ProtectionSettings>>().Value.Protections;
_memberResolvers = _lifetimeScope
.Resolve<ICollection<IMemberResolver>>()
.ToList();;
m_Logger = m_LifetimeScope
_logger = _lifetimeScope
.Resolve<ILogger>()
.ForContext<BitMonoEngine>();
}
Expand All @@ -34,37 +34,36 @@ internal async Task<bool> StartAsync(ProtectionContext context, IDataWriter data
{
context.ThrowIfCancellationRequested();

m_Logger.Information("Loaded Module {0}", context.Module.Name.Value);
_logger.Information("Loaded Module {0}", context.Module.Name.Value);

var protections = m_LifetimeScope
var protections = _lifetimeScope
.Resolve<ICollection<IProtection>>(new TypedParameter(typeof(ProtectionContext), context))
.ToList();
var protectionsSorter = new ProtectionsSorter(m_ObfuscationAttributeResolver, context.Module.Assembly);
var protectionsSort = protectionsSorter.Sort(protections, m_ProtectionSettings);
var protectionsSorter = new ProtectionsSorter(_obfuscationAttributeResolver, context.Module.Assembly);
var protectionsSort = protectionsSorter.Sort(protections, _protectionSettings);
if (protectionsSort.HasProtections == false)
{
m_Logger.Fatal("No one protection were detected, please specify or enable them in protections.json!");
_logger.Fatal("No one protection were detected, please specify or enable them in protections.json!");
return false;
}

var obfuscator = new BitMonoObfuscator(context, m_MemberResolvers, protectionsSort, dataWriter, m_ObfuscationAttributeResolver, m_ObfuscateAssemblyAttributeResolver, m_Obfuscation, m_Logger);
var obfuscator = new BitMonoObfuscator(context, _memberResolvers, protectionsSort, dataWriter, _obfuscationAttributeResolver, _obfuscateAssemblyAttributeResolver, _obfuscation, _logger);
await obfuscator.ProtectAsync();
return true;
}
public async Task<bool> StartAsync(ObfuscationNeeds needs, IModuleFactory moduleFactory, IDataWriter dataWriter, IDependenciesDataResolver dependenciesDataResolver, CancellationToken cancellationToken)
public async Task<bool> StartAsync(ObfuscationNeeds needs, IModuleFactory moduleFactory, IDataWriter dataWriter, IReferencesDataResolver referencesDataResolver, CancellationToken cancellationToken)
{
var bitMonoContextFactory = new BitMonoContextFactory(dependenciesDataResolver, m_Obfuscation);
var bitMonoContext = bitMonoContextFactory.Create(needs.OutputDirectoryName, needs.FileName);

var runtimeModule = ModuleDefinition.FromFile(typeof(BitMono.Runtime.Data).Assembly.Location);
var moduleFactoryResult = moduleFactory.Create();
var bitMonoContextFactory = new BitMonoContextFactory(moduleFactoryResult.Module, referencesDataResolver, _obfuscation);
var bitMonoContext = bitMonoContextFactory.Create(needs.OutputDirectoryName, needs.FileName);
var protectionContextFactory = new ProtectionContextFactory(moduleFactoryResult, runtimeModule, bitMonoContext, cancellationToken);
var protectionContext = protectionContextFactory.Create();
bitMonoContext.OutputFile = OutputFilePathFactory.Create(bitMonoContext);
return await StartAsync(protectionContext, dataWriter);
}
public async Task<bool> StartAsync(ObfuscationNeeds needs, CancellationToken cancellationToken)
{
return await StartAsync(needs, new ModuleFactory(File.ReadAllBytes(needs.FileName), new LogErrorListener(m_Logger)), new FileDataWriter(), new DependenciesDataResolver(needs.DependenciesDirectoryName), cancellationToken);
return await StartAsync(needs, new ModuleFactory(File.ReadAllBytes(needs.FileName), new LogErrorListener(_logger)), new FileDataWriter(), new AutomaticReferencesDataResolver(needs.ReferencesDirectoryName), cancellationToken);
}
}
3 changes: 2 additions & 1 deletion src/BitMono.Obfuscation/BitMonoObfuscator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ private Task<bool> OutputFrameworkInformationAsync()
}
private Task<bool> ResolveDependenciesAsync()
{
var assemblyResolve = new AssemblyResolver().Resolve(_context.BitMonoContext.ReferencesData, _context);
_logger.Information("Starting resolving dependencies...");
var assemblyResolve = AssemblyResolver.Resolve(_context.BitMonoContext.ReferencesData, _context);
foreach (var reference in assemblyResolve.ResolvedReferences)
{
_logger.Information("Successfully resolved dependency: {0}", reference.FullName);
Expand Down
17 changes: 10 additions & 7 deletions src/BitMono.Obfuscation/Factories/BitMonoContextFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@

public class BitMonoContextFactory
{
private readonly IDependenciesDataResolver m_DependenciesDataResolver;
private readonly Shared.Models.Obfuscation m_Obfuscation;
private readonly ModuleDefinition _module;
private readonly IReferencesDataResolver _referencesDataResolver;
private readonly Shared.Models.Obfuscation _obfuscation;

public BitMonoContextFactory(IDependenciesDataResolver dependenciesDataResolver, Shared.Models.Obfuscation obfuscation)
public BitMonoContextFactory(ModuleDefinition module, IReferencesDataResolver referencesDataResolver, Shared.Models.Obfuscation obfuscation)
{
m_DependenciesDataResolver = dependenciesDataResolver;
m_Obfuscation = obfuscation;
_module = module;
_referencesDataResolver = referencesDataResolver;
_obfuscation = obfuscation;
}

public BitMonoContext Create(string outputDirectoryName, string fileName)
{
var referencesData = _referencesDataResolver.Resolve(_module);
return new BitMonoContext
{
OutputDirectoryName = outputDirectoryName,
ReferencesData = m_DependenciesDataResolver.Resolve(),
Watermark = m_Obfuscation.Watermark,
ReferencesData = referencesData,
Watermark = _obfuscation.Watermark,
FileName = fileName
};
}
Expand Down
Loading

0 comments on commit 365187b

Please sign in to comment.