From 4755b98e635ff652690222e5f23daa70159d63ac Mon Sep 17 00:00:00 2001
From: polos <32239939+mcpolo99@users.noreply.github.com>
Date: Thu, 11 Jun 2026 06:29:45 +0200
Subject: [PATCH 01/14] feat: retarget ConfuserEx from .NET Framework 4.6.1 to
.NET 10 (#71)
---
.gitattributes | 46 +-
.github/workflows/ci.yml | 33 +-
.github/workflows/format.yml | 34 +
.github/workflows/test.yml | 240 ++++++
CONTRIBUTING.md | 145 +++-
Confuser.CLI/Confuser.CLI.csproj | 10 +-
Confuser.CLI/Program.cs | 10 +-
Confuser.Core/API/APIStore.cs | 6 +-
Confuser.Core/API/IDataStore.cs | 2 +-
Confuser.Core/API/IOpaquePredicate.cs | 2 +-
Confuser.Core/Annotations.cs | 2 +-
Confuser.Core/Confuser.Core.csproj | 3 +-
Confuser.Core/ConfuserAssemblyResolver.cs | 10 +-
Confuser.Core/ConfuserComponent.cs | 2 +-
Confuser.Core/ConfuserContext.cs | 2 +-
Confuser.Core/ConfuserEngine.cs | 13 +-
Confuser.Core/ConfuserParameters.cs | 2 +-
Confuser.Core/CoreComponent.cs | 2 -
Confuser.Core/DependencyResolver.cs | 2 +-
Confuser.Core/DnlibUtils.cs | 2 +-
Confuser.Core/Helpers/ControlFlowGraph.cs | 8 +-
Confuser.Core/Helpers/InjectHelper.cs | 41 +-
Confuser.Core/Helpers/KeySequence.cs | 10 +-
Confuser.Core/Helpers/MutationHelper.cs | 10 +-
Confuser.Core/ILogger.cs | 2 +-
Confuser.Core/LZMA/Common/CRC.cs | 2 +-
Confuser.Core/LZMA/Common/InBuffer.cs | 2 +-
Confuser.Core/LZMA/Common/OutBuffer.cs | 2 +-
.../LZMA/Compress/LZ/IMatchFinder.cs | 4 +-
Confuser.Core/LZMA/Compress/LZ/LzBinTree.cs | 16 +-
Confuser.Core/LZMA/Compress/LZ/LzInWindow.cs | 2 +-
Confuser.Core/LZMA/Compress/LZ/LzOutWindow.cs | 2 +-
Confuser.Core/LZMA/Compress/LZMA/LzmaBase.cs | 4 +-
.../LZMA/Compress/LZMA/LzmaDecoder.cs | 10 +-
.../LZMA/Compress/LZMA/LzmaEncoder.cs | 62 +-
.../LZMA/Compress/RangeCoder/RangeCoder.cs | 4 +-
.../LZMA/Compress/RangeCoder/RangeCoderBit.cs | 4 +-
.../Compress/RangeCoder/RangeCoderBitTree.cs | 8 +-
Confuser.Core/LZMA/ICoder.cs | 4 +-
Confuser.Core/Marker.cs | 102 +--
Confuser.Core/MarkerResult.cs | 2 +-
Confuser.Core/ModuleSorter.cs | 4 +-
Confuser.Core/NativeEraser.cs | 24 +-
Confuser.Core/NullLogger.cs | 2 +-
Confuser.Core/ObfAttrMarker.cs | 86 +-
Confuser.Core/ObfAttrParser.cs | 2 +-
Confuser.Core/Packer.cs | 18 +-
Confuser.Core/PluginDiscovery.cs | 2 +-
Confuser.Core/Project/ConfuserProject.cs | 64 +-
.../Project/InvalidPatternException.cs | 2 +-
Confuser.Core/Project/PatternParser.cs | 4 +-
Confuser.Core/Project/PatternToken.cs | 2 +-
Confuser.Core/Project/PatternTokenizer.cs | 2 +-
Confuser.Core/Project/Patterns/AndOperator.cs | 2 +-
.../Project/Patterns/DeclTypeFunction.cs | 2 +-
.../Project/Patterns/FullNameFunction.cs | 2 +-
.../Project/Patterns/HasAttrFunction.cs | 2 +-
.../Project/Patterns/InheritsFunction.cs | 2 +-
.../Project/Patterns/IsTypeFunction.cs | 2 +-
.../Project/Patterns/LiteralExpression.cs | 2 +-
.../Project/Patterns/MatchFunction.cs | 2 +-
.../Project/Patterns/MemberTypeFunction.cs | 2 +-
.../Project/Patterns/ModuleFunction.cs | 2 +-
.../Project/Patterns/NameFunction.cs | 2 +-
.../Project/Patterns/NamespaceFunction.cs | 2 +-
Confuser.Core/Project/Patterns/NotOperator.cs | 2 +-
Confuser.Core/Project/Patterns/OrOperator.cs | 2 +-
.../Project/Patterns/PatternExpression.cs | 2 +-
.../Project/Patterns/PatternFunction.cs | 2 +-
.../Project/Patterns/PatternOperator.cs | 2 +-
Confuser.Core/Protection.cs | 2 +-
.../ProtectionDependencyAttributes.cs | 2 +-
Confuser.Core/ProtectionParameters.cs | 2 +-
Confuser.Core/ProtectionPhase.cs | 2 +-
Confuser.Core/ProtectionPipeline.cs | 2 +-
Confuser.Core/ProtectionPreset.cs | 2 +-
Confuser.Core/ProtectionSettings.cs | 2 +-
Confuser.Core/ProtectionTargets.cs | 2 +-
Confuser.Core/ServiceRegistry.cs | 2 +-
Confuser.Core/Services/CompressionService.cs | 2 +-
Confuser.Core/Services/MarkerService.cs | 2 +-
Confuser.Core/Services/TraceService.cs | 13 +-
Confuser.Core/UnreachableException.cs | 2 +-
Confuser.Core/WatermarkingProtection.cs | 2 +-
.../AST/ArrayIndexExpression.cs | 2 +-
Confuser.DynCipher/AST/AssignmentStatement.cs | 2 +-
Confuser.DynCipher/AST/BinOpExpression.cs | 2 +-
Confuser.DynCipher/AST/Expression.cs | 2 +-
Confuser.DynCipher/AST/LiteralExpression.cs | 2 +-
Confuser.DynCipher/AST/LoopStatement.cs | 2 +-
Confuser.DynCipher/AST/Statement.cs | 2 +-
Confuser.DynCipher/AST/StatementBlock.cs | 2 +-
Confuser.DynCipher/AST/UnaryOpExpression.cs | 2 +-
Confuser.DynCipher/AST/Variable.cs | 2 +-
Confuser.DynCipher/AST/VariableExpression.cs | 2 +-
Confuser.DynCipher/Confuser.DynCipher.csproj | 2 +-
Confuser.DynCipher/DynCipherComponent.cs | 2 +-
Confuser.DynCipher/DynCipherService.cs | 2 +-
Confuser.DynCipher/Elements/AddKey.cs | 2 +-
Confuser.DynCipher/Elements/BinOp.cs | 2 +-
Confuser.DynCipher/Elements/CryptoElement.cs | 2 +-
Confuser.DynCipher/Elements/Matrix.cs | 20 +-
Confuser.DynCipher/Elements/NumOp.cs | 2 +-
Confuser.DynCipher/Elements/RotateBit.cs | 2 +-
Confuser.DynCipher/Elements/Swap.cs | 2 +-
Confuser.DynCipher/Generation/CILCodeGen.cs | 2 +-
.../Generation/CipherGenContext.cs | 2 +-
.../Generation/CipherGenerator.cs | 2 +-
Confuser.DynCipher/Generation/DMCodeGen.cs | 2 +-
.../Generation/ExpressionGenerator.cs | 8 +-
Confuser.DynCipher/Generation/x86CodeGen.cs | 36 +-
.../Transforms/ConvertVariables.cs | 2 +-
.../Transforms/ExpansionTransform.cs | 4 +-
.../Transforms/MulToShiftTransform.cs | 2 +-
.../Transforms/NormalizeBinOpTransform.cs | 10 +-
.../Transforms/ShuffleTransform.cs | 6 +-
Confuser.DynCipher/Utils.cs | 2 +-
.../Confuser.MSBuild.Tasks.csproj | 12 +-
Confuser.MSBuild.Tasks/CreateProjectTask.cs | 22 +-
Confuser.MSBuild.Tasks/MSBuildLogger.cs | 4 +-
Confuser.Protections/AntiDumpProtection.cs | 2 +-
Confuser.Protections/AntiILDasmProtection.cs | 2 +-
.../AntiTamper/AntiTamperExtensions.cs | 2 +-
.../AntiTamper/DynamicDeriver.cs | 2 +-
.../AntiTamper/IKeyDeriver.cs | 2 +-
.../AntiTamper/IModeHandler.cs | 2 +-
Confuser.Protections/AntiTamper/JITBody.cs | 20 +-
.../AntiTamper/NormalDeriver.cs | 2 +-
Confuser.Protections/AntiTamper/NormalMode.cs | 3 +-
Confuser.Protections/Compress/Compressor.cs | 22 +-
.../Compress/DynamicDeriver.cs | 2 +-
Confuser.Protections/Compress/IKeyDeriver.cs | 2 +-
.../Compress/NormalDeriver.cs | 2 +-
.../Compress/StubProtection.cs | 8 +-
.../Confuser.Protections.csproj | 8 +-
Confuser.Protections/Constants/CEContext.cs | 2 +-
.../Constants/ConstantProtection.cs | 2 +-
Confuser.Protections/Constants/DynamicMode.cs | 2 +-
.../Constants/EncodeElements.cs | 2 +-
Confuser.Protections/Constants/EncodePhase.cs | 12 +-
Confuser.Protections/Constants/IEncodeMode.cs | 2 +-
Confuser.Protections/Constants/InjectPhase.cs | 16 +-
Confuser.Protections/Constants/Mode.cs | 2 +-
Confuser.Protections/Constants/NormalMode.cs | 2 +-
Confuser.Protections/Constants/x86Mode.cs | 28 +-
.../ControlFlow/BlockParser.cs | 2 +-
Confuser.Protections/ControlFlow/Blocks.cs | 2 +-
Confuser.Protections/ControlFlow/CFContext.cs | 4 +-
.../ControlFlow/ControlFlowProtection.cs | 2 +-
.../ControlFlow/ExpressionPredicate.cs | 2 +-
.../ControlFlow/IPredicate.cs | 2 +-
.../ControlFlow/JumpMangler.cs | 6 +-
.../ControlFlow/ManglerBase.cs | 2 +-
.../ControlFlow/NormalPredicate.cs | 2 +-
.../ControlFlow/SwitchMangler.cs | 10 +-
.../ControlFlow/x86Predicate.cs | 30 +-
Confuser.Protections/HardeningProtection.cs | 2 +-
.../ReferenceProxy/ExpressionEncoding.cs | 2 +-
.../ReferenceProxy/IRPEncoding.cs | 2 +-
.../ReferenceProxy/MildMode.cs | 2 +-
.../ReferenceProxy/NormalEncoding.cs | 2 +-
.../ReferenceProxy/RPContext.cs | 2 +-
Confuser.Protections/ReferenceProxy/RPMode.cs | 2 +-
.../ReferenceProxy/ReferenceProxyPhase.cs | 10 +-
.../ReferenceProxyProtection.cs | 2 +-
.../ReferenceProxy/StrongMode.cs | 4 +-
.../ReferenceProxy/x86Encoding.cs | 18 +-
Confuser.Protections/Resources/DynamicMode.cs | 2 +-
Confuser.Protections/Resources/IEncodeMode.cs | 2 +-
Confuser.Protections/Resources/InjectPhase.cs | 6 +-
Confuser.Protections/Resources/MDPhase.cs | 4 +-
Confuser.Protections/Resources/Mode.cs | 2 +-
Confuser.Protections/Resources/NormalMode.cs | 2 +-
Confuser.Protections/Resources/REContext.cs | 2 +-
.../Resources/ResourceProtection.cs | 2 +-
.../TypeScrambler/AnalyzePhase.cs | 2 +-
.../Scrambler/Analyzers/ContextAnalyzer`1.cs | 2 +-
.../InstructionRewriterFactory.cs | 2 +-
.../MethodSpecInstructionRewriter.cs | 3 +-
.../TypeScrambler/Scrambler/ScannedMethod.cs | 11 +-
Confuser.Renamer/AnalyzePhase.cs | 4 +-
.../Analyzers/CaliburnAnalyzer.cs | 2 +-
.../Analyzers/InterReferenceAnalyzer.cs | 2 +-
Confuser.Renamer/Analyzers/JsonAnalyzer.cs | 2 +-
.../Analyzers/LdtokenEnumAnalyzer.cs | 12 +-
.../Analyzers/ManifestResourceAnalyzer.cs | 18 +-
.../Analyzers/ReflectionAnalyzer.cs | 4 +-
Confuser.Renamer/Analyzers/VTableAnalyzer.cs | 780 +++++++++---------
.../Analyzers/VisualBasicRuntimeAnalyzer.cs | 7 +-
Confuser.Renamer/Analyzers/WPFAnalyzer.cs | 2 +-
Confuser.Renamer/BAML/BAMLAnalyzer.cs | 4 +-
Confuser.Renamer/BAML/BamlDocument.cs | 2 +-
Confuser.Renamer/BAML/BamlElement.cs | 2 +-
Confuser.Renamer/BAML/BamlRW.cs | 6 +-
Confuser.Renamer/BAML/IKnownThings.cs | 2 +-
Confuser.Renamer/BAML/KnownThingsv3.cs | 2 +-
Confuser.Renamer/BAML/KnownThingsv4.cs | 2 +-
Confuser.Renamer/Confuser.Renamer.csproj | 6 +-
Confuser.Renamer/GenericArgumentResolver.cs | 8 +-
Confuser.Renamer/GenericArguments.cs | 2 +-
Confuser.Renamer/INameReference.cs | 2 +-
Confuser.Renamer/IRenamer.cs | 2 +-
Confuser.Renamer/NameService.cs | 27 +-
Confuser.Renamer/PostRenamePhase.cs | 2 +-
.../References/BAMLPathTypeReference.cs | 2 +-
.../MemberOldestSiblingReference.cs | 6 +-
.../References/MemberOverrideReference.cs | 6 +-
.../References/MemberRefReference.cs | 2 +-
.../References/MemberSiblingReference.cs | 8 +-
.../References/OverrideDirectiveReference.cs | 2 +-
.../References/RequiredPrefixReference.cs | 4 +-
.../References/TypeRefReference.cs | 2 +-
Confuser.Renamer/RenamePhase.cs | 5 +-
Confuser.Renamer/VTable.cs | 4 +-
Confuser.Runtime/AntiDebug.Antinet.cs | 2 +-
Confuser.Runtime/AntiDebug.Safe.cs | 2 +-
Confuser.Runtime/AntiDebug.Win32.cs | 6 +-
Confuser.Runtime/AntiDump.cs | 2 +-
Confuser.Runtime/AntiTamper.JIT.cs | 6 +-
Confuser.Runtime/Constant.cs | 10 +-
.../ExcludeFromCodeCoverageAttribute.cs | 1 +
Confuser.Runtime/Lzma.cs | 10 +-
Confuser.Runtime/Mutation.cs | 2 +-
Confuser.Runtime/RefProxy.Strong.cs | 2 +-
Confuser.Runtime/Resource.cs | 2 +-
.../antinet/AntiManagedDebugger.cs | 2 +-
.../antinet/AntiManagedProfiler.cs | 36 +-
...rocessCorruptedStateExceptionsAttribute.cs | 2 +-
Confuser.Runtime/antinet/PEInfo.cs | 2 +-
Confuser2.sln | 405 +++++++++
ConfuserEx.Common.props | 6 +-
ConfuserEx/App.xaml.cs | 2 +-
ConfuserEx/BoolToVisibilityConverter.cs | 2 +-
ConfuserEx/BrushToColorConverter.cs | 2 +-
ConfuserEx/CompComboBox.xaml.cs | 2 +-
ConfuserEx/ComponentConverter.cs | 2 +-
ConfuserEx/ComponentDiscovery.cs | 116 +--
ConfuserEx/ConfuserEx.csproj | 16 +-
ConfuserEx/EmptyToBoolConverter.cs | 2 +-
ConfuserEx/EnumValuesExtension.cs | 2 +-
ConfuserEx/FileDragDrop.cs | 16 +-
ConfuserEx/InvertBoolConverter.cs | 2 +-
ConfuserEx/MainWindow.xaml.cs | 2 +-
ConfuserEx/Skin.cs | 2 +-
ConfuserEx/ViewModel/IViewModel.cs | 2 +-
ConfuserEx/ViewModel/Project/ProjectRuleVM.cs | 2 +-
.../ViewModel/Project/ProjectSettingVM.cs | 2 +-
ConfuserEx/ViewModel/StringItem.cs | 2 +-
ConfuserEx/ViewModel/UI/AboutTabVM.cs | 6 +-
ConfuserEx/ViewModel/UI/AppVM.cs | 8 +-
ConfuserEx/ViewModel/UI/ProjectTabVM.cs | 2 +-
ConfuserEx/ViewModel/UI/ProtectTabVM.cs | 14 +-
ConfuserEx/ViewModel/UI/SettingsTabVM.cs | 4 +-
ConfuserEx/ViewModel/UI/TabViewModel.cs | 2 +-
ConfuserEx/ViewModel/Utils.cs | 2 +-
ConfuserEx/ViewModel/ViewModelBase.cs | 2 +-
ConfuserEx/Views/ProjectModuleView.xaml.cs | 4 +-
ConfuserEx/Views/ProjectRuleView.xaml.cs | 4 +-
.../Views/ProjectTabAdvancedView.xaml.cs | 4 +-
README.md | 31 +
.../118_EnhancedStrongName.Test.csproj | 2 +-
.../EnhancedStrongNameTest.cs | 2 +-
.../118_EnhancedStrongName.csproj | 10 +-
.../118_EnhancedStrongName/IClrStrongName.cs | 48 +-
.../123_InheritCustomAttr.Test.csproj | 2 +-
.../InheritCustomAttributeTest.cs | 8 +-
Tests/123_InheritCustomAttr/D.cs | 4 +-
.../161_DynamicTypeRename.Test.csproj | 2 +-
.../RenameDynamicMethodTest.cs | 6 +-
.../193_ConstantsInlining.Test.csproj | 2 +-
.../ConstantInliningTest.cs | 6 +-
.../244_ClrProtection.Test.csproj | 2 +-
.../ProtectClrAssemblyTest.cs | 2 +-
.../244_ClrProtection.vcxproj | 2 +-
.../252_ComplexInterfaceRenaming.Test.csproj | 2 +-
Tests/252_ComplexInterfaceRenaming/IWorker.cs | 5 +-
Tests/252_ComplexInterfaceRenaming/Manager.cs | 3 +-
...70_EnumArrayConstantProtection.Test.csproj | 2 +-
.../InternalClass1.cs | 2 +-
...06_ComplexClassStructureRename.Test.csproj | 2 +-
.../ComplexRenameTest.cs | 2 +-
.../342_InterfaceRenamingLoop.Test.csproj | 2 +-
Tests/342_InterfaceRenamingLoop/Program.cs | 2 +-
.../345_RenameDynamicParameter.Test.csproj | 2 +-
Tests/345_RenameDynamicParameter/Program.cs | 2 +-
.../389_MixedCultureCasing.Test.csproj | 2 +-
.../MixedCultureCasingTest.cs | 9 +-
Tests/389_MixedCultureCasing/Program.cs | 4 +-
...21_NewtonsoftJsonSerialization.Test.csproj | 2 +-
.../NewtonsoftJsonTest.cs | 2 +-
.../470_ImplementationInBaseClass.Test.csproj | 2 +-
.../RenameTest.cs | 6 +-
.../78_SignatureMismatch.Test.csproj | 2 +-
.../SignatureMismatchTest.cs | 2 +-
Tests/78_SignatureMismatch/Program.cs | 6 +-
Tests/AntiTamper.Test/AntiTamper.Test.csproj | 2 +-
.../BlockingReferences.Test.csproj | 2 +-
Tests/CompressorWithResx.Test/CompressTest.cs | 14 +-
.../CompressorWithResx.Test.csproj | 2 +-
Tests/Confuser.CLI.Test/CliEndToEndTest.cs | 99 +++
.../Confuser.CLI.Test.csproj | 38 +
.../Fixtures/SampleApp.crproj | 6 +
.../Fixtures/SampleApp/Program.cs | 12 +
.../Fixtures/SampleApp/SampleApp.csproj | 8 +
.../Confuser.Core.Test.csproj | 4 +-
Tests/Confuser.Core.Test/Helpers.cs | 8 +-
Tests/Confuser.Core.Test/UtilsTest.cs | 2 +-
.../Confuser.GUI.Test.csproj | 27 +
Tests/Confuser.GUI.Test/GuiSmokeTest.cs | 229 +++++
.../Analyzers/ManifestResourceAnalyzerTest.cs | 3 +-
.../Analyzers/ReflectionAnalyzerTest.cs | 2 +-
.../Analyzers/TypeBlobAnalyzerTest.cs | 12 +-
.../Confuser.Renamer.Test.csproj | 4 +-
Tests/Confuser.Renamer.Test/Helpers.cs | 8 +-
Tests/Confuser.Renamer.Test/VTableTest.cs | 2 +-
.../Confuser.UnitTest.csproj | 8 +-
Tests/Confuser.UnitTest/TestBase.cs | 18 +-
Tests/Confuser.UnitTest/XUnitLogger.cs | 2 +-
.../CrossFramework.Console.Net10.csproj | 7 +
Tests/CrossFramework.Console.Net10/Program.cs | 12 +
.../CrossFramework.Console.Net20.csproj | 10 +
Tests/CrossFramework.Console.Net20/Program.cs | 12 +
.../CrossFramework.Console.Net35.csproj | 10 +
Tests/CrossFramework.Console.Net35/Program.cs | 12 +
.../CrossFramework.Console.Net40.csproj | 10 +
Tests/CrossFramework.Console.Net40/Program.cs | 12 +
.../CrossFramework.Console.Net48.csproj | 7 +
Tests/CrossFramework.Console.Net48/Program.cs | 12 +
.../CrossFramework.Console.Net6.csproj | 7 +
Tests/CrossFramework.Console.Net6/Program.cs | 12 +
.../CrossFramework.Console.Net8.csproj | 7 +
Tests/CrossFramework.Console.Net8/Program.cs | 12 +
.../CrossFramework.Library.Net10.csproj | 6 +
.../SampleService.cs | 43 +
.../CrossFramework.Library.Net48.csproj | 6 +
.../SampleService.cs | 43 +
.../CrossFramework.Library.Net6.csproj | 6 +
.../SampleService.cs | 43 +
.../CrossFramework.Library.Net8.csproj | 6 +
.../SampleService.cs | 43 +
.../CrossFramework.Library.NetStd20.csproj | 6 +
.../SampleService.cs | 43 +
.../ConsoleFrameworkTest.cs | 89 ++
.../CrossFramework.Test.csproj | 54 ++
.../LibraryFrameworkTest.cs | 68 ++
.../WinFormsFrameworkTest.cs | 78 ++
Tests/CrossFramework.Test/WpfFrameworkTest.cs | 78 ++
Tests/CrossFramework.WPF.Net10/App.xaml | 5 +
Tests/CrossFramework.WPF.Net10/App.xaml.cs | 18 +
.../CrossFramework.WPF.Net10.csproj | 8 +
.../CrossFramework.WPF.Net10/MainWindow.xaml | 9 +
.../MainWindow.xaml.cs | 18 +
.../CrossFramework.WPF.Net35.csproj | 14 +
Tests/CrossFramework.WPF.Net35/Program.cs | 46 ++
.../CrossFramework.WPF.Net40.csproj | 14 +
Tests/CrossFramework.WPF.Net40/Program.cs | 46 ++
Tests/CrossFramework.WPF.Net48/App.xaml | 5 +
Tests/CrossFramework.WPF.Net48/App.xaml.cs | 18 +
.../CrossFramework.WPF.Net48.csproj | 8 +
.../CrossFramework.WPF.Net48/MainWindow.xaml | 9 +
.../MainWindow.xaml.cs | 18 +
Tests/CrossFramework.WPF.Net6/App.xaml | 5 +
Tests/CrossFramework.WPF.Net6/App.xaml.cs | 18 +
.../CrossFramework.WPF.Net6.csproj | 8 +
Tests/CrossFramework.WPF.Net6/MainWindow.xaml | 9 +
.../MainWindow.xaml.cs | 18 +
Tests/CrossFramework.WPF.Net8/App.xaml | 5 +
Tests/CrossFramework.WPF.Net8/App.xaml.cs | 18 +
.../CrossFramework.WPF.Net8.csproj | 8 +
Tests/CrossFramework.WPF.Net8/MainWindow.xaml | 9 +
.../MainWindow.xaml.cs | 18 +
.../CrossFramework.WinForms.Net10.csproj | 8 +
.../CrossFramework.WinForms.Net10/MainForm.cs | 39 +
.../CrossFramework.WinForms.Net10/Program.cs | 25 +
.../CrossFramework.WinForms.Net35.csproj | 12 +
.../CrossFramework.WinForms.Net35/MainForm.cs | 39 +
.../CrossFramework.WinForms.Net35/Program.cs | 23 +
.../CrossFramework.WinForms.Net40.csproj | 12 +
.../CrossFramework.WinForms.Net40/MainForm.cs | 39 +
.../CrossFramework.WinForms.Net40/Program.cs | 23 +
.../CrossFramework.WinForms.Net48.csproj | 8 +
.../CrossFramework.WinForms.Net48/MainForm.cs | 38 +
.../CrossFramework.WinForms.Net48/Program.cs | 25 +
.../CrossFramework.WinForms.Net6.csproj | 8 +
.../CrossFramework.WinForms.Net6/MainForm.cs | 38 +
Tests/CrossFramework.WinForms.Net6/Program.cs | 25 +
.../CrossFramework.WinForms.Net8.csproj | 8 +
.../CrossFramework.WinForms.Net8/MainForm.cs | 38 +
Tests/CrossFramework.WinForms.Net8/Program.cs | 25 +
Tests/Directory.Build.props | 20 +
.../IncorrectRedirectToGacTest.cs | 14 +-
.../MessageDeobfuscation.Test.csproj | 2 +-
.../MessageDeobfuscationTest.cs | 24 +-
.../MethodOverloading.Test.csproj | 2 +-
.../MethodOverloadingTest.cs | 8 +-
Tests/MethodOverloading/Program.cs | 12 +-
.../ReferenceProxy.Test.csproj | 2 +-
.../SignatureMismatch2.Test.csproj | 2 +-
.../SignatureMismatch2Test.cs | 4 +-
Tests/SignatureMismatch2/Program.cs | 6 +-
Tests/SignatureMismatch2Helper/External.cs | 3 +-
.../TypeScrambler.Test.csproj | 2 +-
Tests/TypeScrambler.Test/TypeScramblerTest.cs | 6 +-
.../RenamingTest.cs | 4 +-
.../VisualBasicRenamingResx.Test.csproj | 2 +-
.../WinFormsRenaming.Test.csproj | 2 +-
.../WpfRenaming.Test/WpfRenaming.Test.csproj | 2 +-
Tests/coverlet.runsettings | 17 +
appveyor.yml | 91 --
global.json | 7 +
scripts/clean-build-artifacts.sh | 39 +
411 files changed, 4324 insertions(+), 1494 deletions(-)
create mode 100644 .github/workflows/format.yml
create mode 100644 .github/workflows/test.yml
create mode 100644 Tests/Confuser.CLI.Test/CliEndToEndTest.cs
create mode 100644 Tests/Confuser.CLI.Test/Confuser.CLI.Test.csproj
create mode 100644 Tests/Confuser.CLI.Test/Fixtures/SampleApp.crproj
create mode 100644 Tests/Confuser.CLI.Test/Fixtures/SampleApp/Program.cs
create mode 100644 Tests/Confuser.CLI.Test/Fixtures/SampleApp/SampleApp.csproj
create mode 100644 Tests/Confuser.GUI.Test/Confuser.GUI.Test.csproj
create mode 100644 Tests/Confuser.GUI.Test/GuiSmokeTest.cs
create mode 100644 Tests/CrossFramework.Console.Net10/CrossFramework.Console.Net10.csproj
create mode 100644 Tests/CrossFramework.Console.Net10/Program.cs
create mode 100644 Tests/CrossFramework.Console.Net20/CrossFramework.Console.Net20.csproj
create mode 100644 Tests/CrossFramework.Console.Net20/Program.cs
create mode 100644 Tests/CrossFramework.Console.Net35/CrossFramework.Console.Net35.csproj
create mode 100644 Tests/CrossFramework.Console.Net35/Program.cs
create mode 100644 Tests/CrossFramework.Console.Net40/CrossFramework.Console.Net40.csproj
create mode 100644 Tests/CrossFramework.Console.Net40/Program.cs
create mode 100644 Tests/CrossFramework.Console.Net48/CrossFramework.Console.Net48.csproj
create mode 100644 Tests/CrossFramework.Console.Net48/Program.cs
create mode 100644 Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj
create mode 100644 Tests/CrossFramework.Console.Net6/Program.cs
create mode 100644 Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj
create mode 100644 Tests/CrossFramework.Console.Net8/Program.cs
create mode 100644 Tests/CrossFramework.Library.Net10/CrossFramework.Library.Net10.csproj
create mode 100644 Tests/CrossFramework.Library.Net10/SampleService.cs
create mode 100644 Tests/CrossFramework.Library.Net48/CrossFramework.Library.Net48.csproj
create mode 100644 Tests/CrossFramework.Library.Net48/SampleService.cs
create mode 100644 Tests/CrossFramework.Library.Net6/CrossFramework.Library.Net6.csproj
create mode 100644 Tests/CrossFramework.Library.Net6/SampleService.cs
create mode 100644 Tests/CrossFramework.Library.Net8/CrossFramework.Library.Net8.csproj
create mode 100644 Tests/CrossFramework.Library.Net8/SampleService.cs
create mode 100644 Tests/CrossFramework.Library.NetStd20/CrossFramework.Library.NetStd20.csproj
create mode 100644 Tests/CrossFramework.Library.NetStd20/SampleService.cs
create mode 100644 Tests/CrossFramework.Test/ConsoleFrameworkTest.cs
create mode 100644 Tests/CrossFramework.Test/CrossFramework.Test.csproj
create mode 100644 Tests/CrossFramework.Test/LibraryFrameworkTest.cs
create mode 100644 Tests/CrossFramework.Test/WinFormsFrameworkTest.cs
create mode 100644 Tests/CrossFramework.Test/WpfFrameworkTest.cs
create mode 100644 Tests/CrossFramework.WPF.Net10/App.xaml
create mode 100644 Tests/CrossFramework.WPF.Net10/App.xaml.cs
create mode 100644 Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj
create mode 100644 Tests/CrossFramework.WPF.Net10/MainWindow.xaml
create mode 100644 Tests/CrossFramework.WPF.Net10/MainWindow.xaml.cs
create mode 100644 Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj
create mode 100644 Tests/CrossFramework.WPF.Net35/Program.cs
create mode 100644 Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj
create mode 100644 Tests/CrossFramework.WPF.Net40/Program.cs
create mode 100644 Tests/CrossFramework.WPF.Net48/App.xaml
create mode 100644 Tests/CrossFramework.WPF.Net48/App.xaml.cs
create mode 100644 Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj
create mode 100644 Tests/CrossFramework.WPF.Net48/MainWindow.xaml
create mode 100644 Tests/CrossFramework.WPF.Net48/MainWindow.xaml.cs
create mode 100644 Tests/CrossFramework.WPF.Net6/App.xaml
create mode 100644 Tests/CrossFramework.WPF.Net6/App.xaml.cs
create mode 100644 Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj
create mode 100644 Tests/CrossFramework.WPF.Net6/MainWindow.xaml
create mode 100644 Tests/CrossFramework.WPF.Net6/MainWindow.xaml.cs
create mode 100644 Tests/CrossFramework.WPF.Net8/App.xaml
create mode 100644 Tests/CrossFramework.WPF.Net8/App.xaml.cs
create mode 100644 Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj
create mode 100644 Tests/CrossFramework.WPF.Net8/MainWindow.xaml
create mode 100644 Tests/CrossFramework.WPF.Net8/MainWindow.xaml.cs
create mode 100644 Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj
create mode 100644 Tests/CrossFramework.WinForms.Net10/MainForm.cs
create mode 100644 Tests/CrossFramework.WinForms.Net10/Program.cs
create mode 100644 Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj
create mode 100644 Tests/CrossFramework.WinForms.Net35/MainForm.cs
create mode 100644 Tests/CrossFramework.WinForms.Net35/Program.cs
create mode 100644 Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj
create mode 100644 Tests/CrossFramework.WinForms.Net40/MainForm.cs
create mode 100644 Tests/CrossFramework.WinForms.Net40/Program.cs
create mode 100644 Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj
create mode 100644 Tests/CrossFramework.WinForms.Net48/MainForm.cs
create mode 100644 Tests/CrossFramework.WinForms.Net48/Program.cs
create mode 100644 Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj
create mode 100644 Tests/CrossFramework.WinForms.Net6/MainForm.cs
create mode 100644 Tests/CrossFramework.WinForms.Net6/Program.cs
create mode 100644 Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj
create mode 100644 Tests/CrossFramework.WinForms.Net8/MainForm.cs
create mode 100644 Tests/CrossFramework.WinForms.Net8/Program.cs
create mode 100644 Tests/Directory.Build.props
create mode 100644 Tests/coverlet.runsettings
delete mode 100644 appveyor.yml
create mode 100644 global.json
create mode 100644 scripts/clean-build-artifacts.sh
diff --git a/.gitattributes b/.gitattributes
index b8bc1f360..45cf1e579 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,45 @@
-*.cs diff=csharp
+# Set default behavior, in case users don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files we want to always be normalized and converted
+# to native line endings on checkout.
+*.config text
+*.cs text diff=csharp
+*.manifest text
+*.md text
+*.resx text
+*.txt text
+*.xml text
+*.xaml text
+*.yml text
+*.yaml text
+*.json text
+*.props text
+*.targets text
+
+# Declare files that will always have CRLF line endings on checkout.
+*.csproj text eol=crlf
+*.dbproj text eol=crlf
+*.fsproj text eol=crlf
+*.inf text eol=crlf
+*.lsproj text eol=crlf
+*.modelproj text eol=crlf
+*.sln text eol=crlf
+*.sqlproj text eol=crlf
+*.vbproj text eol=crlf
+*.vcproj text eol=crlf
+*.vcxproj text eol=crlf
+*.wixproj text eol=crlf
+*.wmaproj text eol=crlf
+
+# Denote all files that are truly binary and should not be modified.
+*.apk binary
+*.dll binary
+*.exe binary
+*.ico binary
+*.jpeg binary
+*.jpg binary
+*.otf binary
+*.pdf binary
+*.png binary
+*.snk binary
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 50713d395..70c05580e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,8 +8,10 @@ on:
jobs:
build:
- runs-on: windows-2022
- timeout-minutes: 15
+ runs-on: windows-2025
+ timeout-minutes: 10
+ env:
+ NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- uses: actions/checkout@v5
with:
@@ -18,14 +20,13 @@ jobs:
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- - name: Install .NET 4.6.1 targeting pack
- shell: pwsh
- run: |
- $url = 'https://download.microsoft.com/download/F/1/D/F1DEB8DB-D277-4EF9-9F48-3A65D4D8F965/NDP461-DevPack-KB3105179-ENU.exe'
- $installer = "$env:TEMP\ndp461-devpack.exe"
- Invoke-WebRequest -Uri $url -OutFile $installer -UseBasicParsing
- Start-Process -FilePath $installer -ArgumentList '/quiet','/norestart' -Wait
- Write-Host "Installed .NET 4.6.1 Developer Pack"
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ${{ github.workspace }}/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.vcxproj') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
- name: Install nbgv
run: dotnet tool install -g nbgv
@@ -47,7 +48,7 @@ jobs:
- name: Package CLI
shell: pwsh
run: |
- $src = 'Confuser.CLI/bin/Release/net461'
+ $src = 'Confuser.CLI/bin/Release/net10.0'
$zip = 'ConfuserEx-CLI.zip'
Get-ChildItem $src -Exclude '*.pdb','*.xml' | Compress-Archive -DestinationPath $zip
Write-Host "Created $zip ($([math]::Round((Get-Item $zip).Length / 1MB, 1)) MB)"
@@ -55,7 +56,7 @@ jobs:
- name: Package GUI
shell: pwsh
run: |
- $src = 'ConfuserEx/bin/Release/net461'
+ $src = 'ConfuserEx/bin/Release/net10.0-windows'
$zip = 'ConfuserEx-GUI.zip'
Get-ChildItem $src -Exclude '*.pdb','*.xml' | Compress-Archive -DestinationPath $zip
Write-Host "Created $zip ($([math]::Round((Get-Item $zip).Length / 1MB, 1)) MB)"
@@ -66,8 +67,8 @@ jobs:
$zip = 'ConfuserEx.zip'
$tmp = 'combined'
New-Item -ItemType Directory -Path $tmp -Force | Out-Null
- Copy-Item 'Confuser.CLI/bin/Release/net461/*' $tmp -Exclude '*.pdb','*.xml' -Recurse
- Copy-Item 'ConfuserEx/bin/Release/net461/*' $tmp -Exclude '*.pdb','*.xml' -Recurse -Force
+ Copy-Item 'Confuser.CLI/bin/Release/net10.0/*' $tmp -Exclude '*.pdb','*.xml' -Recurse
+ Copy-Item 'ConfuserEx/bin/Release/net10.0-windows/*' $tmp -Exclude '*.pdb','*.xml' -Recurse -Force
Get-ChildItem $tmp | Compress-Archive -DestinationPath $zip
Remove-Item $tmp -Recurse -Force
Write-Host "Created $zip ($([math]::Round((Get-Item $zip).Length / 1MB, 1)) MB)"
@@ -86,7 +87,7 @@ jobs:
pre-release:
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/pre-release'
- runs-on: windows-2022
+ runs-on: windows-2025
timeout-minutes: 5
permissions:
contents: write
@@ -136,7 +137,7 @@ jobs:
release:
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
- runs-on: windows-2022
+ runs-on: windows-2025
timeout-minutes: 5
permissions:
contents: write
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
new file mode 100644
index 000000000..a09a13123
--- /dev/null
+++ b/.github/workflows/format.yml
@@ -0,0 +1,34 @@
+name: format
+
+on:
+ pull_request:
+ branches: [master, pre-release]
+
+jobs:
+ check-format:
+ runs-on: windows-2025
+ timeout-minutes: 5
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Check formatting
+ run: dotnet format Confuser2.sln --verify-no-changes --verbosity diagnostic
+ continue-on-error: true
+ id: format-check
+
+ - name: Format diff
+ if: steps.format-check.outcome == 'failure'
+ shell: pwsh
+ run: |
+ dotnet format Confuser2.sln
+ git diff --stat
+ $diff = git diff --name-only
+ if ($diff) {
+ Write-Host "::warning::The following files need formatting:" -ForegroundColor Yellow
+ $diff | ForEach-Object { Write-Host " $_" }
+ Write-Host ""
+ Write-Host "Run 'dotnet format Confuser2.sln' locally and commit the changes."
+ exit 1
+ }
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..f0a28a0be
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,240 @@
+name: test
+
+on:
+ push:
+ branches: [master, pre-release, feature/**, fix/**]
+ pull_request:
+ branches: [master, pre-release]
+
+jobs:
+ test:
+ runs-on: windows-2025
+ timeout-minutes: 20
+ permissions:
+ contents: read
+ pull-requests: write
+ env:
+ NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+
+ - name: Setup .NET runtimes for cross-framework tests
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 6.0.x
+ 8.0.x
+
+ - name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v2
+
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ${{ github.workspace }}/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.vcxproj') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ - name: Restore
+ run: msbuild Confuser2.sln -t:Restore -verbosity:minimal
+
+ - name: Build
+ run: msbuild Confuser2.sln -p:Configuration=Release -verbosity:minimal
+
+ - name: Run tests with coverage
+ id: tests
+ shell: pwsh
+ run: |
+ $resultsDir = 'test-results'
+ New-Item -ItemType Directory -Path $resultsDir -Force | Out-Null
+
+ $testProjects = Get-ChildItem -Path Tests -Filter '*.Test.csproj' -Recurse | Select-Object -Unique
+ $anyFailed = $false
+
+ foreach ($proj in $testProjects) {
+ $name = $proj.BaseName
+ Write-Host "`n========================================" -ForegroundColor Cyan
+ Write-Host "Testing $name" -ForegroundColor Cyan
+ Write-Host "========================================" -ForegroundColor Cyan
+
+ dotnet test $proj.FullName -c Release --no-build --verbosity minimal `
+ --collect:"XPlat Code Coverage" `
+ --logger "trx;LogFileName=$name.trx" `
+ --results-directory "$resultsDir/$name"
+
+ if ($LASTEXITCODE -ne 0) { $anyFailed = $true }
+ }
+
+ # Parse all TRX files into a combined report
+ $allTests = @()
+ $trxFiles = Get-ChildItem -Path $resultsDir -Filter '*.trx' -Recurse
+
+ foreach ($trx in $trxFiles) {
+ [xml]$xml = Get-Content $trx.FullName
+ $ns = @{ t = 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010' }
+
+ $results = Select-Xml -Xml $xml -XPath '//t:UnitTestResult' -Namespace $ns
+ foreach ($r in $results) {
+ $node = $r.Node
+ $testName = $node.testName
+ $outcome = $node.outcome # Passed, Failed, NotExecuted
+ $duration = $node.duration
+ $errorMsg = ''
+ if ($node.Output -and $node.Output.ErrorInfo) {
+ $errorMsg = $node.Output.ErrorInfo.Message
+ }
+ $allTests += [PSCustomObject]@{
+ Name = $testName
+ Outcome = $outcome
+ Duration = $duration
+ Error = $errorMsg
+ TrxFile = $trx.BaseName
+ }
+ }
+ }
+
+ # Group by category for nice display
+ $passed = $allTests | Where-Object { $_.Outcome -eq 'Passed' }
+ $failed = $allTests | Where-Object { $_.Outcome -eq 'Failed' }
+ $skipped = $allTests | Where-Object { $_.Outcome -eq 'NotExecuted' }
+
+ # Build markdown report
+ $md = @()
+ $md += '## Test Results'
+ $md += ''
+ if ($failed.Count -gt 0) {
+ $md += "> :x: **$($failed.Count) test(s) failed** out of $($allTests.Count) total"
+ } else {
+ $md += "> :white_check_mark: **All $($passed.Count) tests passed**"
+ }
+ $md += ''
+
+ # Summary table grouped by project (TRX file)
+ $grouped = $allTests | Group-Object TrxFile
+ $md += '| Project | :white_check_mark: Passed | :x: Failed | :fast_forward: Skipped | Total |'
+ $md += '|---------|--------|--------|---------|-------|'
+ foreach ($g in $grouped | Sort-Object Name) {
+ $p = ($g.Group | Where-Object { $_.Outcome -eq 'Passed' }).Count
+ $f = ($g.Group | Where-Object { $_.Outcome -eq 'Failed' }).Count
+ $s = ($g.Group | Where-Object { $_.Outcome -eq 'NotExecuted' }).Count
+ $icon = if ($f -gt 0) { ':x:' } else { ':white_check_mark:' }
+ $md += "| $icon $($g.Name) | $p | $f | $s | $($g.Group.Count) |"
+ }
+
+ # Expand CrossFramework tests individually
+ $crossTests = $allTests | Where-Object { $_.Name -match 'CrossFramework|Console_Net|WinForms_Net|WPF_Net|Library_Net' }
+ if ($crossTests.Count -gt 0) {
+ $md += ''
+ $md += 'Cross-Framework Test Details (click to expand)
'
+ $md += ''
+ $md += '| Test | Framework | App Type | Result |'
+ $md += '|------|-----------|----------|--------|'
+ foreach ($t in $crossTests | Sort-Object Name) {
+ $icon = switch ($t.Outcome) { 'Passed' { ':white_check_mark:' } 'Failed' { ':x:' } default { ':fast_forward:' } }
+ # Parse TFM and app type from test name
+ $tfm = ''; $appType = ''
+ if ($t.Name -match '_Net(\w+)_') { $tfm = $Matches[1] }
+ if ($t.Name -match '(Console|WinForms|WPF|Library)_') { $appType = $Matches[1] }
+ $md += "| ``$($t.Name)`` | $tfm | $appType | $icon |"
+ }
+ $md += ''
+ $md += ' '
+ }
+
+ # List failed tests with error messages
+ if ($failed.Count -gt 0) {
+ $md += ''
+ $md += '### :x: Failed Tests'
+ $md += ''
+ foreach ($f in $failed) {
+ $md += "**``$($f.Name)``**"
+ if ($f.Error) {
+ $shortError = ($f.Error -split "`n")[0]
+ if ($shortError.Length -gt 200) { $shortError = $shortError.Substring(0, 200) + '...' }
+ $md += "> $shortError"
+ }
+ $md += ''
+ }
+ }
+
+ $md += ''
+
+ # Write results
+ $md -join "`n" | Set-Content -Path 'test-results.md' -Encoding utf8
+ $md -join "`n" >> $env:GITHUB_STEP_SUMMARY
+
+ echo "any_failed=$anyFailed" >> $env:GITHUB_OUTPUT
+ echo "total_passed=$($passed.Count)" >> $env:GITHUB_OUTPUT
+ echo "total_failed=$($failed.Count)" >> $env:GITHUB_OUTPUT
+
+ - name: Generate coverage report
+ if: always()
+ shell: pwsh
+ run: |
+ dotnet tool install -g dotnet-reportgenerator-globaltool
+ $reports = (Get-ChildItem -Path test-results -Filter 'coverage.cobertura.xml' -Recurse).FullName -join ';'
+ if ($reports) {
+ reportgenerator "-reports:$reports" "-targetdir:coverage/report" "-reporttypes:HtmlInline_AzurePipelines;Cobertura;TextSummary;MarkdownSummaryGithub"
+ Write-Host "--- Coverage Summary ---"
+ Get-Content coverage/report/Summary.txt
+ } else {
+ Write-Host "No coverage files found."
+ }
+
+ - name: Build PR comment
+ if: always() && github.event_name == 'pull_request'
+ shell: pwsh
+ run: |
+ $comment = @()
+ if (Test-Path 'test-results.md') {
+ $comment += Get-Content 'test-results.md' -Raw
+ }
+ $coverageMd = 'coverage/report/SummaryGithub.md'
+ if (Test-Path $coverageMd) {
+ $comment += "`n---`n"
+ $comment += Get-Content $coverageMd -Raw
+ }
+ $comment -join "`n" | Set-Content -Path 'pr-comment.md' -Encoding utf8
+
+ - name: Coverage to job summary
+ if: always()
+ shell: pwsh
+ run: |
+ $mdFile = 'coverage/report/SummaryGithub.md'
+ if (Test-Path $mdFile) {
+ "`n---`n" >> $env:GITHUB_STEP_SUMMARY
+ Get-Content $mdFile >> $env:GITHUB_STEP_SUMMARY
+ }
+
+ - name: Post PR comment
+ if: always() && github.event_name == 'pull_request'
+ uses: marocchino/sticky-pull-request-comment@v2
+ with:
+ header: test-coverage-report
+ path: pr-comment.md
+
+ - name: Upload test results
+ if: always()
+ uses: actions/upload-artifact@v5
+ with:
+ name: test-results
+ path: test-results/
+ if-no-files-found: ignore
+
+ - name: Upload coverage report
+ if: always()
+ uses: actions/upload-artifact@v5
+ with:
+ name: coverage-report
+ path: coverage/report/
+ if-no-files-found: ignore
+
+ - name: Fail if tests failed
+ if: always() && steps.tests.outputs.any_failed == 'True'
+ run: |
+ Write-Host "::error::Tests failed. See the test results above for details."
+ exit 1
+ shell: pwsh
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a26c85519..42a023fc3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,23 +1,136 @@
-# Contributing
+# Contributing to ConfuserExx
-Contributions of any kind are in general always welcome.
+Contributions of any kind are welcome. For bugfixes and unit tests, you can submit a PR directly. For larger changes, please open an issue first to discuss the approach.
-When contributing to this repository, please first discuss the changes you wish
-to make via issue, email, discussions, or any other method with the owners of
-this repository before making a change.
+## Getting Started
-For contributions that contain only bugfixes or added unit tests, this
-discussion is not required before hand.
+1. Fork the repository
+2. Create a feature branch from `pre-release`: `git checkout -b feat/my-feature pre-release`
+3. Make your changes and ensure CI passes
+4. Open a PR targeting `pre-release`
-Please note we have a code of conduct, please follow it in all your
-interactions with the project.
+See [README.md](README.md#building-from-source) for build prerequisites.
+
+## Testing Policy
+
+Every PR must maintain or improve test coverage. We use a **ratchet strategy** — coverage only goes up, never down.
+
+### Coverage Targets
+
+| Assembly | Current Goal | Long-term Goal |
+|----------|-------------|----------------|
+| Confuser.Core | 40% | 70% |
+| Confuser.CLI | 50% | 70% |
+| Confuser.Protections | 30% | 60% |
+| Confuser.Renamer | 30% | 60% |
+| Confuser.DynCipher | 20% | 50% |
+
+These targets will be raised as coverage improves. CI reports coverage on every PR — check the comment.
+
+### What Must Be Tested
+
+**Always test:**
+- New protection implementations (integration test: obfuscate + run + verify)
+- Bug fixes (regression test proving the fix works)
+- Assembly resolution and path handling logic
+- CLI argument parsing and error handling
+- Project file (`.crproj`) parsing edge cases
+
+**Don't need tests:**
+- Simple property getters/setters
+- WPF UI layout or styling changes
+- Third-party library behavior (dnlib, CommunityToolkit)
+
+### Test Types
+
+| Type | Location | Framework | Purpose |
+|------|----------|-----------|---------|
+| Unit tests | `Tests/Confuser.Core.Test/` | xunit + Moq | Test individual classes in isolation |
+| Unit tests | `Tests/Confuser.Renamer.Test/` | xunit + Moq | Test renaming logic |
+| CLI e2e | `Tests/Confuser.CLI.Test/` | xunit | End-to-end CLI obfuscation |
+| GUI smoke | `Tests/Confuser.GUI.Test/` | xunit + FlaUI | WPF UI automation |
+| Integration | `Tests/*_*.Test/` | xunit | Obfuscate sample app, run it, verify output |
+
+### Writing a New Integration Test
+
+Each integration test has two projects:
+- **Subject** (`Tests/MyFeature/`): Small .NET Framework console app that prints `START`, some output, `END`, and returns exit code `42`
+- **Test** (`Tests/MyFeature.Test/`): References `Confuser.UnitTest` and the subject, calls `Run()` with the desired protections
+
+```csharp
+public class MyFeatureTest : TestBase {
+ public MyFeatureTest(ITestOutputHelper outputHelper) : base(outputHelper) { }
+
+ [Fact]
+ public Task MyProtection_SampleApp_RunsCorrectly() =>
+ Run("MyFeature.exe",
+ new[] { "expected output line" },
+ new SettingItem("my-protection-id"));
+}
+```
+
+### Writing a Unit Test
+
+```csharp
+[Fact]
+public void MethodName_Condition_ExpectedResult() {
+ // Arrange
+ var sut = new MyService();
+
+ // Act
+ var result = sut.DoSomething(input);
+
+ // Assert
+ Assert.Equal(expected, result);
+}
+```
+
+Test names follow `MethodName_Condition_ExpectedResult` convention.
+
+### Running Tests Locally
+
+```bash
+# All tests
+dotnet test Confuser2.sln -c Release
+
+# Specific test project
+dotnet test Tests/Confuser.CLI.Test/Confuser.CLI.Test.csproj -c Release
+
+# With coverage
+dotnet test Tests/Confuser.CLI.Test/Confuser.CLI.Test.csproj -c Release --collect:"XPlat Code Coverage"
+```
+
+### Coverage Reporting
+
+Every PR receives an automatic coverage comment showing per-assembly line and branch coverage. The full HTML drill-down report is downloadable as the `coverage-report` artifact from the test workflow.
+
+## Commit Conventions
+
+We use [Conventional Commits](https://www.conventionalcommits.org/):
+
+```
+feat(scope): add new feature
+fix(scope): fix a bug
+test(scope): add or update tests
+refactor(scope): code change that doesn't fix a bug or add a feature
+chore(scope): build, CI, dependency updates
+docs(scope): documentation changes
+```
+
+Always reference the issue number: `fix(renamer): handle FnPtr types (#6)`
## Pull Request Process
-1. Ensure that only the files that are part of your change are actually part of
- the pull request. Changes in unrelated files, unrelated additional files or
- binaries have to be removed.
-2. In case the pull request is created in response to a specific issue, please
- reference the issue in the description of the pull request.
-3. Pull-Requests are merged once they are signed-off by one other developer
- with the permission to merge changes into the repository.
+1. PRs target `pre-release`, not `master`
+2. CI must pass (build + tests + coverage)
+3. Coverage must not decrease
+4. Only include files that are part of your change — no unrelated modifications
+5. Reference the issue in the PR description
+6. If fixing a community-reported issue, tag the reporter to test
+7. Push fixes to the **same branch** — never close and create a replacement PR
+
+## Code Quality
+
+- Roslyn analyzers (NetAnalyzers + Roslynator) run during build — resolve all warnings
+- No `ResolveThrow` calls in new code — use null-safe `Resolve` + handle null
+- Follow existing code style (tabs, braces on same line, etc.)
diff --git a/Confuser.CLI/Confuser.CLI.csproj b/Confuser.CLI/Confuser.CLI.csproj
index 82743f158..d087b4871 100644
--- a/Confuser.CLI/Confuser.CLI.csproj
+++ b/Confuser.CLI/Confuser.CLI.csproj
@@ -4,7 +4,7 @@
Exe
- net461
+ net10.0
true
..\ConfuserEx.snk
@@ -24,6 +24,14 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Confuser.CLI/Program.cs b/Confuser.CLI/Program.cs
index de11d40a2..d0fc00ce6 100644
--- a/Confuser.CLI/Program.cs
+++ b/Confuser.CLI/Program.cs
@@ -13,8 +13,11 @@ internal class Program {
static int Main(string[] args) {
ConsoleColor original = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.White;
- string originalTitle = Console.Title;
- Console.Title = "ConfuserEx";
+ string originalTitle = null;
+ if (OperatingSystem.IsWindows()) {
+ originalTitle = Console.Title;
+ Console.Title = "ConfuserEx";
+ }
try {
bool noPause = false;
bool debug = false;
@@ -138,7 +141,8 @@ static int Main(string[] args) {
}
finally {
Console.ForegroundColor = original;
- Console.Title = originalTitle;
+ if (OperatingSystem.IsWindows() && originalTitle != null)
+ Console.Title = originalTitle;
}
}
diff --git a/Confuser.Core/API/APIStore.cs b/Confuser.Core/API/APIStore.cs
index b024aebf2..9cd06ef26 100644
--- a/Confuser.Core/API/APIStore.cs
+++ b/Confuser.Core/API/APIStore.cs
@@ -50,8 +50,8 @@ public IOpaquePredicateDescriptor GetPredicate(MethodDef method, OpaquePredicate
random.Shuffle(randomPredicates);
foreach (var predicate in randomPredicates) {
if (predicate.IsUsable(method) &&
- (type == null || predicate.Type == type.Value) &&
- (argCount == null || Array.IndexOf(argCount, predicate.ArgumentCount) != -1))
+ (type == null || predicate.Type == type.Value) &&
+ (argCount == null || Array.IndexOf(argCount, predicate.ArgumentCount) != -1))
return predicate;
}
return null;
@@ -95,4 +95,4 @@ public interface IAPIStore {
/// The suitable opaque predicate if found, or null if not found.
IOpaquePredicateDescriptor GetPredicate(MethodDef method, OpaquePredicateType? type, params int[] argCount);
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/API/IDataStore.cs b/Confuser.Core/API/IDataStore.cs
index e8aa12f3a..c96e70889 100644
--- a/Confuser.Core/API/IDataStore.cs
+++ b/Confuser.Core/API/IDataStore.cs
@@ -50,4 +50,4 @@ public interface IDataStoreAccessor {
/// An instruction sequence that returns the stored data.
Instruction[] Emit();
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/API/IOpaquePredicate.cs b/Confuser.Core/API/IOpaquePredicate.cs
index a1eef14b5..9f3b44129 100644
--- a/Confuser.Core/API/IOpaquePredicate.cs
+++ b/Confuser.Core/API/IOpaquePredicate.cs
@@ -76,4 +76,4 @@ public enum OpaquePredicateType {
///
Invariant
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/Annotations.cs b/Confuser.Core/Annotations.cs
index 5e2056d30..9b778b98f 100644
--- a/Confuser.Core/Annotations.cs
+++ b/Confuser.Core/Annotations.cs
@@ -189,4 +189,4 @@ public WeakReferenceKey(object target)
public int HashCode { get; private set; }
}
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/Confuser.Core.csproj b/Confuser.Core/Confuser.Core.csproj
index 0d7acf713..de95bce88 100644
--- a/Confuser.Core/Confuser.Core.csproj
+++ b/Confuser.Core/Confuser.Core.csproj
@@ -4,7 +4,7 @@
- net461;netstandard2.0
+ net48;netstandard2.0
true
..\ConfuserEx.snk
@@ -18,7 +18,6 @@
-
diff --git a/Confuser.Core/ConfuserAssemblyResolver.cs b/Confuser.Core/ConfuserAssemblyResolver.cs
index 0b6a39da3..500d3f92e 100644
--- a/Confuser.Core/ConfuserAssemblyResolver.cs
+++ b/Confuser.Core/ConfuserAssemblyResolver.cs
@@ -84,7 +84,7 @@ public void Clear() {
InternalFuzzyResolver.Clear();
}
- public IEnumerable GetCachedAssemblies() =>
+ public IEnumerable GetCachedAssemblies() =>
InternalExactResolver.GetCachedAssemblies().Concat(InternalFuzzyResolver.GetCachedAssemblies());
public void AddToCache(ModuleDefMD modDef) {
@@ -105,13 +105,13 @@ private sealed class TeeList : IList {
///
public void Add(string item) {
- foreach (var list in _lists)
+ foreach (var list in _lists)
list.Add(item);
}
///
public void Clear() {
- foreach (var list in _lists)
+ foreach (var list in _lists)
list.Clear();
}
@@ -136,13 +136,13 @@ public bool Remove(string item) =>
///
public void Insert(int index, string item) {
- foreach (var list in _lists)
+ foreach (var list in _lists)
list.Insert(index, item);
}
///
public void RemoveAt(int index) {
- foreach (var list in _lists)
+ foreach (var list in _lists)
list.RemoveAt(index);
}
diff --git a/Confuser.Core/ConfuserComponent.cs b/Confuser.Core/ConfuserComponent.cs
index 105211392..7d554c6f5 100644
--- a/Confuser.Core/ConfuserComponent.cs
+++ b/Confuser.Core/ConfuserComponent.cs
@@ -41,4 +41,4 @@ public abstract class ConfuserComponent {
/// The processing pipeline.
protected internal abstract void PopulatePipeline(ProtectionPipeline pipeline);
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/ConfuserContext.cs b/Confuser.Core/ConfuserContext.cs
index 94092bec1..a8c7a1c76 100644
--- a/Confuser.Core/ConfuserContext.cs
+++ b/Confuser.Core/ConfuserContext.cs
@@ -166,7 +166,7 @@ public NativeModuleWriterOptions RequestNative(bool optimizeImageSize) {
return null;
if (CurrentModuleWriterOptions == null)
CurrentModuleWriterOptions = new NativeModuleWriterOptions(CurrentModule, optimizeImageSize);
-
+
// Clone the current options to the new options
var newOptions = new NativeModuleWriterOptions(CurrentModule, optimizeImageSize) {
AddCheckSum = CurrentModuleWriterOptions.AddCheckSum,
diff --git a/Confuser.Core/ConfuserEngine.cs b/Confuser.Core/ConfuserEngine.cs
index 2b90fb315..a4e737cce 100644
--- a/Confuser.Core/ConfuserEngine.cs
+++ b/Confuser.Core/ConfuserEngine.cs
@@ -11,11 +11,11 @@
using dnlib.DotNet.Emit;
using dnlib.DotNet.Writer;
using Microsoft.Win32;
-using InformationalAttribute = System.Reflection.AssemblyInformationalVersionAttribute;
-using ProductAttribute = System.Reflection.AssemblyProductAttribute;
using CopyrightAttribute = System.Reflection.AssemblyCopyrightAttribute;
+using InformationalAttribute = System.Reflection.AssemblyInformationalVersionAttribute;
using MethodAttributes = dnlib.DotNet.MethodAttributes;
using MethodImplAttributes = dnlib.DotNet.MethodImplAttributes;
+using ProductAttribute = System.Reflection.AssemblyProductAttribute;
using TypeAttributes = dnlib.DotNet.TypeAttributes;
namespace Confuser.Core {
@@ -93,7 +93,7 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token)
new SettingItem(WatermarkingProtection._Id)
});
- var asmResolver = new ConfuserAssemblyResolver {EnableTypeDefCache = true};
+ var asmResolver = new ConfuserAssemblyResolver { EnableTypeDefCache = true };
asmResolver.DefaultModuleContext = new ModuleContext(asmResolver);
context.InternalResolver = asmResolver;
context.BaseDirectory = Path.GetFullPath(context.Project.BaseDirectory).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
@@ -320,7 +320,7 @@ static void CheckStrongName(ConfuserContext context, ModuleDef module) {
else if (isKeyProvided && !moduleIsSignedOrDelayedSigned)
context.Logger.WarnFormat("[{0}] SN Key or SN public Key is provided for an unsigned module, the output may not be working.", module.Name);
else if (snPubKeyBytes != null && moduleIsSignedOrDelayedSigned &&
- !module.Assembly.PublicKey.Data.SequenceEqual(snPubKeyBytes))
+ !module.Assembly.PublicKey.Data.SequenceEqual(snPubKeyBytes))
context.Logger.WarnFormat("[{0}] Provided SN public Key and signed module's public key do not match, the output may not be working.",
module.Name);
}
@@ -375,7 +375,7 @@ static void BeginModule(ConfuserContext context) {
}
}
- static void ProcessModule(ConfuserContext context) =>
+ static void ProcessModule(ConfuserContext context) =>
context.CurrentModuleWriterOptions.WriterEvent += (sender, e) => context.CheckCancellation();
static void OptimizeMethods(ConfuserContext context) {
@@ -396,7 +396,8 @@ static void EndModule(ConfuserContext context) {
context.Logger.WarnFormat("Input file is not inside the base directory. Relative path can't be created. Placing file into output root." +
Environment.NewLine + "Responsible file is: {0}", output);
output = Path.GetFileName(output);
- } else {
+ }
+ else {
output = relativeOutput;
}
}
diff --git a/Confuser.Core/ConfuserParameters.cs b/Confuser.Core/ConfuserParameters.cs
index ad30f8bab..f3e05a2c1 100644
--- a/Confuser.Core/ConfuserParameters.cs
+++ b/Confuser.Core/ConfuserParameters.cs
@@ -56,4 +56,4 @@ internal Marker GetMarker() {
return Marker ?? new ObfAttrMarker();
}
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/CoreComponent.cs b/Confuser.Core/CoreComponent.cs
index 7ae6d7a82..31ca4b15e 100644
--- a/Confuser.Core/CoreComponent.cs
+++ b/Confuser.Core/CoreComponent.cs
@@ -42,11 +42,9 @@ public class CoreComponent : ConfuserComponent {
readonly Marker marker;
readonly ConfuserContext _context;
-
///
/// Initializes a new instance of the class.
///
- /// The parameters.
/// The marker.
internal CoreComponent(ConfuserContext context, Marker marker) {
_context = context;
diff --git a/Confuser.Core/DependencyResolver.cs b/Confuser.Core/DependencyResolver.cs
index c7c694525..8c0bca6a3 100644
--- a/Confuser.Core/DependencyResolver.cs
+++ b/Confuser.Core/DependencyResolver.cs
@@ -146,4 +146,4 @@ internal CircularDependencyException(Protection a, Protection b)
///
public Protection ProtectionB { get; private set; }
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/DnlibUtils.cs b/Confuser.Core/DnlibUtils.cs
index 7570da946..440b1af97 100644
--- a/Confuser.Core/DnlibUtils.cs
+++ b/Confuser.Core/DnlibUtils.cs
@@ -544,7 +544,7 @@ public static bool IsEntryPoint(this TypeDef typeDef) {
return typeDef == typeDef.Module.EntryPoint?.DeclaringType;
}
-
+
///
/// Merges a specified call instruction into the body.
///
diff --git a/Confuser.Core/Helpers/ControlFlowGraph.cs b/Confuser.Core/Helpers/ControlFlowGraph.cs
index 54127be3b..aeea27dfa 100644
--- a/Confuser.Core/Helpers/ControlFlowGraph.cs
+++ b/Confuser.Core/Helpers/ControlFlowGraph.cs
@@ -91,7 +91,7 @@ void PopulateBlockHeaders(HashSet blockHeaders, HashSet {1} {2}", Id, Type, string.Join(", ", Targets.Select(block => block.Id.ToString()).ToArray()));
}
}
-}
\ No newline at end of file
+}
diff --git a/Confuser.Core/Helpers/InjectHelper.cs b/Confuser.Core/Helpers/InjectHelper.cs
index 6cb066c23..6c5f931fe 100644
--- a/Confuser.Core/Helpers/InjectHelper.cs
+++ b/Confuser.Core/Helpers/InjectHelper.cs
@@ -99,7 +99,7 @@ static void CopyMethodDef(MethodDef methodDef, InjectContext ctx) {
newMethodDef.Signature = ctx.Importer.Import(methodDef.Signature);
newMethodDef.Parameters.UpdateParameterTypes();
-
+
foreach (var paramDef in methodDef.ParamDefs)
newMethodDef.ParamDefs.Add(new ParamDefUser(paramDef.Name, paramDef.Sequence, paramDef.Attributes));
@@ -112,16 +112,14 @@ static void CopyMethodDef(MethodDef methodDef, InjectContext ctx) {
if (methodDef.HasBody)
CopyMethodBody(methodDef, ctx, newMethodDef);
}
-
- static void CopyMethodBody(MethodDef methodDef, InjectContext ctx, MethodDef newMethodDef)
- {
+
+ static void CopyMethodBody(MethodDef methodDef, InjectContext ctx, MethodDef newMethodDef) {
newMethodDef.Body = new CilBody(methodDef.Body.InitLocals, new List(),
- new List(), new List()) {MaxStack = methodDef.Body.MaxStack};
+ new List(), new List()) { MaxStack = methodDef.Body.MaxStack };
var bodyMap = new Dictionary