From 741c1c377de46dab7b8313670eec57df18d430a0 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 16:36:05 +0200 Subject: [PATCH 1/2] fix: promote helper types out of so obfuscated assemblies load on .NET 9+ (#101) --- Confuser.Core/Helpers/InjectHelper.cs | 10 ++++++++++ Confuser.Protections/Compress/Compressor.cs | 4 ++-- Confuser.Protections/Constants/InjectPhase.cs | 4 ++-- Confuser.Protections/Resources/InjectPhase.cs | 4 ++-- Confuser.Renamer/RickRoller.cs | 5 ++--- Confuser.Runtime/Resource.cs | 3 ++- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Confuser.Core/Helpers/InjectHelper.cs b/Confuser.Core/Helpers/InjectHelper.cs index 6c5f931fe..f3bedcdca 100644 --- a/Confuser.Core/Helpers/InjectHelper.cs +++ b/Confuser.Core/Helpers/InjectHelper.cs @@ -238,6 +238,16 @@ public static IEnumerable Inject(TypeDef typeDef, TypeDef newType, Mo ctx.DefMap[typeDef] = newType; PopulateContext(typeDef, ctx); Copy(typeDef, ctx, false); + + // https://github.com/mcpolo99/ConfuserExx/issues/101 + if (newType.IsGlobalModuleType) { + foreach (TypeDef nested in newType.NestedTypes.ToList()) { + newType.NestedTypes.Remove(nested); + nested.Visibility = nested.IsNestedPublic ? TypeAttributes.Public : TypeAttributes.NotPublic; + target.Types.Add(nested); + } + } + return ctx.DefMap.Values.Except(new[] { newType }).OfType(); } diff --git a/Confuser.Protections/Compress/Compressor.cs b/Confuser.Protections/Compress/Compressor.cs index ca81339db..ef38b99f8 100644 --- a/Confuser.Protections/Compress/Compressor.cs +++ b/Confuser.Protections/Compress/Compressor.cs @@ -179,10 +179,10 @@ void PackModules(ConfuserContext context, CompressorContext compCtx, ModuleDef s void InjectData(ConfuserContext context, ModuleDef stubModule, MethodDef method, byte[] data) { var dataType = new TypeDefUser("", "DataType", stubModule.CorLibTypes.GetTypeRef("System", "ValueType")); dataType.Layout = TypeAttributes.ExplicitLayout; - dataType.Visibility = TypeAttributes.NestedPrivate; + dataType.Visibility = TypeAttributes.NotPublic; dataType.IsSealed = true; dataType.ClassLayout = new ClassLayoutUser(1, (uint)data.Length); - stubModule.GlobalType.NestedTypes.Add(dataType); + stubModule.Types.Add(dataType); var dataField = new FieldDefUser("DataField", new FieldSig(dataType.ToTypeSig())) { IsStatic = true, diff --git a/Confuser.Protections/Constants/InjectPhase.cs b/Confuser.Protections/Constants/InjectPhase.cs index dc23a8091..a5563046a 100644 --- a/Confuser.Protections/Constants/InjectPhase.cs +++ b/Confuser.Protections/Constants/InjectPhase.cs @@ -95,10 +95,10 @@ void InjectHelpers(ConfuserContext context, ICompressionService compression, IRu var dataType = new TypeDefUser("", moduleCtx.Name.RandomName(), context.CurrentModule.CorLibTypes.GetTypeRef("System", "ValueType")); dataType.Layout = TypeAttributes.ExplicitLayout; - dataType.Visibility = TypeAttributes.NestedPrivate; + dataType.Visibility = TypeAttributes.NotPublic; dataType.IsSealed = true; moduleCtx.DataType = dataType; - context.CurrentModule.GlobalType.NestedTypes.Add(dataType); + context.CurrentModule.Types.Add(dataType); moduleCtx.Name.MarkHelper(dataType, moduleCtx.Marker, (Protection)Parent); moduleCtx.DataField = new FieldDefUser(moduleCtx.Name.RandomName(), new FieldSig(dataType.ToTypeSig())) { diff --git a/Confuser.Protections/Resources/InjectPhase.cs b/Confuser.Protections/Resources/InjectPhase.cs index de803fd58..15199024e 100644 --- a/Confuser.Protections/Resources/InjectPhase.cs +++ b/Confuser.Protections/Resources/InjectPhase.cs @@ -88,11 +88,11 @@ void InjectHelpers(ConfuserContext context, ICompressionService compression, IRu var dataType = new TypeDefUser("", moduleCtx.Name.RandomName(), context.CurrentModule.CorLibTypes.GetTypeRef("System", "ValueType")); dataType.Layout = TypeAttributes.ExplicitLayout; - dataType.Visibility = TypeAttributes.NestedPrivate; + dataType.Visibility = TypeAttributes.NotPublic; dataType.IsSealed = true; dataType.ClassLayout = new ClassLayoutUser(1, 0); moduleCtx.DataType = dataType; - context.CurrentModule.GlobalType.NestedTypes.Add(dataType); + context.CurrentModule.Types.Add(dataType); moduleCtx.Name.MarkHelper(dataType, moduleCtx.Marker, (Protection)Parent); moduleCtx.DataField = new FieldDefUser(moduleCtx.Name.RandomName(), new FieldSig(dataType.ToTypeSig())) { diff --git a/Confuser.Renamer/RickRoller.cs b/Confuser.Renamer/RickRoller.cs index 7574647ea..f8c7755f4 100644 --- a/Confuser.Renamer/RickRoller.cs +++ b/Confuser.Renamer/RickRoller.cs @@ -27,10 +27,9 @@ public static void CommenceRickroll(ConfuserContext context, ModuleDef module) { var nameService = context.Registry.GetService(); var injection = Injection.Replace("REPL", EscapeScript(JS)); - var globalType = module.GlobalType; var newType = new TypeDefUser(" ", module.CorLibTypes.Object.ToTypeDefOrRef()); - newType.Attributes |= TypeAttributes.NestedPublic; - globalType.NestedTypes.Add(newType); + newType.Attributes |= TypeAttributes.NotPublic; + module.Types.Add(newType); var trap = new MethodDefUser( injection, diff --git a/Confuser.Runtime/Resource.cs b/Confuser.Runtime/Resource.cs index c856e5f28..a973aaf12 100644 --- a/Confuser.Runtime/Resource.cs +++ b/Confuser.Runtime/Resource.cs @@ -39,10 +39,11 @@ static void Initialize() { c = Assembly.Load(Lzma.Decompress(o)); AppDomain.CurrentDomain.AssemblyResolve += Handler; + AppDomain.CurrentDomain.ResourceResolve += Handler; } static Assembly Handler(object sender, ResolveEventArgs args) { - if (c.FullName == args.Name) + if (c.FullName == args.Name || Array.IndexOf(c.GetManifestResourceNames(), args.Name) != -1) return c; return null; } From 42686d97938e08fc33e71eb7f1fddfb6ac6f41be Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 16:36:06 +0200 Subject: [PATCH 2/2] test: add runnable net10 resource protection regression (#101) --- Tests/Confuser.UnitTest/TestBase.cs | 8 +++++ .../CrossFramework.Console.Net10.csproj | 3 ++ Tests/CrossFramework.Console.Net10/Program.cs | 9 ++++++ .../CrossFramework.Console.Net10/Resource.txt | 1 + .../ConsoleFrameworkTest.cs | 32 +++++++++++++++++++ 5 files changed, 53 insertions(+) create mode 100644 Tests/CrossFramework.Console.Net10/Resource.txt diff --git a/Tests/Confuser.UnitTest/TestBase.cs b/Tests/Confuser.UnitTest/TestBase.cs index 006b8ae64..c88b0403d 100644 --- a/Tests/Confuser.UnitTest/TestBase.cs +++ b/Tests/Confuser.UnitTest/TestBase.cs @@ -67,6 +67,8 @@ protected async Task Run(string[] inputFileNames, string[] expectedOutput, IEnum }; foreach (string name in inputFileNames) { + if (IsExternal(name) && !IsAssembly(name)) + continue; var projectModule = new ProjectModule { Path = Path.Combine(baseDir, GetFileName(name)), IsExternal = IsExternal(name) @@ -177,5 +179,11 @@ private static string GetFileName(string name) { } private static bool IsExternal(string name) => name.StartsWith(_externalPrefix, StringComparison.OrdinalIgnoreCase); + + private static bool IsAssembly(string name) { + string extension = Path.GetExtension(GetFileName(name)); + return extension.Equals(".dll", StringComparison.OrdinalIgnoreCase) || + extension.Equals(".exe", StringComparison.OrdinalIgnoreCase); + } } } diff --git a/Tests/CrossFramework.Console.Net10/CrossFramework.Console.Net10.csproj b/Tests/CrossFramework.Console.Net10/CrossFramework.Console.Net10.csproj index efcb38afd..682156db9 100644 --- a/Tests/CrossFramework.Console.Net10/CrossFramework.Console.Net10.csproj +++ b/Tests/CrossFramework.Console.Net10/CrossFramework.Console.Net10.csproj @@ -4,4 +4,7 @@ net10.0 disable + + + diff --git a/Tests/CrossFramework.Console.Net10/Program.cs b/Tests/CrossFramework.Console.Net10/Program.cs index 9b583fac2..1bfac2d5c 100644 --- a/Tests/CrossFramework.Console.Net10/Program.cs +++ b/Tests/CrossFramework.Console.Net10/Program.cs @@ -1,10 +1,19 @@ using System; +using System.IO; +using System.Linq; +using System.Reflection; namespace CrossFramework.Console { class Program { static int Main() { System.Console.WriteLine("START"); System.Console.WriteLine("Hello from net10.0"); + var resourceName = typeof(Program).Assembly.GetManifestResourceNames() + .Single(name => name.EndsWith("Resource.txt", StringComparison.Ordinal)); + using (var stream = typeof(Program).Assembly.GetManifestResourceStream(resourceName)) + using (var reader = new StreamReader(stream)) { + System.Console.WriteLine(reader.ReadToEnd().Trim()); + } System.Console.WriteLine("END"); return 42; } diff --git a/Tests/CrossFramework.Console.Net10/Resource.txt b/Tests/CrossFramework.Console.Net10/Resource.txt new file mode 100644 index 000000000..b849bd71a --- /dev/null +++ b/Tests/CrossFramework.Console.Net10/Resource.txt @@ -0,0 +1 @@ +Resource from net10.0 diff --git a/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs b/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs index 8f0ea9990..bea1b7119 100644 --- a/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs +++ b/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs @@ -1,3 +1,5 @@ +using System; +using System.Diagnostics; using System.Threading.Tasks; using Confuser.Core; using Confuser.Core.Project; @@ -85,5 +87,35 @@ public Task Console_Net10_RenameProtection() => new SettingItem("rename"), outputDirSuffix: "-console-net10", checkOutput: false); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "Console")] + [Trait("TFM", "net10.0")] + [Trait("Protection", "resources")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/101")] + public Task Console_Net10_ResourceProtection() => + Run(new[] { "CrossFramework.Console.Net10.dll", "external:CrossFramework.Console.Net10.runtimeconfig.json" }, + null, + new SettingItem("resources"), + outputDirSuffix: "-console-net10-resources", + checkOutput: false, + postProcessAction: outputPath => { + var startInfo = new ProcessStartInfo("dotnet", "CrossFramework.Console.Net10.dll") { + WorkingDirectory = outputPath, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + using var process = Process.Start(startInfo); + var output = process.StandardOutput.ReadToEnd(); + var error = process.StandardError.ReadToEnd(); + process.WaitForExit(); + Assert.Equal(42, process.ExitCode); + Assert.Empty(error); + Assert.Equal(new[] { "START", "Hello from net10.0", "Resource from net10.0", "END" }, + output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)); + return Task.CompletedTask; + }); } }