From bf2e1d340d4147728f3aa2fb7bdf2e8b7c09514e Mon Sep 17 00:00:00 2001 From: Roman Tymoshyk Date: Mon, 4 Aug 2025 16:53:59 +0300 Subject: [PATCH 1/2] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6915812d..127573f2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -16,7 +16,7 @@ permissions: jobs: analyze: name: Analyze - runs-on: windows-2019 + runs-on: windows-latest strategy: fail-fast: false From 978d514da82332f658af3a81c2c823cd3b1659a0 Mon Sep 17 00:00:00 2001 From: Roman Tymoshyk Date: Wed, 6 Aug 2025 20:01:41 +0300 Subject: [PATCH 2/2] dotnet8 (#2) Co-authored-by: kumagai-xstorage --- Confuser.CLI/Confuser.CLI.csproj | 2 +- Confuser.Core/Confuser.Core.csproj | 4 +- Confuser.Core/ConfuserEngine.cs | 1 + Confuser.DynCipher/Confuser.DynCipher.csproj | 2 +- .../Generation/ExpressionGenerator.cs | 3 +- Confuser.Protections/AntiTamper/AntiMode.cs | 1 + Confuser.Protections/AntiTamper/JITBody.cs | 8 +- Confuser.Protections/AntiTamper/JITMode.cs | 1 + Confuser.Protections/AntiTamper/NormalMode.cs | 1 + Confuser.Protections/Compress/Compressor.cs | 707 +++++++++--------- .../Confuser.Protections.csproj | 2 +- Confuser.Protections/Constants/EncodePhase.cs | 1 + Confuser.Protections/Constants/InjectPhase.cs | 3 +- .../Constants/ReferenceReplacer.cs | 1 + Confuser.Protections/Resources/InjectPhase.cs | 3 +- .../Analyzers/TypeBlobAnalyzer.cs | 1 + Confuser.Renamer/BAML/BAMLAnalyzer.cs | 1 + Confuser.Renamer/Confuser.Renamer.csproj | 2 +- Confuser.Renamer/VTable.cs | 1 + Confuser.Runtime/Confuser.Runtime.csproj | 2 +- Confuser2.sln | 4 +- ConfuserEx/ComponentDiscovery.cs | 25 +- ConfuserEx/ConfuserEx.csproj | 17 +- ConfuserEx/FileDragDrop.cs | 27 +- ConfuserEx/ViewModel/Project/ProjectRuleVM.cs | 12 +- ConfuserEx/ViewModel/UI/AboutTabVM.cs | 4 +- ConfuserEx/ViewModel/UI/AppVM.cs | 2 +- ConfuserEx/ViewModel/UI/ProjectTabVM.cs | 2 +- ConfuserEx/ViewModel/UI/ProtectTabVM.cs | 2 +- ConfuserEx/ViewModel/UI/SettingsTabVM.cs | 2 +- ConfuserEx/Views/ProjectRuleView.xaml.cs | 4 +- .../Views/ProjectTabAdvancedView.xaml.cs | 4 +- 32 files changed, 454 insertions(+), 398 deletions(-) diff --git a/Confuser.CLI/Confuser.CLI.csproj b/Confuser.CLI/Confuser.CLI.csproj index 82743f15..1ae57456 100644 --- a/Confuser.CLI/Confuser.CLI.csproj +++ b/Confuser.CLI/Confuser.CLI.csproj @@ -4,7 +4,7 @@ Exe - net461 + net8.0 true ..\ConfuserEx.snk diff --git a/Confuser.Core/Confuser.Core.csproj b/Confuser.Core/Confuser.Core.csproj index 0d7acf71..198daf15 100644 --- a/Confuser.Core/Confuser.Core.csproj +++ b/Confuser.Core/Confuser.Core.csproj @@ -4,7 +4,7 @@ - net461;netstandard2.0 + net8.0 true ..\ConfuserEx.snk @@ -15,7 +15,7 @@ - + diff --git a/Confuser.Core/ConfuserEngine.cs b/Confuser.Core/ConfuserEngine.cs index 519f8585..177db84e 100644 --- a/Confuser.Core/ConfuserEngine.cs +++ b/Confuser.Core/ConfuserEngine.cs @@ -536,6 +536,7 @@ static void PrintEnvironmentInfo(ConfuserContext context) { if (context.Resolver != null) { context.Logger.Error("Cached assemblies:"); foreach (AssemblyDef asm in context.InternalResolver.GetCachedAssemblies()) { + if (asm is null) continue; if (string.IsNullOrEmpty(asm.ManifestModule.Location)) context.Logger.ErrorFormat(" {0}", asm.FullName); else diff --git a/Confuser.DynCipher/Confuser.DynCipher.csproj b/Confuser.DynCipher/Confuser.DynCipher.csproj index 669658e5..fdeb05c7 100644 --- a/Confuser.DynCipher/Confuser.DynCipher.csproj +++ b/Confuser.DynCipher/Confuser.DynCipher.csproj @@ -4,7 +4,7 @@ - net461;netstandard2.0 + net8.0 true ..\ConfuserEx.snk diff --git a/Confuser.DynCipher/Generation/ExpressionGenerator.cs b/Confuser.DynCipher/Generation/ExpressionGenerator.cs index 4a01dac8..2936a2b1 100644 --- a/Confuser.DynCipher/Generation/ExpressionGenerator.cs +++ b/Confuser.DynCipher/Generation/ExpressionGenerator.cs @@ -4,6 +4,7 @@ using Confuser.Core; using Confuser.Core.Services; using Confuser.DynCipher.AST; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.DynCipher.Generation { internal class ExpressionGenerator { @@ -161,4 +162,4 @@ enum ExpressionOps { Neg } } -} \ No newline at end of file +} diff --git a/Confuser.Protections/AntiTamper/AntiMode.cs b/Confuser.Protections/AntiTamper/AntiMode.cs index 5df4cdf6..d9707c9c 100644 --- a/Confuser.Protections/AntiTamper/AntiMode.cs +++ b/Confuser.Protections/AntiTamper/AntiMode.cs @@ -12,6 +12,7 @@ using dnlib.DotNet.Emit; using dnlib.DotNet.Writer; using MethodBody = dnlib.DotNet.Writer.MethodBody; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Protections.AntiTamper { internal class AntiMode : IModeHandler { diff --git a/Confuser.Protections/AntiTamper/JITBody.cs b/Confuser.Protections/AntiTamper/JITBody.cs index ae306d34..0f5c664b 100644 --- a/Confuser.Protections/AntiTamper/JITBody.cs +++ b/Confuser.Protections/AntiTamper/JITBody.cs @@ -104,7 +104,9 @@ public void Serialize(uint token, uint key, byte[] fieldLayout) { counter ^= (state >> 5) | (state << 27); } } - } + + public uint CalculateAlignment() => 0; + } internal class JITMethodBodyWriter : MethodBodyWriterBase { readonly CilBody body; @@ -251,5 +253,7 @@ public void PopulateSection(PESection section) { offset += entry.Value.GetFileLength(); } } + + public uint CalculateAlignment() => 0; } -} \ No newline at end of file +} diff --git a/Confuser.Protections/AntiTamper/JITMode.cs b/Confuser.Protections/AntiTamper/JITMode.cs index 23f41e90..7b38d608 100644 --- a/Confuser.Protections/AntiTamper/JITMode.cs +++ b/Confuser.Protections/AntiTamper/JITMode.cs @@ -12,6 +12,7 @@ using dnlib.DotNet.Emit; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Protections.AntiTamper { internal class JITMode : IModeHandler { diff --git a/Confuser.Protections/AntiTamper/NormalMode.cs b/Confuser.Protections/AntiTamper/NormalMode.cs index ec631307..dfe22664 100644 --- a/Confuser.Protections/AntiTamper/NormalMode.cs +++ b/Confuser.Protections/AntiTamper/NormalMode.cs @@ -12,6 +12,7 @@ using dnlib.DotNet.Emit; using dnlib.DotNet.Writer; using MethodBody = dnlib.DotNet.Writer.MethodBody; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Protections.AntiTamper { internal class NormalMode : IModeHandler { diff --git a/Confuser.Protections/Compress/Compressor.cs b/Confuser.Protections/Compress/Compressor.cs index 33c0ea6b..ed7e68c5 100644 --- a/Confuser.Protections/Compress/Compressor.cs +++ b/Confuser.Protections/Compress/Compressor.cs @@ -1,355 +1,356 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; -using System.Text; -using Confuser.Core; -using Confuser.Core.Helpers; -using Confuser.Core.Services; -using Confuser.Protections.Compress; -using dnlib.DotNet; -using dnlib.DotNet.Emit; -using dnlib.DotNet.MD; -using dnlib.DotNet.Writer; -using dnlib.PE; -using FileAttributes = dnlib.DotNet.FileAttributes; -using SR = System.Reflection; - -namespace Confuser.Protections { - internal class Compressor : Packer { - public const string _Id = "compressor"; - public const string _FullId = "Ki.Compressor"; - public const string _ServiceId = "Ki.Compressor"; - public static readonly object ContextKey = new object(); - - public override string Name { - get { return "Compressing Packer"; } - } - - public override string Description { - get { return "This packer reduces the size of output."; } - } - - public override string Id { - get { return _Id; } - } - - public override string FullId { - get { return _FullId; } - } - - protected override void Initialize(ConfuserContext context) { } - - protected override void PopulatePipeline(ProtectionPipeline pipeline) { - pipeline.InsertPreStage(PipelineStage.WriteModule, new ExtractPhase(this)); - } - - protected override void Pack(ConfuserContext context, ProtectionParameters parameters) { - var ctx = context.Annotations.Get(context, ContextKey); - if (ctx == null) { - context.Logger.Error("No executable module!"); - throw new ConfuserException(null); - } - - ModuleDefMD originModule = context.Modules[ctx.ModuleIndex]; - ctx.OriginModuleDef = originModule; - - var stubModule = new ModuleDefUser(ctx.ModuleName, originModule.Mvid, originModule.CorLibTypes.AssemblyRef); - if (ctx.CompatMode) { - var assembly = new AssemblyDefUser(originModule.Assembly); - assembly.Name += ".cr"; - assembly.Modules.Add(stubModule); - } - else { - ctx.Assembly.Modules.Insert(0, stubModule); - ImportAssemblyTypeReferences(originModule, stubModule); - } - stubModule.Characteristics = originModule.Characteristics; - stubModule.Cor20HeaderFlags = originModule.Cor20HeaderFlags; - stubModule.Cor20HeaderRuntimeVersion = originModule.Cor20HeaderRuntimeVersion; - stubModule.DllCharacteristics = originModule.DllCharacteristics; - stubModule.EncBaseId = originModule.EncBaseId; - stubModule.EncId = originModule.EncId; - stubModule.Generation = originModule.Generation; - stubModule.Kind = ctx.Kind; - stubModule.Machine = originModule.Machine; - stubModule.RuntimeVersion = originModule.RuntimeVersion; - stubModule.TablesHeaderVersion = originModule.TablesHeaderVersion; - stubModule.Win32Resources = originModule.Win32Resources; - - InjectStub(context, ctx, parameters, stubModule); - +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; +using Confuser.Core; +using Confuser.Core.Helpers; +using Confuser.Core.Services; +using Confuser.Protections.Compress; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.DotNet.Writer; +using dnlib.PE; +using FileAttributes = dnlib.DotNet.FileAttributes; +using SR = System.Reflection; +using UnreachableException = Confuser.Core.UnreachableException; + +namespace Confuser.Protections { + internal class Compressor : Packer { + public const string _Id = "compressor"; + public const string _FullId = "Ki.Compressor"; + public const string _ServiceId = "Ki.Compressor"; + public static readonly object ContextKey = new object(); + + public override string Name { + get { return "Compressing Packer"; } + } + + public override string Description { + get { return "This packer reduces the size of output."; } + } + + public override string Id { + get { return _Id; } + } + + public override string FullId { + get { return _FullId; } + } + + protected override void Initialize(ConfuserContext context) { } + + protected override void PopulatePipeline(ProtectionPipeline pipeline) { + pipeline.InsertPreStage(PipelineStage.WriteModule, new ExtractPhase(this)); + } + + protected override void Pack(ConfuserContext context, ProtectionParameters parameters) { + var ctx = context.Annotations.Get(context, ContextKey); + if (ctx == null) { + context.Logger.Error("No executable module!"); + throw new ConfuserException(null); + } + + ModuleDefMD originModule = context.Modules[ctx.ModuleIndex]; + ctx.OriginModuleDef = originModule; + + var stubModule = new ModuleDefUser(ctx.ModuleName, originModule.Mvid, originModule.CorLibTypes.AssemblyRef); + if (ctx.CompatMode) { + var assembly = new AssemblyDefUser(originModule.Assembly); + assembly.Name += ".cr"; + assembly.Modules.Add(stubModule); + } + else { + ctx.Assembly.Modules.Insert(0, stubModule); + ImportAssemblyTypeReferences(originModule, stubModule); + } + stubModule.Characteristics = originModule.Characteristics; + stubModule.Cor20HeaderFlags = originModule.Cor20HeaderFlags; + stubModule.Cor20HeaderRuntimeVersion = originModule.Cor20HeaderRuntimeVersion; + stubModule.DllCharacteristics = originModule.DllCharacteristics; + stubModule.EncBaseId = originModule.EncBaseId; + stubModule.EncId = originModule.EncId; + stubModule.Generation = originModule.Generation; + stubModule.Kind = ctx.Kind; + stubModule.Machine = originModule.Machine; + stubModule.RuntimeVersion = originModule.RuntimeVersion; + stubModule.TablesHeaderVersion = originModule.TablesHeaderVersion; + stubModule.Win32Resources = originModule.Win32Resources; + + InjectStub(context, ctx, parameters, stubModule); + var snKey = context.Annotations.Get(originModule, Marker.SNKey); - var snPubKey = context.Annotations.Get(originModule, Marker.SNPubKey); - var snDelaySig = context.Annotations.Get(originModule, Marker.SNDelaySig, false); + var snPubKey = context.Annotations.Get(originModule, Marker.SNPubKey); + var snDelaySig = context.Annotations.Get(originModule, Marker.SNDelaySig, false); var snSigKey = context.Annotations.Get(originModule, Marker.SNSigKey); - var snPubSigKey = context.Annotations.Get(originModule, Marker.SNSigPubKey); - - using (var ms = new MemoryStream()) { - var options = new ModuleWriterOptions(stubModule) { - StrongNameKey = snKey, - StrongNamePublicKey = snPubKey, - DelaySign = snDelaySig - }; - var injector = new KeyInjector(ctx); - options.WriterEvent += injector.WriterEvent; - - stubModule.Write(ms, options); - context.CheckCancellation(); - ProtectStub(context, context.OutputPaths[ctx.ModuleIndex], ms.ToArray(), snKey, snPubKey, snSigKey, snPubKey, snDelaySig, new StubProtection(ctx, originModule)); - } - } - - static string GetId(byte[] module) { - var md = MetadataFactory.CreateMetadata(new PEImage(module)); - var assembly = new AssemblyNameInfo(); - if (md.TablesStream.TryReadAssemblyRow(1, out var assemblyRow)) { - assembly.Name = md.StringsStream.ReadNoNull(assemblyRow.Name); - assembly.Culture = md.StringsStream.ReadNoNull(assemblyRow.Locale); - assembly.PublicKeyOrToken = new PublicKey(md.BlobStream.Read(assemblyRow.PublicKey)); - assembly.HashAlgId = (AssemblyHashAlgorithm)assemblyRow.HashAlgId; - assembly.Version = new Version(assemblyRow.MajorVersion, assemblyRow.MinorVersion, assemblyRow.BuildNumber, assemblyRow.RevisionNumber); - assembly.Attributes = (AssemblyAttributes)assemblyRow.Flags; - } - return GetId(assembly); - } - - static string GetId(IAssembly assembly) { - return new SR.AssemblyName(assembly.FullName).FullName.ToUpperInvariant(); - } - - void PackModules(ConfuserContext context, CompressorContext compCtx, ModuleDef stubModule, ICompressionService comp, RandomGenerator random) { - int maxLen = 0; - var modules = new Dictionary(); - for (int i = 0; i < context.OutputModules.Count; i++) { - if (i == compCtx.ModuleIndex) - continue; - - string id = GetId(context.Modules[i].Assembly); - modules.Add(id, context.OutputModules[i]); - - int strLen = Encoding.UTF8.GetByteCount(id); - if (strLen > maxLen) - maxLen = strLen; - } - foreach (var extModule in context.ExternalModules) { - var name = GetId(extModule).ToUpperInvariant(); - modules.Add(name, extModule); - - int strLen = Encoding.UTF8.GetByteCount(name); - if (strLen > maxLen) - maxLen = strLen; - } - - byte[] key = random.NextBytes(4 + maxLen); - key[0] = (byte)(compCtx.EntryPointToken >> 0); - key[1] = (byte)(compCtx.EntryPointToken >> 8); - key[2] = (byte)(compCtx.EntryPointToken >> 16); - key[3] = (byte)(compCtx.EntryPointToken >> 24); - for (int i = 4; i < key.Length; i++) // no zero bytes - key[i] |= 1; - compCtx.KeySig = key; - - int moduleIndex = 0; - foreach (var entry in modules) { - byte[] name = Encoding.UTF8.GetBytes(entry.Key); - for (int i = 0; i < name.Length; i++) - name[i] *= key[i + 4]; - - uint state = 0x6fff61; - foreach (byte chr in name) - state = state * 0x5e3f1f + chr; - byte[] encrypted = compCtx.Encrypt(comp, entry.Value, state, progress => { - progress = (progress + moduleIndex) / modules.Count; - context.Logger.Progress((int)(progress * 10000), 10000); - }); - context.CheckCancellation(); - - var resource = new EmbeddedResource(Convert.ToBase64String(name), encrypted, ManifestResourceAttributes.Private); - stubModule.Resources.Add(resource); - moduleIndex++; - } - context.Logger.EndProgress(); - } - - void InjectData(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.IsSealed = true; - dataType.ClassLayout = new ClassLayoutUser(1, (uint)data.Length); - stubModule.GlobalType.NestedTypes.Add(dataType); - - var dataField = new FieldDefUser("DataField", new FieldSig(dataType.ToTypeSig())) { - IsStatic = true, - HasFieldRVA = true, - InitialValue = data, - Access = FieldAttributes.CompilerControlled - }; - stubModule.GlobalType.Fields.Add(dataField); - - MutationHelper.ReplacePlaceholder(method, arg => { - var repl = new List(); - repl.AddRange(arg); - repl.Add(Instruction.Create(OpCodes.Dup)); - repl.Add(Instruction.Create(OpCodes.Ldtoken, dataField)); - repl.Add(Instruction.Create(OpCodes.Call, stubModule.Import( - typeof(RuntimeHelpers).GetMethod("InitializeArray")))); - return repl.ToArray(); - }); - } - - void InjectStub(ConfuserContext context, CompressorContext compCtx, ProtectionParameters parameters, ModuleDef stubModule) { - var rt = context.Registry.GetService(); - RandomGenerator random = context.Registry.GetService().GetRandomGenerator(Id); - var comp = context.Registry.GetService(); - - var rtType = rt.GetRuntimeType(compCtx.CompatMode ? "Confuser.Runtime.CompressorCompat" : "Confuser.Runtime.Compressor"); - IEnumerable defs = InjectHelper.Inject(rtType, stubModule.GlobalType, stubModule); - - switch (parameters.GetParameter(context, context.CurrentModule, "key", Mode.Normal)) { - case Mode.Normal: - compCtx.Deriver = new NormalDeriver(); - break; - case Mode.Dynamic: - compCtx.Deriver = new DynamicDeriver(); - break; - default: - throw new UnreachableException(); - } - compCtx.Deriver.Init(context, random); - - context.Logger.Debug("Encrypting modules..."); - - // Main - MethodDef entryPoint = defs.OfType().Single(method => method.Name == "Main"); - stubModule.EntryPoint = entryPoint; - - if (compCtx.EntryPoint.HasAttribute("System.STAThreadAttribute")) { - var attrType = stubModule.CorLibTypes.GetTypeRef("System", "STAThreadAttribute"); - var ctorSig = MethodSig.CreateInstance(stubModule.CorLibTypes.Void); - entryPoint.CustomAttributes.Add(new CustomAttribute( - new MemberRefUser(stubModule, ".ctor", ctorSig, attrType))); - } - else if (compCtx.EntryPoint.HasAttribute("System.MTAThreadAttribute")) { - var attrType = stubModule.CorLibTypes.GetTypeRef("System", "MTAThreadAttribute"); - var ctorSig = MethodSig.CreateInstance(stubModule.CorLibTypes.Void); - entryPoint.CustomAttributes.Add(new CustomAttribute( - new MemberRefUser(stubModule, ".ctor", ctorSig, attrType))); - } - - uint seed = random.NextUInt32(); - compCtx.OriginModule = context.OutputModules[compCtx.ModuleIndex]; - - byte[] encryptedModule = compCtx.Encrypt(comp, compCtx.OriginModule, seed, - progress => context.Logger.Progress((int)(progress * 10000), 10000)); - context.Logger.EndProgress(); - context.CheckCancellation(); - - compCtx.EncryptedModule = encryptedModule; - - MutationHelper.InjectKeys(entryPoint, - new[] { 0, 1 }, - new[] { encryptedModule.Length >> 2, (int)seed }); - InjectData(stubModule, entryPoint, encryptedModule); - - // Decrypt - MethodDef decrypter = defs.OfType().Single(method => method.Name == "Decrypt"); - decrypter.Body.SimplifyMacros(decrypter.Parameters); - List instrs = decrypter.Body.Instructions.ToList(); - for (int i = 0; i < instrs.Count; i++) { - Instruction instr = instrs[i]; - if (instr.OpCode == OpCodes.Call) { - var method = (IMethod)instr.Operand; - if (method.DeclaringType.Name == "Mutation" && - method.Name == "Crypt") { - Instruction ldDst = instrs[i - 2]; - Instruction ldSrc = instrs[i - 1]; - Debug.Assert(ldDst.OpCode == OpCodes.Ldloc && ldSrc.OpCode == OpCodes.Ldloc); - instrs.RemoveAt(i); - instrs.RemoveAt(i - 1); - instrs.RemoveAt(i - 2); - instrs.InsertRange(i - 2, compCtx.Deriver.EmitDerivation(decrypter, context, (Local)ldDst.Operand, (Local)ldSrc.Operand)); - } - else if (method.DeclaringType.Name == "Lzma" && - method.Name == "Decompress") { - MethodDef decomp = comp.GetRuntimeDecompressor(stubModule, member => { }); - instr.Operand = decomp; - } - } - } - decrypter.Body.Instructions.Clear(); - foreach (Instruction instr in instrs) - decrypter.Body.Instructions.Add(instr); - - // Pack modules - PackModules(context, compCtx, stubModule, comp, random); - } - - void ImportAssemblyTypeReferences(ModuleDef originModule, ModuleDef stubModule) { - var assembly = stubModule.Assembly; - foreach (var ca in assembly.CustomAttributes) { - if (ca.AttributeType.Scope == originModule) - ca.Constructor = (ICustomAttributeType)stubModule.Import(ca.Constructor); - } - foreach (var ca in assembly.DeclSecurities.SelectMany(declSec => declSec.CustomAttributes)) { - if (ca.AttributeType.Scope == originModule) - ca.Constructor = (ICustomAttributeType)stubModule.Import(ca.Constructor); - } - } - - class KeyInjector { - readonly CompressorContext ctx; - - public KeyInjector(CompressorContext ctx) { - this.ctx = ctx; - } - - public void WriterEvent(object sender, ModuleWriterEventArgs args) { - OnWriterEvent(args.Writer, args.Event); - } - - private void OnWriterEvent(ModuleWriterBase writer, ModuleWriterEvent evt) { - if (evt == ModuleWriterEvent.MDBeginCreateTables) { - // Add key signature - uint sigBlob = writer.Metadata.BlobHeap.Add(ctx.KeySig); - uint sigRid = writer.Metadata.TablesHeap.StandAloneSigTable.Add(new RawStandAloneSigRow(sigBlob)); - Debug.Assert(sigRid == 1); - uint sigToken = 0x11000000 | sigRid; - ctx.KeyToken = sigToken; - MutationHelper.InjectKey(writer.Module.EntryPoint, 2, (int)sigToken); - } - else if (evt == ModuleWriterEvent.MDBeginAddResources && !ctx.CompatMode) { - // Compute hash - byte[] hash = SHA1.Create().ComputeHash(ctx.OriginModule); - uint hashBlob = writer.Metadata.BlobHeap.Add(hash); - - MDTable fileTbl = writer.Metadata.TablesHeap.FileTable; - uint fileRid = fileTbl.Add(new RawFileRow( - (uint)FileAttributes.ContainsMetadata, - writer.Metadata.StringsHeap.Add("koi"), - hashBlob)); - uint impl = CodedToken.Implementation.Encode(new MDToken(Table.File, fileRid)); - - // Add resources - MDTable resTbl = writer.Metadata.TablesHeap.ManifestResourceTable; - foreach (var resource in ctx.ManifestResources) - resTbl.Add(new RawManifestResourceRow(resource.Offset, resource.Flags, writer.Metadata.StringsHeap.Add(resource.Value), impl)); - - // Add exported types - var exTbl = writer.Metadata.TablesHeap.ExportedTypeTable; - foreach (var type in ctx.OriginModuleDef.GetTypes()) { - if (!type.IsVisibleOutside()) - continue; - exTbl.Add(new RawExportedTypeRow((uint)type.Attributes, 0, - writer.Metadata.StringsHeap.Add(type.Name), - writer.Metadata.StringsHeap.Add(type.Namespace), impl)); - } - } - } - } - } -} + var snPubSigKey = context.Annotations.Get(originModule, Marker.SNSigPubKey); + + using (var ms = new MemoryStream()) { + var options = new ModuleWriterOptions(stubModule) { + StrongNameKey = snKey, + StrongNamePublicKey = snPubKey, + DelaySign = snDelaySig + }; + var injector = new KeyInjector(ctx); + options.WriterEvent += injector.WriterEvent; + + stubModule.Write(ms, options); + context.CheckCancellation(); + ProtectStub(context, context.OutputPaths[ctx.ModuleIndex], ms.ToArray(), snKey, snPubKey, snSigKey, snPubKey, snDelaySig, new StubProtection(ctx, originModule)); + } + } + + static string GetId(byte[] module) { + var md = MetadataFactory.CreateMetadata(new PEImage(module)); + var assembly = new AssemblyNameInfo(); + if (md.TablesStream.TryReadAssemblyRow(1, out var assemblyRow)) { + assembly.Name = md.StringsStream.ReadNoNull(assemblyRow.Name); + assembly.Culture = md.StringsStream.ReadNoNull(assemblyRow.Locale); + assembly.PublicKeyOrToken = new PublicKey(md.BlobStream.Read(assemblyRow.PublicKey)); + assembly.HashAlgId = (AssemblyHashAlgorithm)assemblyRow.HashAlgId; + assembly.Version = new Version(assemblyRow.MajorVersion, assemblyRow.MinorVersion, assemblyRow.BuildNumber, assemblyRow.RevisionNumber); + assembly.Attributes = (AssemblyAttributes)assemblyRow.Flags; + } + return GetId(assembly); + } + + static string GetId(IAssembly assembly) { + return new SR.AssemblyName(assembly.FullName).FullName.ToUpperInvariant(); + } + + void PackModules(ConfuserContext context, CompressorContext compCtx, ModuleDef stubModule, ICompressionService comp, RandomGenerator random) { + int maxLen = 0; + var modules = new Dictionary(); + for (int i = 0; i < context.OutputModules.Count; i++) { + if (i == compCtx.ModuleIndex) + continue; + + string id = GetId(context.Modules[i].Assembly); + modules.Add(id, context.OutputModules[i]); + + int strLen = Encoding.UTF8.GetByteCount(id); + if (strLen > maxLen) + maxLen = strLen; + } + foreach (var extModule in context.ExternalModules) { + var name = GetId(extModule).ToUpperInvariant(); + modules.Add(name, extModule); + + int strLen = Encoding.UTF8.GetByteCount(name); + if (strLen > maxLen) + maxLen = strLen; + } + + byte[] key = random.NextBytes(4 + maxLen); + key[0] = (byte)(compCtx.EntryPointToken >> 0); + key[1] = (byte)(compCtx.EntryPointToken >> 8); + key[2] = (byte)(compCtx.EntryPointToken >> 16); + key[3] = (byte)(compCtx.EntryPointToken >> 24); + for (int i = 4; i < key.Length; i++) // no zero bytes + key[i] |= 1; + compCtx.KeySig = key; + + int moduleIndex = 0; + foreach (var entry in modules) { + byte[] name = Encoding.UTF8.GetBytes(entry.Key); + for (int i = 0; i < name.Length; i++) + name[i] *= key[i + 4]; + + uint state = 0x6fff61; + foreach (byte chr in name) + state = state * 0x5e3f1f + chr; + byte[] encrypted = compCtx.Encrypt(comp, entry.Value, state, progress => { + progress = (progress + moduleIndex) / modules.Count; + context.Logger.Progress((int)(progress * 10000), 10000); + }); + context.CheckCancellation(); + + var resource = new EmbeddedResource(Convert.ToBase64String(name), encrypted, ManifestResourceAttributes.Private); + stubModule.Resources.Add(resource); + moduleIndex++; + } + context.Logger.EndProgress(); + } + + void InjectData(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.IsSealed = true; + dataType.ClassLayout = new ClassLayoutUser(1, (uint)data.Length); + stubModule.GlobalType.NestedTypes.Add(dataType); + + var dataField = new FieldDefUser("DataField", new FieldSig(dataType.ToTypeSig())) { + IsStatic = true, + HasFieldRVA = true, + InitialValue = data, + Access = FieldAttributes.CompilerControlled + }; + stubModule.GlobalType.Fields.Add(dataField); + + MutationHelper.ReplacePlaceholder(method, arg => { + var repl = new List(); + repl.AddRange(arg); + repl.Add(Instruction.Create(OpCodes.Dup)); + repl.Add(Instruction.Create(OpCodes.Ldtoken, dataField)); + repl.Add(Instruction.Create(OpCodes.Call, stubModule.Import( + typeof(RuntimeHelpers).GetMethod("InitializeArray")))); + return repl.ToArray(); + }); + } + + void InjectStub(ConfuserContext context, CompressorContext compCtx, ProtectionParameters parameters, ModuleDef stubModule) { + var rt = context.Registry.GetService(); + RandomGenerator random = context.Registry.GetService().GetRandomGenerator(Id); + var comp = context.Registry.GetService(); + + var rtType = rt.GetRuntimeType(compCtx.CompatMode ? "Confuser.Runtime.CompressorCompat" : "Confuser.Runtime.Compressor"); + IEnumerable defs = InjectHelper.Inject(rtType, stubModule.GlobalType, stubModule); + + switch (parameters.GetParameter(context, context.CurrentModule, "key", Mode.Normal)) { + case Mode.Normal: + compCtx.Deriver = new NormalDeriver(); + break; + case Mode.Dynamic: + compCtx.Deriver = new DynamicDeriver(); + break; + default: + throw new UnreachableException(); + } + compCtx.Deriver.Init(context, random); + + context.Logger.Debug("Encrypting modules..."); + + // Main + MethodDef entryPoint = defs.OfType().Single(method => method.Name == "Main"); + stubModule.EntryPoint = entryPoint; + + if (compCtx.EntryPoint.HasAttribute("System.STAThreadAttribute")) { + var attrType = stubModule.CorLibTypes.GetTypeRef("System", "STAThreadAttribute"); + var ctorSig = MethodSig.CreateInstance(stubModule.CorLibTypes.Void); + entryPoint.CustomAttributes.Add(new CustomAttribute( + new MemberRefUser(stubModule, ".ctor", ctorSig, attrType))); + } + else if (compCtx.EntryPoint.HasAttribute("System.MTAThreadAttribute")) { + var attrType = stubModule.CorLibTypes.GetTypeRef("System", "MTAThreadAttribute"); + var ctorSig = MethodSig.CreateInstance(stubModule.CorLibTypes.Void); + entryPoint.CustomAttributes.Add(new CustomAttribute( + new MemberRefUser(stubModule, ".ctor", ctorSig, attrType))); + } + + uint seed = random.NextUInt32(); + compCtx.OriginModule = context.OutputModules[compCtx.ModuleIndex]; + + byte[] encryptedModule = compCtx.Encrypt(comp, compCtx.OriginModule, seed, + progress => context.Logger.Progress((int)(progress * 10000), 10000)); + context.Logger.EndProgress(); + context.CheckCancellation(); + + compCtx.EncryptedModule = encryptedModule; + + MutationHelper.InjectKeys(entryPoint, + new[] { 0, 1 }, + new[] { encryptedModule.Length >> 2, (int)seed }); + InjectData(stubModule, entryPoint, encryptedModule); + + // Decrypt + MethodDef decrypter = defs.OfType().Single(method => method.Name == "Decrypt"); + decrypter.Body.SimplifyMacros(decrypter.Parameters); + List instrs = decrypter.Body.Instructions.ToList(); + for (int i = 0; i < instrs.Count; i++) { + Instruction instr = instrs[i]; + if (instr.OpCode == OpCodes.Call) { + var method = (IMethod)instr.Operand; + if (method.DeclaringType.Name == "Mutation" && + method.Name == "Crypt") { + Instruction ldDst = instrs[i - 2]; + Instruction ldSrc = instrs[i - 1]; + Debug.Assert(ldDst.OpCode == OpCodes.Ldloc && ldSrc.OpCode == OpCodes.Ldloc); + instrs.RemoveAt(i); + instrs.RemoveAt(i - 1); + instrs.RemoveAt(i - 2); + instrs.InsertRange(i - 2, compCtx.Deriver.EmitDerivation(decrypter, context, (Local)ldDst.Operand, (Local)ldSrc.Operand)); + } + else if (method.DeclaringType.Name == "Lzma" && + method.Name == "Decompress") { + MethodDef decomp = comp.GetRuntimeDecompressor(stubModule, member => { }); + instr.Operand = decomp; + } + } + } + decrypter.Body.Instructions.Clear(); + foreach (Instruction instr in instrs) + decrypter.Body.Instructions.Add(instr); + + // Pack modules + PackModules(context, compCtx, stubModule, comp, random); + } + + void ImportAssemblyTypeReferences(ModuleDef originModule, ModuleDef stubModule) { + var assembly = stubModule.Assembly; + foreach (var ca in assembly.CustomAttributes) { + if (ca.AttributeType.Scope == originModule) + ca.Constructor = (ICustomAttributeType)stubModule.Import(ca.Constructor); + } + foreach (var ca in assembly.DeclSecurities.SelectMany(declSec => declSec.CustomAttributes)) { + if (ca.AttributeType.Scope == originModule) + ca.Constructor = (ICustomAttributeType)stubModule.Import(ca.Constructor); + } + } + + class KeyInjector { + readonly CompressorContext ctx; + + public KeyInjector(CompressorContext ctx) { + this.ctx = ctx; + } + + public void WriterEvent(object sender, ModuleWriterEventArgs args) { + OnWriterEvent(args.Writer, args.Event); + } + + private void OnWriterEvent(ModuleWriterBase writer, ModuleWriterEvent evt) { + if (evt == ModuleWriterEvent.MDBeginCreateTables) { + // Add key signature + uint sigBlob = writer.Metadata.BlobHeap.Add(ctx.KeySig); + uint sigRid = writer.Metadata.TablesHeap.StandAloneSigTable.Add(new RawStandAloneSigRow(sigBlob)); + Debug.Assert(sigRid == 1); + uint sigToken = 0x11000000 | sigRid; + ctx.KeyToken = sigToken; + MutationHelper.InjectKey(writer.Module.EntryPoint, 2, (int)sigToken); + } + else if (evt == ModuleWriterEvent.MDBeginAddResources && !ctx.CompatMode) { + // Compute hash + byte[] hash = SHA1.Create().ComputeHash(ctx.OriginModule); + uint hashBlob = writer.Metadata.BlobHeap.Add(hash); + + MDTable fileTbl = writer.Metadata.TablesHeap.FileTable; + uint fileRid = fileTbl.Add(new RawFileRow( + (uint)FileAttributes.ContainsMetadata, + writer.Metadata.StringsHeap.Add("koi"), + hashBlob)); + uint impl = CodedToken.Implementation.Encode(new MDToken(Table.File, fileRid)); + + // Add resources + MDTable resTbl = writer.Metadata.TablesHeap.ManifestResourceTable; + foreach (var resource in ctx.ManifestResources) + resTbl.Add(new RawManifestResourceRow(resource.Offset, resource.Flags, writer.Metadata.StringsHeap.Add(resource.Value), impl)); + + // Add exported types + var exTbl = writer.Metadata.TablesHeap.ExportedTypeTable; + foreach (var type in ctx.OriginModuleDef.GetTypes()) { + if (!type.IsVisibleOutside()) + continue; + exTbl.Add(new RawExportedTypeRow((uint)type.Attributes, 0, + writer.Metadata.StringsHeap.Add(type.Name), + writer.Metadata.StringsHeap.Add(type.Namespace), impl)); + } + } + } + } + } +} diff --git a/Confuser.Protections/Confuser.Protections.csproj b/Confuser.Protections/Confuser.Protections.csproj index 8584200b..4819278d 100644 --- a/Confuser.Protections/Confuser.Protections.csproj +++ b/Confuser.Protections/Confuser.Protections.csproj @@ -4,7 +4,7 @@ - net461;netstandard2.0 + net8.0 true ..\ConfuserEx.snk diff --git a/Confuser.Protections/Constants/EncodePhase.cs b/Confuser.Protections/Constants/EncodePhase.cs index 4eba9118..d4426c2c 100644 --- a/Confuser.Protections/Constants/EncodePhase.cs +++ b/Confuser.Protections/Constants/EncodePhase.cs @@ -10,6 +10,7 @@ using Confuser.Core.Services; using dnlib.DotNet; using dnlib.DotNet.Emit; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Protections.Constants { internal class EncodePhase : ProtectionPhase { diff --git a/Confuser.Protections/Constants/InjectPhase.cs b/Confuser.Protections/Constants/InjectPhase.cs index c8d0c331..bb1588ee 100644 --- a/Confuser.Protections/Constants/InjectPhase.cs +++ b/Confuser.Protections/Constants/InjectPhase.cs @@ -10,6 +10,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Protections.Constants { internal class InjectPhase : ProtectionPhase { @@ -175,4 +176,4 @@ void MutateInitializer(CEContext moduleCtx, MethodDef decomp) { moduleCtx.InitMethod.Body.Instructions.Add(instr); } } -} \ No newline at end of file +} diff --git a/Confuser.Protections/Constants/ReferenceReplacer.cs b/Confuser.Protections/Constants/ReferenceReplacer.cs index 901cd76c..91ade60b 100644 --- a/Confuser.Protections/Constants/ReferenceReplacer.cs +++ b/Confuser.Protections/Constants/ReferenceReplacer.cs @@ -7,6 +7,7 @@ using Confuser.Core.Services; using dnlib.DotNet; using dnlib.DotNet.Emit; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Protections.Constants { internal class ReferenceReplacer { diff --git a/Confuser.Protections/Resources/InjectPhase.cs b/Confuser.Protections/Resources/InjectPhase.cs index 54059175..79bd008f 100644 --- a/Confuser.Protections/Resources/InjectPhase.cs +++ b/Confuser.Protections/Resources/InjectPhase.cs @@ -10,6 +10,7 @@ using Confuser.Renamer; using dnlib.DotNet; using dnlib.DotNet.Emit; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Protections.Resources { internal class InjectPhase : ProtectionPhase { @@ -143,4 +144,4 @@ void MutateInitializer(REContext moduleCtx, MethodDef decomp) { moduleCtx.Context.Registry.GetService().ExcludeMethod(moduleCtx.Context, moduleCtx.InitMethod); } } -} \ No newline at end of file +} diff --git a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs index 15a3e94f..cafe18ee 100644 --- a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs @@ -7,6 +7,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Renamer.Analyzers { public sealed class TypeBlobAnalyzer : IRenamer { diff --git a/Confuser.Renamer/BAML/BAMLAnalyzer.cs b/Confuser.Renamer/BAML/BAMLAnalyzer.cs index 141a5ac0..71450a86 100644 --- a/Confuser.Renamer/BAML/BAMLAnalyzer.cs +++ b/Confuser.Renamer/BAML/BAMLAnalyzer.cs @@ -9,6 +9,7 @@ using Confuser.Renamer.Analyzers; using Confuser.Renamer.References; using dnlib.DotNet; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Renamer.BAML { internal class BAMLAnalyzer { diff --git a/Confuser.Renamer/Confuser.Renamer.csproj b/Confuser.Renamer/Confuser.Renamer.csproj index f053a7a7..03ef6b16 100644 --- a/Confuser.Renamer/Confuser.Renamer.csproj +++ b/Confuser.Renamer/Confuser.Renamer.csproj @@ -4,7 +4,7 @@ - net461;netstandard2.0 + net8.0 true ..\ConfuserEx.snk diff --git a/Confuser.Renamer/VTable.cs b/Confuser.Renamer/VTable.cs index a1fe8f21..ecb8ae9f 100644 --- a/Confuser.Renamer/VTable.cs +++ b/Confuser.Renamer/VTable.cs @@ -5,6 +5,7 @@ using Confuser.Core; using dnlib.DotNet; using ILogger = Confuser.Core.ILogger; +using UnreachableException = Confuser.Core.UnreachableException; namespace Confuser.Renamer { public class VTableSignature { diff --git a/Confuser.Runtime/Confuser.Runtime.csproj b/Confuser.Runtime/Confuser.Runtime.csproj index ce22f3c4..a94ffd4c 100644 --- a/Confuser.Runtime/Confuser.Runtime.csproj +++ b/Confuser.Runtime/Confuser.Runtime.csproj @@ -4,7 +4,7 @@ - net20 + net8.0 true true ..\ConfuserEx.snk diff --git a/Confuser2.sln b/Confuser2.sln index cbacdd96..7bd87358 100644 --- a/Confuser2.sln +++ b/Confuser2.sln @@ -221,8 +221,8 @@ Global {E832E9B8-2158-4FC0-89A1-56C6ECC10F6B}.Release|x64.Build.0 = Release|Any CPU {E832E9B8-2158-4FC0-89A1-56C6ECC10F6B}.Release|x86.ActiveCfg = Release|Any CPU {E832E9B8-2158-4FC0-89A1-56C6ECC10F6B}.Release|x86.Build.0 = Release|Any CPU - {A45C184F-F98F-4258-A928-BFF437034791}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {A45C184F-F98F-4258-A928-BFF437034791}.Debug|Any CPU.Build.0 = Release|Any CPU + {A45C184F-F98F-4258-A928-BFF437034791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A45C184F-F98F-4258-A928-BFF437034791}.Debug|Any CPU.Build.0 = Debug|Any CPU {A45C184F-F98F-4258-A928-BFF437034791}.Debug|x64.ActiveCfg = Debug|Any CPU {A45C184F-F98F-4258-A928-BFF437034791}.Debug|x64.Build.0 = Debug|Any CPU {A45C184F-F98F-4258-A928-BFF437034791}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/ConfuserEx/ComponentDiscovery.cs b/ConfuserEx/ComponentDiscovery.cs index 3a898857..24f885c3 100644 --- a/ConfuserEx/ComponentDiscovery.cs +++ b/ConfuserEx/ComponentDiscovery.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Runtime.Loader; using Confuser.Core; namespace ConfuserEx { @@ -28,11 +29,25 @@ static void CrossDomainLoadComponents() { } public static void LoadComponents(IList protections, IList packers, string pluginPath) { + var ctx = new CrossDomainContext(protections, packers, pluginPath); - AppDomain appDomain = AppDomain.CreateDomain(""); - appDomain.SetData("ctx", ctx); - appDomain.DoCallBack(CrossDomainLoadComponents); - AppDomain.Unload(appDomain); + var domain = new AssemblyLoadContext(null); + + Assembly assembly = domain.LoadFromAssemblyPath(ctx.PluginPath); + foreach (var module in assembly.GetLoadedModules()) + foreach (var i in module.GetTypes()) { + if (i.IsAbstract || !PluginDiscovery.HasAccessibleDefConstructor(i)) + continue; + + if (typeof(Protection).IsAssignableFrom(i)) { + var prot = (Protection)Activator.CreateInstance(i); + ctx.AddProtection(Info.FromComponent(prot, ctx.PluginPath)); + } + else if (typeof(Packer).IsAssignableFrom(i)) { + var packer = (Packer)Activator.CreateInstance(i); + ctx.AddPacker(Info.FromComponent(packer, ctx.PluginPath)); + } + } } public static void RemoveComponents(IList protections, IList packers, string pluginPath) { @@ -123,4 +138,4 @@ protected override void PopulatePipeline(ProtectionPipeline pipeline) { } } } -} \ No newline at end of file +} diff --git a/ConfuserEx/ConfuserEx.csproj b/ConfuserEx/ConfuserEx.csproj index f9f103e7..350913aa 100644 --- a/ConfuserEx/ConfuserEx.csproj +++ b/ConfuserEx/ConfuserEx.csproj @@ -4,10 +4,11 @@ WinExe - net461 + net8.0-windows true true ..\ConfuserEx.snk + true @@ -17,7 +18,7 @@ - + @@ -29,6 +30,18 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/ConfuserEx/FileDragDrop.cs b/ConfuserEx/FileDragDrop.cs index c58a5a10..a0542a33 100644 --- a/ConfuserEx/FileDragDrop.cs +++ b/ConfuserEx/FileDragDrop.cs @@ -3,18 +3,19 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using CommunityToolkit.Mvvm.Input; using ConfuserEx.ViewModel; -using GalaSoft.MvvmLight.CommandWpf; namespace ConfuserEx { public class FileDragDrop { public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(FileDragDrop), new UIPropertyMetadata(null, OnCommandChanged)); - public static ICommand FileCmd = new DragDropCommand( + public static ICommand FileCmd = DragDropCommand.CreateDragDropCommand( data => { Debug.Assert(data.Item2.GetDataPresent(DataFormats.FileDrop)); if (data.Item1 is TextBox) { @@ -38,7 +39,7 @@ public class FileDragDrop { }); - public static ICommand DirectoryCmd = new DragDropCommand( + public static ICommand DirectoryCmd = DragDropCommand.CreateDragDropCommand( data => { Debug.Assert(data.Item2.GetDataPresent(DataFormats.FileDrop)); if (data.Item1 is TextBox) { @@ -86,7 +87,7 @@ static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventA static void OnDragOver(object sender, DragEventArgs e) { ICommand cmd = GetCommand((DependencyObject)sender); e.Effects = DragDropEffects.None; - if (cmd is DragDropCommand) { + if (DragDropCommand.IsDragDropCommand(cmd)) { if (cmd.CanExecute(Tuple.Create((UIElement)sender, e.Data))) e.Effects = DragDropEffects.Link; } @@ -99,7 +100,7 @@ static void OnDragOver(object sender, DragEventArgs e) { static void OnDrop(object sender, DragEventArgs e) { ICommand cmd = GetCommand((DependencyObject)sender); - if (cmd is DragDropCommand) { + if (DragDropCommand.IsDragDropCommand(cmd)) { if (cmd.CanExecute(Tuple.Create((UIElement)sender, e.Data))) cmd.Execute(Tuple.Create((UIElement)sender, e.Data)); } @@ -110,10 +111,18 @@ static void OnDrop(object sender, DragEventArgs e) { e.Handled = true; } + public static class DragDropCommand + { + public static RelayCommand> CreateDragDropCommand(Action> execute, Func, bool> canExecute) + { + return new RelayCommand>(execute, value => canExecute(value)); + } - class DragDropCommand : RelayCommand> { - public DragDropCommand(Action> execute, Func, bool> canExecute) - : base(execute, canExecute) { } + public static bool IsDragDropCommand(ICommand command) + { + if (command is RelayCommand>) return true; + return false; + } } } -} \ No newline at end of file +} diff --git a/ConfuserEx/ViewModel/Project/ProjectRuleVM.cs b/ConfuserEx/ViewModel/Project/ProjectRuleVM.cs index f15940d2..0042f10b 100644 --- a/ConfuserEx/ViewModel/Project/ProjectRuleVM.cs +++ b/ConfuserEx/ViewModel/Project/ProjectRuleVM.cs @@ -51,11 +51,13 @@ public string ExpressionError { set { SetProperty(ref error, value, "ExpressionError"); } } - public ProtectionPreset Preset { - get { return rule.Preset; } + public string Preset { + get { return rule.Preset.ToString(); } set { - if (SetProperty(rule.Preset != value, val => rule.Preset = val, value, "Preset")) - parent.IsModified = true; + if (Enum.TryParse(value, out var value1)) { + if (SetProperty(rule.Preset != value1, val => rule.Preset = val, value1, "Preset")) + parent.IsModified = true; + } } } @@ -88,4 +90,4 @@ void ParseExpression() { Expression = expression; } } -} \ No newline at end of file +} diff --git a/ConfuserEx/ViewModel/UI/AboutTabVM.cs b/ConfuserEx/ViewModel/UI/AboutTabVM.cs index 113b452a..64aa5bde 100644 --- a/ConfuserEx/ViewModel/UI/AboutTabVM.cs +++ b/ConfuserEx/ViewModel/UI/AboutTabVM.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Windows.Input; using System.Windows.Media.Imaging; -using GalaSoft.MvvmLight.CommandWpf; +using CommunityToolkit.Mvvm.Input; namespace ConfuserEx.ViewModel { internal class AboutTabVM : TabViewModel { @@ -20,4 +20,4 @@ public ICommand LaunchBrowser { public BitmapSource Icon { get; private set; } } -} \ No newline at end of file +} diff --git a/ConfuserEx/ViewModel/UI/AppVM.cs b/ConfuserEx/ViewModel/UI/AppVM.cs index b058b239..1e3875db 100644 --- a/ConfuserEx/ViewModel/UI/AppVM.cs +++ b/ConfuserEx/ViewModel/UI/AppVM.cs @@ -6,9 +6,9 @@ using System.Windows; using System.Windows.Input; using System.Xml; +using CommunityToolkit.Mvvm.Input; using Confuser.Core; using Confuser.Core.Project; -using GalaSoft.MvvmLight.CommandWpf; using Ookii.Dialogs.Wpf; namespace ConfuserEx.ViewModel { diff --git a/ConfuserEx/ViewModel/UI/ProjectTabVM.cs b/ConfuserEx/ViewModel/UI/ProjectTabVM.cs index 899dd5f9..a989f7bf 100644 --- a/ConfuserEx/ViewModel/UI/ProjectTabVM.cs +++ b/ConfuserEx/ViewModel/UI/ProjectTabVM.cs @@ -4,9 +4,9 @@ using System.Linq; using System.Windows; using System.Windows.Input; +using CommunityToolkit.Mvvm.Input; using Confuser.Core.Project; using ConfuserEx.Views; -using GalaSoft.MvvmLight.CommandWpf; using Ookii.Dialogs.Wpf; namespace ConfuserEx.ViewModel { diff --git a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs index 7e2f1686..667cfb0d 100644 --- a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs +++ b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs @@ -5,9 +5,9 @@ using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; +using CommunityToolkit.Mvvm.Input; using Confuser.Core; using Confuser.Core.Project; -using GalaSoft.MvvmLight.CommandWpf; namespace ConfuserEx.ViewModel { internal class ProtectTabVM : TabViewModel, ILogger { diff --git a/ConfuserEx/ViewModel/UI/SettingsTabVM.cs b/ConfuserEx/ViewModel/UI/SettingsTabVM.cs index 5641d088..7f398700 100644 --- a/ConfuserEx/ViewModel/UI/SettingsTabVM.cs +++ b/ConfuserEx/ViewModel/UI/SettingsTabVM.cs @@ -5,10 +5,10 @@ using System.Windows; using System.Windows.Data; using System.Windows.Input; +using CommunityToolkit.Mvvm.Input; using Confuser.Core; using Confuser.Core.Project; using ConfuserEx.Views; -using GalaSoft.MvvmLight.CommandWpf; namespace ConfuserEx.ViewModel { internal class SettingsTabVM : TabViewModel { diff --git a/ConfuserEx/Views/ProjectRuleView.xaml.cs b/ConfuserEx/Views/ProjectRuleView.xaml.cs index 6d8a1534..8da8f2df 100644 --- a/ConfuserEx/Views/ProjectRuleView.xaml.cs +++ b/ConfuserEx/Views/ProjectRuleView.xaml.cs @@ -3,10 +3,10 @@ using System.Diagnostics; using System.Windows; using System.Windows.Media; +using CommunityToolkit.Mvvm.Input; using Confuser.Core; using Confuser.Core.Project; using ConfuserEx.ViewModel; -using GalaSoft.MvvmLight.CommandWpf; namespace ConfuserEx.Views { public partial class ProjectRuleView : Window { @@ -43,7 +43,7 @@ public override void OnApplyTemplate() { prots.SelectedIndex = selIndex >= rule.Protections.Count ? rule.Protections.Count - 1 : selIndex; }, () => prots.SelectedIndex != -1); - prots.SelectionChanged += (sender, args) => (RemoveBtn.Command as RelayCommand)?.RaiseCanExecuteChanged(); + prots.SelectionChanged += (sender, args) => (RemoveBtn.Command as RelayCommand)?.NotifyCanExecuteChanged(); } public void Cleanup() { diff --git a/ConfuserEx/Views/ProjectTabAdvancedView.xaml.cs b/ConfuserEx/Views/ProjectTabAdvancedView.xaml.cs index fe6bbd24..09b63206 100644 --- a/ConfuserEx/Views/ProjectTabAdvancedView.xaml.cs +++ b/ConfuserEx/Views/ProjectTabAdvancedView.xaml.cs @@ -1,8 +1,8 @@ using System; using System.Diagnostics; using System.Windows; +using CommunityToolkit.Mvvm.Input; using ConfuserEx.ViewModel; -using GalaSoft.MvvmLight.CommandWpf; using Ookii.Dialogs.Wpf; namespace ConfuserEx.Views { @@ -61,4 +61,4 @@ public override void OnApplyTemplate() { }, () => ProbePaths.SelectedIndex != -1); } } -} \ No newline at end of file +}