Skip to content

Commit 1a81d79

Browse files
mcpolo99RandomCrocodileryanroeCopilotkamcip
authored
Pre release (#68)
* fix(ci): add pre-release branch to CI triggers (#36) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(renamer): fix WPF relative resource renaming (#30) * fix WPF relative resource renaming * Update Confuser.Renamer/Analyzers/WPFAnalyzer.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Ryan <smbserv@qq.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(core): support .NET Standard library obfuscation (#31) * Add types in referencing assembly to netstandard DLL. Types defined in AssemblyRefs of netstandard (e.g. mscorlib) will be moved to netstandard. Therefore, subsequence ModuleDef.Find will return AssemblyRef to netstandard. As a result, the confused module will only reference to netstandard. * Remove AssemblyAttributes.PA_NoPlatform from assembly. Net standard project may refer to NuGet package (e.g. System.ComponentModel.Composition) in order to use the Framework libraries. However, DLL in NuGet may have this attribute set but the actual Framework DLL does not. As a result, dnlib treats them as different assemblies and confused DLL will reference to two identical assemblies. * Fix assembly reference to assemblies that hidden by netstandard assembly. TargetModule.GetAssemblyRef returns null if type is defined in assembly hidden by netstandard. This change searches assemblies hidden by netstandard and returns the fixed type reference. * Returns the method from CorLib of module to be confused instead from runtime. MSBuild may runs on .net framework and runtime help will reference to mscorlib instead of netstandard. This change try resolve it from CorLib before from runtime type. --------- Co-authored-by: KC Ip <kamchuen.ip@hidglobal.com> * fix(core): don't create PDB files when debug=false (upstream mkaring#532) (#32) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(renamer): fix generic name parsing null case (upstream mkaring#516) (#33) When preserveGenericParams is true but name is null or empty, ParseGenericName would crash. Add a null/empty guard to prevent this. Cherry-pick of mkaring#516. Co-authored-by: RandomCrocodile <mawi@polosab.com> * 修复控制流保护在#153的错误 (#34) 注意到 mkaring 在 1.4.0版本中对控制流保护做出了误操作(272行) 将src => statementLast.Contains(src),错误修改成了src => !statementLast.Contains(src),导致了保护后的程序出现了错误的循环,这里特此做出修复! Co-authored-by: wujiayang2007 <52036257+wujiayang2007@users.noreply.github.com> * feat(core): add wildcard module loading in .crproj (upstream mkaring#481) (#35) Cherry-pick of mkaring#481 — allows using wildcards like *.dll in module paths to batch-load modules from the base directory. The Load method now accepts an optional baseDirRoot parameter to resolve relative base directory paths. New internal helpers AddModule, IsWildcard, and BatchLoadModules handle wildcard expansion using Directory.GetFiles with TopDirectoryOnly search. Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(renamer): skip renaming of DataContract/DataMember/JsonProperty attributed types and members (#38) Detect serialization-related attributes and exclude decorated types and members from renaming to prevent WCF/DataContract serialization breakage at runtime. Attributes now checked: - DataContractAttribute on types - DataMemberAttribute on fields and properties - EnumMemberAttribute on enum fields Fixes mcpolo99/private-ConfuserEx#9 Upstream: mkaring#147 Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(renamer): preserve anonymous type constructor arg names for JSON serialization (#39) Co-authored-by: RandomCrocodile <mawi@polosab.com> * feat(cli): add --snkey and --snkeypass options for CI/CD signing (#40) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(runtime): catch ReflectionTypeLoadException in packer startup (#41) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(protections): use non-throwing resolution in ref proxy mild mode for external types (#42) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(renamer): handle FnPtr type signatures instead of throwing NotSupportedException (#43) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(renamer): skip renaming WPF theme resources in themes/Generic.xaml (#44) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(protections): skip unresolvable external types in ref proxy phase (#45) Co-authored-by: RandomCrocodile <mawi@polosab.com> * feat(analysis): enable .NET analyzers and configure severity rules (#50) Co-authored-by: RandomCrocodile <mawi@polosab.com> * feat(analysis): add Roslynator.Analyzers for broader code quality coverage (#51) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(analysis): resolve analyzer warnings and suppress noisy rules (#52) Co-authored-by: RandomCrocodile <mawi@polosab.com> * fix(core): improve error message for .NET 6+ native host .exe files (#19) (#56) Co-authored-by: RandomCrocodile <mawi@polosab.com> * docs: update README for ConfuserExx fork — fix links, add features, modernize (#57) Co-authored-by: RandomCrocodile <mawi@polosab.com> * feat(core): tolerate missing dependencies — warn instead of crash (#4) (#58) Co-authored-by: RandomCrocodile <mawi@polosab.com> * feat(renamer): add opt-in overload confusion — rename methods with different signatures to same name (#25) * feat(ci): add pre-release builds with downloadable binaries (#pre-release) (#61) Co-authored-by: RandomCrocodile <mawi@polosab.com> * feat(core): auto-detect .NET runtime paths for assembly resolution (#55) * feat(core): auto-detect .NET Core/5+/6/7/8+ runtime paths for assembly resolution (#12) * fix(core): probe all installed .NET runtime versions for cross-version dependencies (#12) * refactor(core): add logging to DotNetCorePathResolver, address review feedback (#12) --------- Co-authored-by: RandomCrocodile <mawi@polosab.com> --------- Co-authored-by: RandomCrocodile <mawi@polosab.com> Co-authored-by: Ryan <smbserv@qq.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: KC Ip <kamchuen.ip@hidglobal.com> Co-authored-by: wujiayang2007 <52036257+wujiayang2007@users.noreply.github.com>
1 parent 97cffa0 commit 1a81d79

29 files changed

Lines changed: 557 additions & 101 deletions

.editorconfig

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,44 @@ dotnet_style_qualification_for_property = false:suggestion
3939
dotnet_style_require_accessibility_modifiers = never:info
4040

4141
[*.cs]
42+
# .NET Analyzer severity overrides
43+
dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists (too restrictive for internal code)
44+
dotnet_diagnostic.CA1031.severity = none # Do not catch general exceptions (too many existing catch blocks)
45+
dotnet_diagnostic.CA1032.severity = none # Implement standard exception constructors
46+
dotnet_diagnostic.CA1034.severity = none # Nested types should not be visible
47+
dotnet_diagnostic.CA1062.severity = none # Validate arguments of public methods (too noisy initially)
48+
dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters
49+
dotnet_diagnostic.CA1304.severity = none # Specify CultureInfo
50+
dotnet_diagnostic.CA1305.severity = none # Specify IFormatProvider
51+
dotnet_diagnostic.CA1307.severity = none # Specify StringComparison for clarity
52+
dotnet_diagnostic.CA1310.severity = none # Specify StringComparison for correctness
53+
dotnet_diagnostic.CA1707.severity = none # Identifiers should not contain underscores (test naming)
54+
dotnet_diagnostic.CA1710.severity = none # Identifiers should have correct suffix
55+
dotnet_diagnostic.CA1711.severity = none # Identifiers should not have incorrect suffix
56+
dotnet_diagnostic.CA1720.severity = none # Identifiers should not contain type names
57+
dotnet_diagnostic.CA1822.severity = suggestion # Mark members as static (informational only)
58+
dotnet_diagnostic.CA1825.severity = warning # Avoid zero-length array allocations
59+
dotnet_diagnostic.CA1859.severity = suggestion # Use concrete types for improved performance
60+
dotnet_diagnostic.CA2000.severity = suggestion # Dispose objects before losing scope
61+
dotnet_diagnostic.CA2007.severity = none # ConfigureAwait (not needed for desktop app)
62+
dotnet_diagnostic.CA2227.severity = none # Collection properties should be read only
63+
64+
# Roslynator severity overrides
65+
dotnet_diagnostic.RCS1018.severity = none # Add accessibility modifiers (conflicts with existing style)
66+
dotnet_diagnostic.RCS1102.severity = none # Make class static (many are non-static for runtime injection)
67+
dotnet_diagnostic.RCS1037.severity = none # Remove trailing white-space
68+
dotnet_diagnostic.RCS1057.severity = none # Add empty line between declarations
69+
dotnet_diagnostic.RCS1085.severity = none # Use auto-implemented property
70+
dotnet_diagnostic.RCS1090.severity = none # ConfigureAwait
71+
dotnet_diagnostic.RCS1118.severity = none # Mark local variable as const
72+
dotnet_diagnostic.RCS1124.severity = none # Inline local variable
73+
dotnet_diagnostic.RCS1138.severity = none # Add summary to documentation comment
74+
dotnet_diagnostic.RCS1139.severity = none # Add summary element to documentation comment
75+
dotnet_diagnostic.RCS1163.severity = none # Unused parameter
76+
dotnet_diagnostic.RCS1194.severity = none # Implement exception constructors
77+
dotnet_diagnostic.RCS1213.severity = none # Remove unused member declaration (too aggressive)
78+
dotnet_diagnostic.RCS1228.severity = none # Unused element in documentation comment
79+
4280
csharp_indent_block_contents = true
4381
csharp_indent_braces = false
4482
csharp_indent_case_contents = true

.github/workflows/ci.yml

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: ci
22

33
on:
44
push:
5-
branches: [master, feature/**, fix/**]
5+
branches: [master, pre-release, feature/**, fix/**]
66
pull_request:
7-
branches: [master]
7+
branches: [master, pre-release]
88

99
jobs:
1010
build:
@@ -82,6 +82,56 @@ jobs:
8282
ConfuserEx.zip
8383
Confuser.MSBuild.Tasks/bin/Release/*.nupkg
8484
85+
# Pre-release: on push to pre-release branch
86+
pre-release:
87+
needs: build
88+
if: github.event_name == 'push' && github.ref == 'refs/heads/pre-release'
89+
runs-on: windows-2022
90+
timeout-minutes: 5
91+
permissions:
92+
contents: write
93+
steps:
94+
- uses: actions/checkout@v5
95+
with:
96+
fetch-depth: 0
97+
98+
- name: Install nbgv
99+
run: dotnet tool install -g nbgv
100+
101+
- name: Compute version
102+
id: version
103+
shell: pwsh
104+
run: |
105+
$ver = nbgv get-version -v NuGetPackageVersion
106+
echo "VERSION=$ver" >> $env:GITHUB_OUTPUT
107+
Write-Host "Pre-release version: $ver"
108+
109+
- name: Download artifacts
110+
uses: actions/download-artifact@v5
111+
with:
112+
name: confuserex-packages
113+
114+
- name: Create or update pre-release
115+
uses: softprops/action-gh-release@v2
116+
with:
117+
tag_name: pre-release
118+
name: "Pre-release v${{ steps.version.outputs.VERSION }}"
119+
prerelease: true
120+
make_latest: false
121+
body: |
122+
**Pre-release build** — for testing only, not production use.
123+
124+
Version: `${{ steps.version.outputs.VERSION }}`
125+
Branch: `pre-release`
126+
Commit: ${{ github.sha }}
127+
128+
Download the binaries below to test recent fixes and features before they are included in a stable release.
129+
files: |
130+
ConfuserEx-CLI.zip
131+
ConfuserEx-GUI.zip
132+
ConfuserEx.zip
133+
*.nupkg
134+
85135
# Release: only on PR merge to master
86136
release:
87137
needs: build

Confuser.CLI/Program.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ static int Main(string[] args) {
1919
bool noPause = false;
2020
bool debug = false;
2121
string outDir = null;
22+
string snKeyPath = null;
23+
string snKeyPass = null;
2224
List<string> probePaths = new List<string>();
2325
List<string> plugins = new List<string>();
2426
var p = new OptionSet {
@@ -37,6 +39,12 @@ static int Main(string[] args) {
3739
}, {
3840
"debug", "specifies debug symbol generation.",
3941
value => { debug = (value != null); }
42+
}, {
43+
"snkey=", "specifies strong name key file path.",
44+
value => { snKeyPath = value; }
45+
}, {
46+
"snkeypass=", "specifies strong name key password.",
47+
value => { snKeyPass = value; }
4048
}
4149
};
4250

@@ -60,10 +68,8 @@ static int Main(string[] args) {
6068
try {
6169
var xmlDoc = new XmlDocument();
6270
xmlDoc.Load(files[0]);
63-
proj.Load(xmlDoc);
64-
string crprojDir = Path.GetDirectoryName(Path.GetFullPath(files[0]));
65-
proj.BaseDirectory = Path.GetFullPath(Path.Combine(crprojDir, proj.BaseDirectory));
66-
proj.OutputDirectory = Path.GetFullPath(Path.Combine(crprojDir, proj.OutputDirectory));
71+
proj.Load(xmlDoc, Path.GetDirectoryName(Path.GetFullPath(files[0])));
72+
proj.OutputDirectory = Path.GetFullPath(Path.Combine(proj.BaseDirectory, proj.OutputDirectory));
6773
}
6874
catch (Exception ex) {
6975
WriteLineWithColor(ConsoleColor.Red, "Failed to load project:");
@@ -103,10 +109,13 @@ static int Main(string[] args) {
103109
modulePath = modulePath.Substring(proj.BaseDirectory.Length + 1);
104110
}
105111

106-
if (TryMatchTemplateProject(templateModules, proj.BaseDirectory, modulePath, out var matchedModule))
112+
if (TryMatchTemplateProject(templateModules, proj.BaseDirectory, modulePath, out var matchedModule)) {
113+
if (snKeyPath != null) matchedModule.SNKeyPath = snKeyPath;
114+
if (snKeyPass != null) matchedModule.SNKeyPassword = snKeyPass;
107115
proj.Add(matchedModule);
116+
}
108117
else
109-
proj.Add(new ProjectModule { Path = modulePath });
118+
proj.Add(new ProjectModule { Path = modulePath, SNKeyPath = snKeyPath, SNKeyPassword = snKeyPass });
110119
}
111120

112121
proj.OutputDirectory = outDir;
@@ -202,6 +211,8 @@ static void PrintUsage() {
202211
WriteLine(" -probe : specifies probe directory.");
203212
WriteLine(" -plugin : specifies plugin path.");
204213
WriteLine(" -debug : specifies debug symbol generation.");
214+
WriteLine(" -snkey : specifies strong name key file path.");
215+
WriteLine(" -snkeypass : specifies strong name key password.");
205216
}
206217

207218
static void WriteLineWithColor(ConsoleColor color, string txt) {

Confuser.Core/ConfuserAssemblyResolver.cs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,50 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) {
3333
if (assembly is AssemblyDef assemblyDef)
3434
return assemblyDef;
3535

36-
var resolvedAssemblyDef = InternalExactResolver.Resolve(assembly, sourceModule);
37-
return resolvedAssemblyDef ?? InternalFuzzyResolver.Resolve(assembly, sourceModule);
36+
var resolvedAssemblyDef =
37+
InternalExactResolver.Resolve(assembly, sourceModule) ??
38+
InternalFuzzyResolver.Resolve(assembly, sourceModule);
39+
40+
// Remove AssemblyAttributes.PA_NoPlatform
41+
if (null != resolvedAssemblyDef &&
42+
(AssemblyAttributes.PA_Mask & resolvedAssemblyDef.Attributes) == AssemblyAttributes.PA_NoPlatform) {
43+
resolvedAssemblyDef.Attributes =
44+
resolvedAssemblyDef.Attributes & ~AssemblyAttributes.PA_FullMask;
45+
}
46+
47+
if (resolvedAssemblyDef?.Name == "netstandard" && 0 < resolvedAssemblyDef.ManifestModule.ExportedTypes.Count) {
48+
// Move types from AssemblyRef to here
49+
var module = resolvedAssemblyDef.ManifestModule;
50+
var newTypes = new List<TypeDef>();
51+
var allAssemblyRefs = new List<AssemblyDef>();
52+
53+
module.ExportedTypes.Clear();
54+
55+
foreach (var assemblyRef in module.GetAssemblyRefs()) {
56+
var subAss =
57+
InternalExactResolver.Resolve(assemblyRef, module) ??
58+
InternalFuzzyResolver.Resolve(assemblyRef, module);
59+
allAssemblyRefs.Add(subAss);
60+
foreach (var subModule in subAss?.Modules) {
61+
foreach (var defType in subModule.Types) {
62+
newTypes.Add(defType);
63+
}
64+
subModule.Types.Clear();
65+
foreach (var defType in newTypes) {
66+
module.Types.Add(defType);
67+
}
68+
newTypes.Clear();
69+
}
70+
}
71+
72+
// Remove them because their types has been removed.
73+
foreach (var subAss in allAssemblyRefs) {
74+
InternalExactResolver.Remove(subAss);
75+
InternalFuzzyResolver.Remove(subAss);
76+
}
77+
}
78+
79+
return resolvedAssemblyDef;
3880
}
3981

4082
public void Clear() {

Confuser.Core/ConfuserEngine.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token)
101101
foreach (string probePath in context.Project.ProbePaths)
102102
asmResolver.PostSearchPaths.Insert(0, Path.Combine(context.BaseDirectory, probePath));
103103

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

106116
Marker marker = parameters.GetMarker();
@@ -265,13 +275,10 @@ static void Inspection(ConfuserContext context) {
265275
context.Logger.Info("Resolving dependencies...");
266276
foreach (var dependency in context.Modules
267277
.SelectMany(module => module.GetAssemblyRefs().Select(asmRef => Tuple.Create(asmRef, module)))) {
268-
try {
269-
context.Resolver.ResolveThrow(dependency.Item1, dependency.Item2);
270-
}
271-
catch (AssemblyResolveException ex) {
272-
context.Logger.ErrorException("Failed to resolve dependency of '" + dependency.Item2.Name + "'.", ex);
273-
throw new ConfuserException(ex);
274-
}
278+
var resolved = context.Resolver.Resolve(dependency.Item1, dependency.Item2);
279+
if (resolved == null)
280+
context.Logger.WarnFormat("Failed to resolve dependency '{0}' of '{1}'. Some protections may not work correctly.",
281+
dependency.Item1.FullName, dependency.Item2.Name);
275282
}
276283

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

424431
static void Debug(ConfuserContext context) {
425432
context.Logger.Info("Finalizing...");
433+
if (!context.Project.Debug)
434+
return;
426435
for (int i = 0; i < context.OutputModules.Count; i++) {
427436
if (context.OutputSymbols[i] == null)
428437
continue;

Confuser.Core/DnlibUtils.cs

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,12 @@ public static bool IsDelegate(this TypeDef type) {
209209
/// <param name="baseType">The full name of base type.</param>
210210
/// <returns><c>true</c> if the specified type is inherited from a base type; otherwise, <c>false</c>.</returns>
211211
public static bool InheritsFromCorlib(this TypeDef type, string baseType) {
212-
if (type.BaseType == null)
213-
return false;
214-
215-
TypeDef bas = type;
216-
do {
217-
bas = bas.BaseType.ResolveTypeDefThrow();
212+
var bas = type.BaseType?.ResolveTypeDef();
213+
while (bas != null && bas.DefinitionAssembly.IsCorLib()) {
218214
if (bas.ReflectionFullName == baseType)
219215
return true;
220-
} while (bas.BaseType != null && bas.BaseType.DefinitionAssembly.IsCorLib());
216+
bas = bas.BaseType?.ResolveTypeDef();
217+
}
221218
return false;
222219
}
223220

@@ -228,15 +225,12 @@ public static bool InheritsFromCorlib(this TypeDef type, string baseType) {
228225
/// <param name="baseType">The full name of base type.</param>
229226
/// <returns><c>true</c> if the specified type is inherited from a base type; otherwise, <c>false</c>.</returns>
230227
public static bool InheritsFrom(this TypeDef type, string baseType) {
231-
if (type.BaseType == null)
232-
return false;
233-
234-
TypeDef bas = type;
235-
do {
236-
bas = bas.BaseType.ResolveTypeDefThrow();
228+
var bas = type.BaseType?.ResolveTypeDef();
229+
while (bas != null) {
237230
if (bas.ReflectionFullName == baseType)
238231
return true;
239-
} while (bas.BaseType != null);
232+
bas = bas.BaseType?.ResolveTypeDef();
233+
}
240234
return false;
241235
}
242236

@@ -247,18 +241,12 @@ public static bool InheritsFrom(this TypeDef type, string baseType) {
247241
/// <param name="fullName">The full name of the type of interface.</param>
248242
/// <returns><c>true</c> if the specified type implements the interface; otherwise, <c>false</c>.</returns>
249243
public static bool Implements(this TypeDef type, string fullName) {
250-
do {
251-
foreach (InterfaceImpl iface in type.Interfaces) {
252-
if (iface.Interface.ReflectionFullName == fullName)
253-
return true;
254-
}
255-
256-
if (type.BaseType == null)
257-
return false;
258-
259-
type = type.BaseType.ResolveTypeDefThrow();
260-
} while (type != null);
261-
throw new UnreachableException();
244+
while (type != null) {
245+
if (type.Interfaces.Any(iface => iface.Interface.ReflectionFullName == fullName))
246+
return true;
247+
type = type.BaseType?.ResolveTypeDef();
248+
}
249+
return false;
262250
}
263251

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

452440
if (method.IsPublic && method.IsNewSlot) {
453441
foreach (var iFace in method.DeclaringType.Interfaces) {
454-
var iFaceDef = iFace.Interface.ResolveTypeDefThrow();
455-
if (iFaceDef.FindMethod(method.Name, (MethodSig)method.Signature) != null)
442+
var iFaceDef = iFace.Interface.ResolveTypeDef();
443+
if (iFaceDef != null && iFaceDef.FindMethod(method.Name, (MethodSig)method.Signature) != null)
456444
return true;
457445
}
458446
}

0 commit comments

Comments
 (0)