Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions Confuser.Core/ConfuserEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,9 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token)

bool ok = false;
try {
// Enable watermarking by default
context.Project.Rules.Insert(0, new Rule {
new SettingItem<Protection>(WatermarkingProtection._Id)
});
// Watermarking is opt-in (issue #69): the "watermark" protection is no longer
// force-enabled, so obfuscated output carries no ConfuserEx fingerprint attribute
// unless a project explicitly requests it via <protection id="watermark" />.

var asmResolver = new ConfuserAssemblyResolver { EnableTypeDefCache = true };
asmResolver.DefaultModuleContext = new ModuleContext(asmResolver);
Expand Down
13 changes: 10 additions & 3 deletions Confuser.Core/WatermarkingProtection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,17 @@ protected internal override void Execute(ConfuserContext context, ProtectionPara

context.Logger.LogDebug("Watermarking...");
foreach (var module in parameters.Targets.OfType<ModuleDef>()) {
// Both are configurable so users can brand (or disguise) the watermark instead of
// carrying the identifiable default ConfuserEx fingerprint.
var attributeName = parameters.GetParameter(context, module, "attributeName", "ConfusedByAttribute");
if (string.IsNullOrEmpty(attributeName))
attributeName = "ConfusedByAttribute";
var text = parameters.GetParameter(context, module, "text", ConfuserEngine.Version);

var attrRef = module.CorLibTypes.GetTypeRef("System", "Attribute");
var attrType = module.FindNormal("ConfusedByAttribute");
var attrType = module.FindNormal(attributeName);
if (attrType == null) {
attrType = new TypeDefUser("", "ConfusedByAttribute", attrRef);
attrType = new TypeDefUser("", attributeName, attrRef);
module.Types.Add(attrType);
marker.Mark(attrType, Parent);
}
Expand All @@ -75,7 +82,7 @@ protected internal override void Execute(ConfuserContext context, ProtectionPara
}

var attr = new CustomAttribute(ctor);
attr.ConstructorArguments.Add(new CAArgument(module.CorLibTypes.String, ConfuserEngine.Version));
attr.ConstructorArguments.Add(new CAArgument(module.CorLibTypes.String, text));

module.CustomAttributes.Add(attr);
}
Expand Down
15 changes: 15 additions & 0 deletions Confuser2.sln
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntiDebug.Test", "Tests\Ant
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SymbolMapReuse.Test", "Tests\SymbolMapReuse.Test\SymbolMapReuse.Test.csproj", "{591069EF-617C-4284-A968-5DB55BE1E1EF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Watermark.Test", "Tests\Watermark.Test\Watermark.Test.csproj", "{0C937593-1C74-4576-B79F-D99642612484}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1399,6 +1401,18 @@ Global
{591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|x64.Build.0 = Release|Any CPU
{591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|x86.ActiveCfg = Release|Any CPU
{591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|x86.Build.0 = Release|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Debug|x64.ActiveCfg = Debug|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Debug|x64.Build.0 = Debug|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Debug|x86.ActiveCfg = Debug|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Debug|x86.Build.0 = Debug|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Release|Any CPU.Build.0 = Release|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Release|x64.ActiveCfg = Release|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Release|x64.Build.0 = Release|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Release|x86.ActiveCfg = Release|Any CPU
{0C937593-1C74-4576-B79F-D99642612484}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1493,6 +1507,7 @@ Global
{1B22CAAE-FC4A-478D-BD68-D29A3081F938} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
{47197200-B8CB-400A-B1BD-84975FEC8C28} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
{591069EF-617C-4284-A968-5DB55BE1E1EF} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
{0C937593-1C74-4576-B79F-D99642612484} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0D937D9E-E04B-4A68-B639-D4260473A388}
Expand Down
13 changes: 13 additions & 0 deletions Tests/Watermark.Test/Watermark.Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net462</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Confuser.UnitTest\Confuser.UnitTest.csproj" />
<ProjectReference Include="..\AntiTamper\AntiTamper.csproj" />
</ItemGroup>

</Project>
58 changes: 58 additions & 0 deletions Tests/Watermark.Test/WatermarkTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Confuser.Core;
using Confuser.Core.Project;
using Confuser.UnitTest;
using dnlib.DotNet;
using Xunit;
using Xunit.Abstractions;

namespace Watermark.Test {
public sealed class WatermarkTest : TestBase {
public WatermarkTest(ITestOutputHelper outputHelper) : base(outputHelper) { }

// Issue #69: the watermark is opt-in. An obfuscation that does not request it must produce
// output with no ConfusedByAttribute fingerprint.
[Fact]
[Trait("Category", "Protection")]
[Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/69")]
public Task Watermark_NotRequested_ProducesNoFingerprintAttribute() =>
Run("AntiTamper.exe",
new[] { "This is a test." },
new SettingItem<Protection>("rename"),
"_wm_off",
postProcessAction: outputPath => {
using (var module = ModuleDefMD.Load(Path.Combine(outputPath, "AntiTamper.exe"))) {
Assert.DoesNotContain(module.CustomAttributes, a => a.TypeFullName == "ConfusedByAttribute");
Assert.DoesNotContain(module.GetTypes(), t => t.Name == "ConfusedByAttribute");
}
return Task.CompletedTask;
});

// When explicitly enabled with custom text and attribute name, the watermark must use them
// (so it can be branded or disguised instead of the default ConfuserEx fingerprint).
[Fact]
[Trait("Category", "Protection")]
[Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/69")]
public Task Watermark_CustomTextAndName_AppliesConfiguredAttribute() =>
Run("AntiTamper.exe",
new[] { "This is a test." },
new SettingItem<Protection>("watermark") {
{ "text", "MyCorp Security" },
{ "attributeName", "SecurityStampAttribute" }
},
"_wm_custom",
postProcessAction: outputPath => {
using (var module = ModuleDefMD.Load(Path.Combine(outputPath, "AntiTamper.exe"))) {
var attr = module.CustomAttributes.FirstOrDefault(a => a.TypeFullName == "SecurityStampAttribute");
Assert.NotNull(attr);
Assert.Equal("MyCorp Security", attr.ConstructorArguments[0].Value?.ToString());

// The default fingerprint name must not appear.
Assert.DoesNotContain(module.CustomAttributes, a => a.TypeFullName == "ConfusedByAttribute");
}
return Task.CompletedTask;
});
}
}
25 changes: 25 additions & 0 deletions docs/protections.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,31 @@ Compresses the entire output assembly and wraps it in a native stub that decompr
</packer>
```

---

### Watermark

**ID:** `watermark`

Adds a small custom attribute to the assembly marking that it was protected. It is **opt-in** —
obfuscated output carries no watermark unless this protection is explicitly enabled, so the
default output has no identifiable ConfuserEx fingerprint.

**Options:**

| Name | Values | Default | Description |
|------|--------|---------|-------------|
| `text` | any string | ConfuserEx version | The value stored in the watermark attribute. |
| `attributeName` | any identifier | `ConfusedByAttribute` | The name of the injected attribute type. |

```xml
<!-- Branded watermark -->
<protection id="watermark">
<argument name="text" value="MyCompany Security" />
<argument name="attributeName" value="SecurityStampAttribute" />
</protection>
```

## Combining Protections

Protections stack. You can start from a preset and add/remove individual protections:
Expand Down