Plugins
BitMono can load plugins, external assemblies that add your own protections, without rebuilding BitMono. Drop a plugin DLL into the plugins directory and BitMono finds the protections inside it and runs them exactly like the built-in ones. (#227)
Warning
Plugins run inside the BitMono process with full trust, .NET can’t sandbox them. Only drop in plugins from sources you trust, same as you would with any other executable.
Where plugins live
By default BitMono scans a plugins directory next to the BitMono executable. You can rename it (or
point it at an absolute path) with PluginsDirectoryName in obfuscation.json:
{
"PluginsDirectoryName": "plugins"
}
or override it for a single run from the CLI with --plugins:
BitMono.CLI -f MyApp.dll --plugins "C:\path\to\my-plugins" -p HelloPlugin
Two layouts work:
Flat, drop the DLL straight in:
plugins/MyProtections.dll.Per-plugin folder, give each plugin its own folder:
plugins/MyProtections/MyProtections.dll. Put any extra dependencies in a nested folder (sayplugins/MyProtections/libs/), they’re resolved on demand and aren’t treated as plugins themselves.
If the directory doesn’t exist, BitMono just skips plugin loading.
Writing a plugin
A plugin is an ordinary class library that references BitMono.API (and usually BitMono.Core for
the Protection base class) and exposes one or more public protections. Writing the protection itself
is the same as writing a built-in one:
using BitMono.Core;
using BitMono.Core.Attributes;
using BitMono.Shared.DependencyInjection;
using BitMono.Shared.Logging;
[ProtectionName("HelloPlugin")]
public class HelloPlugin : Protection
{
private readonly ILogger _logger;
public HelloPlugin(IBitMonoServiceProvider serviceProvider) : base(serviceProvider)
{
_logger = serviceProvider.GetRequiredService<ILogger>().ForContext<HelloPlugin>();
}
public override Task ExecuteAsync()
{
_logger.Information("Hello from a BitMono plugin!");
return Task.CompletedTask;
}
}
Note
The container builds your protection through its first constructor, resolving each parameter.
Take IBitMonoServiceProvider (like above) to reach BitMono’s services. If a parameter can’t be
resolved the protection is skipped and the error is logged, it never aborts the run.
Reference BitMono, don’t ship it
Reference BitMono from NuGet and mark it ExcludeAssets="runtime" so its assemblies are not copied
into your build output (BitMono itself provides them at runtime). Your plugin has to bind to the same
BitMono.API the host is running. Ship your own copy and your protection implements a different
IProtection type, so BitMono ignores it (and warns you in the log).
BitMono.Core pulls in BitMono.API, BitMono.Shared and AsmResolver for you, and
ExcludeAssets="runtime" keeps the whole graph out of your output, so a successful build leaves just
your own plugin DLL behind.
<ItemGroup>
<!-- Use the version that matches the BitMono you run the plugin against. -->
<PackageReference Include="BitMono.Core" Version="0.41.1">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
Versioning and compatibility
There’s no special “plugin version” attribute - your plugin’s own assembly version is its version, and
BitMono prints it when it loads it (Loaded plugin: MyPlugin v1.2.0.0).
What actually matters is the BitMono.API version you build against (BitMono.Core pulls it in). It’s
the contract, and it follows semver: a new minor only adds things, a new major can
break IProtection/Protection. If a plugin is built against a newer BitMono.API than the
BitMono you drop it into, it’d probably call something that isn’t there - so BitMono skips it and logs a
warning telling you to rebuild against the version you’re running. Same or older builds load fine.
So pin BitMono.Core to the version you ship against and bump it when you move to a newer BitMono.
External dependencies
If your plugin uses external libraries (other NuGet packages, your own helpers), reference them
normally (without ExcludeAssets - that switch is only for BitMono itself) so they’re copied next
to your plugin, then ship those DLLs next to the plugin or in a nested folder like libs. BitMono
installs an AppDomain.AssemblyResolve handler that probes the plugin directories and loads
dependencies on demand, so they don’t have to sit in BitMono’s own folder.
A typical per-plugin layout:
plugins/
MyProtections/
MyProtections.dll <- your plugin
libs/
SomeNuGetPackage.dll <- resolved automatically when needed
Enabling a plugin protection
Plugin protections are configured exactly like the built-in ones, by name (the class name, or the
[ProtectionName("...")] value). Add them to protections.json:
{
"Protections": [
{
"Name": "HelloPlugin",
"Enabled": true
}
]
}
or pass them on the command line:
BitMono.CLI -f MyApp.dll -p HelloPlugin
Execution order follows the configuration order, same as built-in protections. See Obfuscation Engine Execution Order for details.