Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3178e2e
fix(ci): add pre-release branch to CI triggers (#36)
mcpolo99 Jun 7, 2026
8cae43e
fix(renamer): fix WPF relative resource renaming (#30)
mcpolo99 Jun 7, 2026
3ad3663
fix(core): support .NET Standard library obfuscation (#31)
mcpolo99 Jun 7, 2026
b596de6
fix(core): don't create PDB files when debug=false (upstream #532) (#32)
mcpolo99 Jun 7, 2026
ca1551b
fix(renamer): fix generic name parsing null case (upstream #516) (#33)
mcpolo99 Jun 7, 2026
93f9c34
修复控制流保护在#153的错误 (#34)
mcpolo99 Jun 7, 2026
fa0b829
feat(core): add wildcard module loading in .crproj (upstream #481) (#35)
mcpolo99 Jun 7, 2026
49b365f
fix(renamer): skip renaming of DataContract/DataMember/JsonProperty a…
mcpolo99 Jun 7, 2026
a1527f2
fix(renamer): preserve anonymous type constructor arg names for JSON …
mcpolo99 Jun 7, 2026
9c4249c
feat(cli): add --snkey and --snkeypass options for CI/CD signing (#40)
mcpolo99 Jun 7, 2026
197d1d6
fix(runtime): catch ReflectionTypeLoadException in packer startup (#41)
mcpolo99 Jun 7, 2026
e235150
fix(protections): use non-throwing resolution in ref proxy mild mode …
mcpolo99 Jun 7, 2026
da95f28
fix(renamer): handle FnPtr type signatures instead of throwing NotSup…
mcpolo99 Jun 7, 2026
bb6165e
fix(renamer): skip renaming WPF theme resources in themes/Generic.xam…
mcpolo99 Jun 7, 2026
04ba4f5
fix(protections): skip unresolvable external types in ref proxy phase…
mcpolo99 Jun 7, 2026
6b7a058
feat(analysis): enable .NET analyzers and configure severity rules (#50)
mcpolo99 Jun 7, 2026
132aaa3
feat(analysis): add Roslynator.Analyzers for broader code quality cov…
mcpolo99 Jun 7, 2026
da2b709
fix(analysis): resolve analyzer warnings and suppress noisy rules (#52)
mcpolo99 Jun 7, 2026
bc48521
fix(core): improve error message for .NET 6+ native host .exe files (…
mcpolo99 Jun 8, 2026
6c49075
docs: update README for ConfuserExx fork — fix links, add features, m…
mcpolo99 Jun 8, 2026
ff01e84
feat(core): tolerate missing dependencies — warn instead of crash (#4…
mcpolo99 Jun 8, 2026
6167387
feat(renamer): add opt-in overload confusion — rename methods with di…
Jun 8, 2026
39fac2b
feat(ci): add pre-release builds with downloadable binaries (#pre-rel…
mcpolo99 Jun 10, 2026
ad4207f
feat(core): auto-detect .NET runtime paths for assembly resolution (#55)
mcpolo99 Jun 10, 2026
39af086
Merge branch 'master' into pre-release
mcpolo99 Jun 10, 2026
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
38 changes: 38 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,44 @@ dotnet_style_qualification_for_property = false:suggestion
dotnet_style_require_accessibility_modifiers = never:info

[*.cs]
# .NET Analyzer severity overrides
dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists (too restrictive for internal code)
dotnet_diagnostic.CA1031.severity = none # Do not catch general exceptions (too many existing catch blocks)
dotnet_diagnostic.CA1032.severity = none # Implement standard exception constructors
dotnet_diagnostic.CA1034.severity = none # Nested types should not be visible
dotnet_diagnostic.CA1062.severity = none # Validate arguments of public methods (too noisy initially)
dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters
dotnet_diagnostic.CA1304.severity = none # Specify CultureInfo
dotnet_diagnostic.CA1305.severity = none # Specify IFormatProvider
dotnet_diagnostic.CA1307.severity = none # Specify StringComparison for clarity
dotnet_diagnostic.CA1310.severity = none # Specify StringComparison for correctness
dotnet_diagnostic.CA1707.severity = none # Identifiers should not contain underscores (test naming)
dotnet_diagnostic.CA1710.severity = none # Identifiers should have correct suffix
dotnet_diagnostic.CA1711.severity = none # Identifiers should not have incorrect suffix
dotnet_diagnostic.CA1720.severity = none # Identifiers should not contain type names
dotnet_diagnostic.CA1822.severity = suggestion # Mark members as static (informational only)
dotnet_diagnostic.CA1825.severity = warning # Avoid zero-length array allocations
dotnet_diagnostic.CA1859.severity = suggestion # Use concrete types for improved performance
dotnet_diagnostic.CA2000.severity = suggestion # Dispose objects before losing scope
dotnet_diagnostic.CA2007.severity = none # ConfigureAwait (not needed for desktop app)
dotnet_diagnostic.CA2227.severity = none # Collection properties should be read only

# Roslynator severity overrides
dotnet_diagnostic.RCS1018.severity = none # Add accessibility modifiers (conflicts with existing style)
dotnet_diagnostic.RCS1102.severity = none # Make class static (many are non-static for runtime injection)
dotnet_diagnostic.RCS1037.severity = none # Remove trailing white-space
dotnet_diagnostic.RCS1057.severity = none # Add empty line between declarations
dotnet_diagnostic.RCS1085.severity = none # Use auto-implemented property
dotnet_diagnostic.RCS1090.severity = none # ConfigureAwait
dotnet_diagnostic.RCS1118.severity = none # Mark local variable as const
dotnet_diagnostic.RCS1124.severity = none # Inline local variable
dotnet_diagnostic.RCS1138.severity = none # Add summary to documentation comment
dotnet_diagnostic.RCS1139.severity = none # Add summary element to documentation comment
dotnet_diagnostic.RCS1163.severity = none # Unused parameter
dotnet_diagnostic.RCS1194.severity = none # Implement exception constructors
dotnet_diagnostic.RCS1213.severity = none # Remove unused member declaration (too aggressive)
dotnet_diagnostic.RCS1228.severity = none # Unused element in documentation comment

csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
Expand Down
54 changes: 52 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: ci

on:
push:
branches: [master, feature/**, fix/**]
branches: [master, pre-release, feature/**, fix/**]
pull_request:
branches: [master]
branches: [master, pre-release]

jobs:
build:
Expand Down Expand Up @@ -82,6 +82,56 @@ jobs:
ConfuserEx.zip
Confuser.MSBuild.Tasks/bin/Release/*.nupkg

# Pre-release: on push to pre-release branch
pre-release:
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/pre-release'
runs-on: windows-2022
timeout-minutes: 5
permissions:
contents: write
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Install nbgv
run: dotnet tool install -g nbgv

- name: Compute version
id: version
shell: pwsh
run: |
$ver = nbgv get-version -v NuGetPackageVersion
echo "VERSION=$ver" >> $env:GITHUB_OUTPUT
Write-Host "Pre-release version: $ver"

- name: Download artifacts
uses: actions/download-artifact@v5
with:
name: confuserex-packages

- name: Create or update pre-release
uses: softprops/action-gh-release@v2
with:
tag_name: pre-release
name: "Pre-release v${{ steps.version.outputs.VERSION }}"
prerelease: true
make_latest: false
body: |
**Pre-release build** — for testing only, not production use.

Version: `${{ steps.version.outputs.VERSION }}`
Branch: `pre-release`
Commit: ${{ github.sha }}

Download the binaries below to test recent fixes and features before they are included in a stable release.
files: |
ConfuserEx-CLI.zip
ConfuserEx-GUI.zip
ConfuserEx.zip
*.nupkg

# Release: only on PR merge to master
release:
needs: build
Expand Down
23 changes: 17 additions & 6 deletions Confuser.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ static int Main(string[] args) {
bool noPause = false;
bool debug = false;
string outDir = null;
string snKeyPath = null;
string snKeyPass = null;
List<string> probePaths = new List<string>();
List<string> plugins = new List<string>();
var p = new OptionSet {
Expand All @@ -37,6 +39,12 @@ static int Main(string[] args) {
}, {
"debug", "specifies debug symbol generation.",
value => { debug = (value != null); }
}, {
"snkey=", "specifies strong name key file path.",
value => { snKeyPath = value; }
}, {
"snkeypass=", "specifies strong name key password.",
value => { snKeyPass = value; }
}
};

Expand All @@ -60,10 +68,8 @@ static int Main(string[] args) {
try {
var xmlDoc = new XmlDocument();
xmlDoc.Load(files[0]);
proj.Load(xmlDoc);
string crprojDir = Path.GetDirectoryName(Path.GetFullPath(files[0]));
proj.BaseDirectory = Path.GetFullPath(Path.Combine(crprojDir, proj.BaseDirectory));
proj.OutputDirectory = Path.GetFullPath(Path.Combine(crprojDir, proj.OutputDirectory));
proj.Load(xmlDoc, Path.GetDirectoryName(Path.GetFullPath(files[0])));
proj.OutputDirectory = Path.GetFullPath(Path.Combine(proj.BaseDirectory, proj.OutputDirectory));
}
catch (Exception ex) {
WriteLineWithColor(ConsoleColor.Red, "Failed to load project:");
Expand Down Expand Up @@ -103,10 +109,13 @@ static int Main(string[] args) {
modulePath = modulePath.Substring(proj.BaseDirectory.Length + 1);
}

if (TryMatchTemplateProject(templateModules, proj.BaseDirectory, modulePath, out var matchedModule))
if (TryMatchTemplateProject(templateModules, proj.BaseDirectory, modulePath, out var matchedModule)) {
if (snKeyPath != null) matchedModule.SNKeyPath = snKeyPath;
if (snKeyPass != null) matchedModule.SNKeyPassword = snKeyPass;
proj.Add(matchedModule);
}
else
proj.Add(new ProjectModule { Path = modulePath });
proj.Add(new ProjectModule { Path = modulePath, SNKeyPath = snKeyPath, SNKeyPassword = snKeyPass });
}

proj.OutputDirectory = outDir;
Expand Down Expand Up @@ -202,6 +211,8 @@ static void PrintUsage() {
WriteLine(" -probe : specifies probe directory.");
WriteLine(" -plugin : specifies plugin path.");
WriteLine(" -debug : specifies debug symbol generation.");
WriteLine(" -snkey : specifies strong name key file path.");
WriteLine(" -snkeypass : specifies strong name key password.");
}

static void WriteLineWithColor(ConsoleColor color, string txt) {
Expand Down
46 changes: 44 additions & 2 deletions Confuser.Core/ConfuserAssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,50 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) {
if (assembly is AssemblyDef assemblyDef)
return assemblyDef;

var resolvedAssemblyDef = InternalExactResolver.Resolve(assembly, sourceModule);
return resolvedAssemblyDef ?? InternalFuzzyResolver.Resolve(assembly, sourceModule);
var resolvedAssemblyDef =
InternalExactResolver.Resolve(assembly, sourceModule) ??
InternalFuzzyResolver.Resolve(assembly, sourceModule);

// Remove AssemblyAttributes.PA_NoPlatform
if (null != resolvedAssemblyDef &&
(AssemblyAttributes.PA_Mask & resolvedAssemblyDef.Attributes) == AssemblyAttributes.PA_NoPlatform) {
resolvedAssemblyDef.Attributes =
resolvedAssemblyDef.Attributes & ~AssemblyAttributes.PA_FullMask;
}

if (resolvedAssemblyDef?.Name == "netstandard" && 0 < resolvedAssemblyDef.ManifestModule.ExportedTypes.Count) {
// Move types from AssemblyRef to here
var module = resolvedAssemblyDef.ManifestModule;
var newTypes = new List<TypeDef>();
var allAssemblyRefs = new List<AssemblyDef>();

module.ExportedTypes.Clear();

foreach (var assemblyRef in module.GetAssemblyRefs()) {
var subAss =
InternalExactResolver.Resolve(assemblyRef, module) ??
InternalFuzzyResolver.Resolve(assemblyRef, module);
allAssemblyRefs.Add(subAss);
foreach (var subModule in subAss?.Modules) {
foreach (var defType in subModule.Types) {
newTypes.Add(defType);
}
subModule.Types.Clear();
foreach (var defType in newTypes) {
module.Types.Add(defType);
}
newTypes.Clear();
}
}

// Remove them because their types has been removed.
foreach (var subAss in allAssemblyRefs) {
InternalExactResolver.Remove(subAss);
InternalFuzzyResolver.Remove(subAss);
}
}

return resolvedAssemblyDef;
}

public void Clear() {
Expand Down
23 changes: 16 additions & 7 deletions Confuser.Core/ConfuserEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token)
foreach (string probePath in context.Project.ProbePaths)
asmResolver.PostSearchPaths.Insert(0, Path.Combine(context.BaseDirectory, probePath));

// Auto-detect .NET Core/5+/6/7/8+ runtime paths from first module
var firstModule = context.Project.FirstOrDefault(m => !m.IsExternal);
if (firstModule != null) {
var modulePath = Path.Combine(context.BaseDirectory, firstModule.Path);
foreach (var runtimePath in DotNetCorePathResolver.ResolveRuntimePaths(modulePath, context.Logger)) {
asmResolver.PostSearchPaths.Add(runtimePath);
context.Logger.DebugFormat("Auto-detected .NET runtime path: {0}", runtimePath);
}
}

context.CheckCancellation();

Marker marker = parameters.GetMarker();
Expand Down Expand Up @@ -265,13 +275,10 @@ static void Inspection(ConfuserContext context) {
context.Logger.Info("Resolving dependencies...");
foreach (var dependency in context.Modules
.SelectMany(module => module.GetAssemblyRefs().Select(asmRef => Tuple.Create(asmRef, module)))) {
try {
context.Resolver.ResolveThrow(dependency.Item1, dependency.Item2);
}
catch (AssemblyResolveException ex) {
context.Logger.ErrorException("Failed to resolve dependency of '" + dependency.Item2.Name + "'.", ex);
throw new ConfuserException(ex);
}
var resolved = context.Resolver.Resolve(dependency.Item1, dependency.Item2);
if (resolved == null)
context.Logger.WarnFormat("Failed to resolve dependency '{0}' of '{1}'. Some protections may not work correctly.",
dependency.Item1.FullName, dependency.Item2.Name);
}

context.Logger.Debug("Checking Strong Name...");
Expand Down Expand Up @@ -423,6 +430,8 @@ static void WriteModule(ConfuserContext context) {

static void Debug(ConfuserContext context) {
context.Logger.Info("Finalizing...");
if (!context.Project.Debug)
return;
for (int i = 0; i < context.OutputModules.Count; i++) {
if (context.OutputSymbols[i] == null)
continue;
Expand Down
44 changes: 16 additions & 28 deletions Confuser.Core/DnlibUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,12 @@ public static bool IsDelegate(this TypeDef type) {
/// <param name="baseType">The full name of base type.</param>
/// <returns><c>true</c> if the specified type is inherited from a base type; otherwise, <c>false</c>.</returns>
public static bool InheritsFromCorlib(this TypeDef type, string baseType) {
if (type.BaseType == null)
return false;

TypeDef bas = type;
do {
bas = bas.BaseType.ResolveTypeDefThrow();
var bas = type.BaseType?.ResolveTypeDef();
while (bas != null && bas.DefinitionAssembly.IsCorLib()) {
if (bas.ReflectionFullName == baseType)
return true;
} while (bas.BaseType != null && bas.BaseType.DefinitionAssembly.IsCorLib());
bas = bas.BaseType?.ResolveTypeDef();
}
return false;
}

Expand All @@ -228,15 +225,12 @@ public static bool InheritsFromCorlib(this TypeDef type, string baseType) {
/// <param name="baseType">The full name of base type.</param>
/// <returns><c>true</c> if the specified type is inherited from a base type; otherwise, <c>false</c>.</returns>
public static bool InheritsFrom(this TypeDef type, string baseType) {
if (type.BaseType == null)
return false;

TypeDef bas = type;
do {
bas = bas.BaseType.ResolveTypeDefThrow();
var bas = type.BaseType?.ResolveTypeDef();
while (bas != null) {
if (bas.ReflectionFullName == baseType)
return true;
} while (bas.BaseType != null);
bas = bas.BaseType?.ResolveTypeDef();
}
return false;
}

Expand All @@ -247,18 +241,12 @@ public static bool InheritsFrom(this TypeDef type, string baseType) {
/// <param name="fullName">The full name of the type of interface.</param>
/// <returns><c>true</c> if the specified type implements the interface; otherwise, <c>false</c>.</returns>
public static bool Implements(this TypeDef type, string fullName) {
do {
foreach (InterfaceImpl iface in type.Interfaces) {
if (iface.Interface.ReflectionFullName == fullName)
return true;
}

if (type.BaseType == null)
return false;

type = type.BaseType.ResolveTypeDefThrow();
} while (type != null);
throw new UnreachableException();
while (type != null) {
if (type.Interfaces.Any(iface => iface.Interface.ReflectionFullName == fullName))
return true;
type = type.BaseType?.ResolveTypeDef();
}
return false;
}

/// <summary>
Expand Down Expand Up @@ -451,8 +439,8 @@ public static bool IsImplicitImplementedInterfaceMember(this MethodDef method) {

if (method.IsPublic && method.IsNewSlot) {
foreach (var iFace in method.DeclaringType.Interfaces) {
var iFaceDef = iFace.Interface.ResolveTypeDefThrow();
if (iFaceDef.FindMethod(method.Name, (MethodSig)method.Signature) != null)
var iFaceDef = iFace.Interface.ResolveTypeDef();
if (iFaceDef != null && iFaceDef.FindMethod(method.Name, (MethodSig)method.Signature) != null)
return true;
}
}
Expand Down
Loading
Loading