From e96430a3a19fc8db931d14b3d3bfb8026cdeae10 Mon Sep 17 00:00:00 2001 From: "Adam - Zyph. Tech." <273765681+ZypherionTechnologies@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:05:00 -0700 Subject: [PATCH] Fix constants pipeline and resource handling --- .../Protections/Constants/IResolver.cs | 355 ++++++++++++++++-- .../Protections/Constants/NormalResolver.cs | 29 +- .../Protections/Constants/X86Resolver.cs | 29 +- .../Protections/Renamer/UnicodeRemover.cs | 52 ++- .../Protections/Resources/ResourcesRemover.cs | 7 +- UnConfuserEx/RuntimeOptions.cs | 1 + UnConfuserEx/UnConfuserEx.cs | 40 +- UnConfuserEx/UnConfuserEx.csproj | 1 + 8 files changed, 447 insertions(+), 67 deletions(-) diff --git a/UnConfuserEx/Protections/Constants/IResolver.cs b/UnConfuserEx/Protections/Constants/IResolver.cs index e67c2bf..3570579 100644 --- a/UnConfuserEx/Protections/Constants/IResolver.cs +++ b/UnConfuserEx/Protections/Constants/IResolver.cs @@ -132,18 +132,21 @@ namespace UnConfuserEx.Protections.Constants return (int)ctx.Stack.Pop(); } - protected static int CollapseToLdcI4(IList instrs, int endInclusive) - { - if (instrs[endInclusive].IsLdcI4()) - return endInclusive; - - int start = FindArithmeticStart(instrs, endInclusive); - int value = ComputeIntValueBefore(instrs, endInclusive); - - var replacement = Instruction.CreateLdcI4(value); - instrs[start] = replacement; - for (int k = endInclusive; k > start; k--) - instrs.RemoveAt(k); + protected static int CollapseToLdcI4(IList instrs, int endInclusive) + { + if (instrs[endInclusive].IsLdcI4()) + return endInclusive; + + int start = FindArithmeticStart(instrs, endInclusive); + int value = ComputeIntValueBefore(instrs, endInclusive); + + instrs[start].OpCode = OpCodes.Ldc_I4; + instrs[start].Operand = value; + for (int k = start + 1; k <= endInclusive; k++) + { + instrs[k].OpCode = OpCodes.Nop; + instrs[k].Operand = null; + } return start; } @@ -179,11 +182,255 @@ namespace UnConfuserEx.Protections.Constants } } + + protected static bool IsStringType(TypeSig type) + { + return type.ElementType == ElementType.String || type.FullName == "System.String"; + } + + protected static bool IsSupportedNumberType(TypeSig type) + { + switch (type.ElementType) + { + case ElementType.I4: + case ElementType.R8: + case ElementType.R4: + return true; + default: + return false; + } + } + + protected static void SimplifyStatefulHelperCalls(MethodDef method) + { + if (!method.HasBody || method.Body.Instructions.Count == 0) + return; + + var states = new Dictionary(); + var instrs = method.Body.Instructions; + int index = 0; + while (index < instrs.Count) + { + if (TryInitializeHelperLocal(method, instrs, states, index, out var initEnd)) + { + index = initEnd + 1; + continue; + } + + if (TryFoldHelperCall(method, instrs, states, index, out var replacementEnd)) + { + index = Math.Max(0, index - 1); + continue; + } + + index++; + } + } + + private static bool TryInitializeHelperLocal(MethodDef method, IList instrs, Dictionary states, int index, out int endIndex) + { + endIndex = index; + if (index + 2 >= instrs.Count) + return false; + if (!TryGetLdlocaLocal(instrs[index], out var local)) + return false; + if (!instrs[index + 1].IsLdcI4()) + return false; + if (instrs[index + 2].OpCode != OpCodes.Call) + return false; + if (instrs[index + 2].Operand is not IMethod ctorRef) + return false; + var ctor = ctorRef.ResolveMethodDef(); + if (ctor == null || !ctor.HasBody || !ctor.IsInstanceConstructor) + return false; + if (!TypeEqualityComparer.Instance.Equals(local.Type.ToTypeDefOrRef(), ctor.DeclaringType)) + return false; + + var seed = instrs[index + 1].GetLdcI4Value(); + if (!TryExecuteHelperCtor(ctor, (uint)seed, out var state)) + return false; + + states[local] = state; + endIndex = index + 2; + return true; + } + + private static bool TryFoldHelperCall(MethodDef method, IList instrs, Dictionary states, int index, out int endIndex) + { + endIndex = index; + if (index + 3 >= instrs.Count) + return false; + if (!TryGetLdlocaLocal(instrs[index], out var local)) + return false; + if (!states.TryGetValue(local, out var state)) + return false; + if (!instrs[index + 1].IsLdcI4() || !instrs[index + 2].IsLdcI4()) + return false; + if (instrs[index + 3].OpCode != OpCodes.Call) + return false; + if (instrs[index + 3].Operand is not IMethod helperRef) + return false; + var helper = helperRef.ResolveMethodDef(); + if (helper == null || !helper.HasBody || helper.MethodSig.Params.Count != 2 || helper.ReturnType.ElementType != ElementType.U4) + return false; + if (!helper.MethodSig.HasThis) + return false; + if (!TypeEqualityComparer.Instance.Equals(local.Type.ToTypeDefOrRef(), helper.DeclaringType)) + return false; + + var arg1 = (byte)instrs[index + 1].GetLdcI4Value(); + var arg2 = (uint)instrs[index + 2].GetLdcI4Value(); + if (!TryExecuteHelperMethod(helper, state, arg1, arg2, out var result)) + return false; + + instrs[index].OpCode = OpCodes.Ldc_I4; + instrs[index].Operand = unchecked((int)result); + for (int replacementIndex = index + 1; replacementIndex <= index + 3; replacementIndex++) + { + instrs[replacementIndex].OpCode = OpCodes.Nop; + instrs[replacementIndex].Operand = null; + } + endIndex = index; + return true; + } + + private static bool TryGetLdlocaLocal(Instruction instruction, out Local local) + { + local = null!; + if (instruction.Operand is not Local candidate) + return false; + switch (instruction.OpCode.Code) + { + case Code.Ldloca: + case Code.Ldloca_S: + local = candidate; + return true; + default: + return false; + } + } + + private static bool TryExecuteHelperCtor(MethodDef ctor, uint seed, out HelperLocalState state) + { + state = new HelperLocalState(4); + if (!ctor.HasBody) + return false; + + var stack = new Stack(); + foreach (var instruction in ctor.Body.Instructions) + { + switch (instruction.OpCode.Code) + { + case Code.Ldarg_0: + stack.Push(uint.MaxValue); + break; + case Code.Ldarg_1: + stack.Push(seed); + break; + case Code.Ldc_I4: + case Code.Ldc_I4_S: + case Code.Ldc_I4_0: + case Code.Ldc_I4_1: + case Code.Ldc_I4_2: + case Code.Ldc_I4_3: + case Code.Ldc_I4_4: + case Code.Ldc_I4_5: + case Code.Ldc_I4_6: + case Code.Ldc_I4_7: + case Code.Ldc_I4_8: + case Code.Ldc_I4_M1: + stack.Push(unchecked((uint)instruction.GetLdcI4Value())); + break; + case Code.Mul: + { + var right = stack.Pop(); + var left = stack.Pop(); + stack.Push(unchecked(left * right)); + break; + } + case Code.Dup: + stack.Push(stack.Peek()); + break; + case Code.Starg: + case Code.Starg_S: + seed = stack.Peek(); + break; + case Code.Stfld: + { + var value = stack.Pop(); + stack.Pop(); + if (instruction.Operand is not FieldDef field) + return false; + int fieldIndex = ctor.DeclaringType.Fields.IndexOf(field); + if (fieldIndex < 0 || fieldIndex >= state.Fields.Length) + return false; + state.Fields[fieldIndex] = value; + break; + } + case Code.Ret: + return true; + default: + return false; + } + } + + return false; + } + + private static bool TryExecuteHelperMethod(MethodDef helper, HelperLocalState state, byte arg1, uint arg2, out uint result) + { + result = 0; + if (!helper.HasBody) + return false; + + int lowBits = arg1 & 3; + if ((arg1 & 0x80) != 0) + { + if (lowBits < 0 || lowBits >= state.Fields.Length) + return false; + state.Fields[lowBits] = arg2; + } + else + { + if (lowBits < 0 || lowBits >= state.Fields.Length) + return false; + state.Fields[lowBits] = lowBits switch + { + 0 => state.Fields[lowBits] ^ arg2, + 1 => unchecked(state.Fields[lowBits] + arg2), + 2 => state.Fields[lowBits] ^ arg2, + 3 => unchecked(state.Fields[lowBits] - arg2), + _ => state.Fields[lowBits] + }; + } + + int returnIndex = (arg1 >> 2) & 3; + if (returnIndex < 0 || returnIndex >= state.Fields.Length) + return false; + result = state.Fields[returnIndex]; + return true; + } + + private sealed class HelperLocalState + { + public HelperLocalState(int size) + { + Fields = new uint[size]; + } + + public uint[] Fields { get; } + } + protected int GetNextInstanceInMethod(MethodDef getter, MethodDef method, out TypeSig? genericType) + { + return GetNextInstanceInMethod(getter, method, 0, out genericType); + } + + protected int GetNextInstanceInMethod(MethodDef getter, MethodDef method, int startIndex, out TypeSig? genericType) { var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count; i++) + for (int i = Math.Max(0, startIndex); i < instrs.Count; i++) { if (instrs[i].OpCode == OpCodes.Call && instrs[i].Operand is MethodSpec ms && @@ -192,33 +439,59 @@ namespace UnConfuserEx.Protections.Constants { genericType = ms.GenericInstMethodSig.GenericArguments[0]; - if (instrs[i - 1].IsBr() && - instrs[i - 1].Operand is Instruction target && - target == instrs[i]) - { - method.Body.Instructions.RemoveAt(i - 1); - i--; - } - - return i - 1; - } + if (i > 0 && instrs[i - 1].IsBr() && + instrs[i - 1].Operand is Instruction target && + target == instrs[i]) + { + instrs[i - 1].OpCode = OpCodes.Nop; + instrs[i - 1].Operand = null; + } + + return i - 1; + } } genericType = null; return -1; } + protected bool TryValidateDataRange(int id, int size, out int normalizedId) + { + normalizedId = id; + if (data == null) + return false; + if (id < 0) + return false; + if (size < 0) + return false; + if (id > data.Length - size) + return false; + return true; + } + + protected static bool CanReplaceConstant(MethodDef method, int instrOffset) + { + return instrOffset >= 0 && instrOffset + 1 < method.Body.Instructions.Count; + } + protected void FixStringConstant(MethodDef method, int instrOffset, int id) { + if (!CanReplaceConstant(method, instrOffset) || !TryValidateDataRange(id, 4, out _)) + throw new IndexOutOfRangeException("String constant header is outside the decrypted blob"); + uint count = (uint)(data![id] | (data[id + 1] << 8) | (data[id + 2] << 16) | (data[id + 3] << 24)); if (count > data.Length) { count = (count << 4) | (count >> 0x1C); } - string result = string.Intern(Encoding.UTF8.GetString(data, id + 4, (int)count)); + if (count > data.Length || id > data.Length - 4 - count) + throw new IndexOutOfRangeException("String constant payload is outside the decrypted blob"); - method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldstr; - method.Body.Instructions[instrOffset].Operand = result; - method.Body.Instructions.RemoveAt(instrOffset + 1); + string result = string.Intern(Encoding.UTF8.GetString(data, id + 4, (int)count)); + + method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldstr; + method.Body.Instructions[instrOffset].Operand = result; + method.Body.Instructions[instrOffset + 1].OpCode = OpCodes.Nop; + method.Body.Instructions[instrOffset + 1].Operand = null; } protected void FixNumberConstant(MethodDef method, int instrOffset, int id, TypeSig type) @@ -245,15 +518,23 @@ namespace UnConfuserEx.Protections.Constants protected void FixNumberConstant(MethodDef method, int instrOffset, int id) { - T[] array = new T[1]; - Buffer.BlockCopy(data!, id, array, 0, Marshal.SizeOf(default(T))); + int size = Marshal.SizeOf(default(T)); + if (!CanReplaceConstant(method, instrOffset) || !TryValidateDataRange(id, size, out _)) + throw new IndexOutOfRangeException("Number constant is outside the decrypted blob"); - method.Body.Instructions[instrOffset].Operand = array[0]; - method.Body.Instructions.RemoveAt(instrOffset + 1); + T[] array = new T[1]; + Buffer.BlockCopy(data!, id, array, 0, size); + + method.Body.Instructions[instrOffset].Operand = array[0]; + method.Body.Instructions[instrOffset + 1].OpCode = OpCodes.Nop; + method.Body.Instructions[instrOffset + 1].Operand = null; } protected void FixObjectConstant(MethodDef method, int instrOffset, int id, TypeSig type) { + if (!CanReplaceConstant(method, instrOffset) || !TryValidateDataRange(id, 8, out _)) + throw new IndexOutOfRangeException("Object constant header is outside the decrypted blob"); + int num0 = data![id] | (data[id + 1] << 8) | (data[id + 2] << 16) | (data[id + 3] << 24); uint num1 = (uint)(data![id + 4] | (data[id + 5] << 8) | (data[id + 6] << 16) | (data[id + 7] << 24)); num1 = (num1 << 4) | (num1 >> 0x1C); @@ -263,9 +544,13 @@ namespace UnConfuserEx.Protections.Constants protected void FixDefaultConstant(MethodDef method, int instrOffset, TypeSig type) { - method.Body.Instructions[instrOffset].OpCode = OpCodes.Initobj; - method.Body.Instructions[instrOffset].Operand = type.ToTypeDefOrRef(); - method.Body.Instructions.RemoveAt(instrOffset + 1); - } + if (!CanReplaceConstant(method, instrOffset)) + throw new IndexOutOfRangeException("Default constant replacement is outside the method body"); + + method.Body.Instructions[instrOffset].OpCode = OpCodes.Initobj; + method.Body.Instructions[instrOffset].Operand = type.ToTypeDefOrRef(); + method.Body.Instructions[instrOffset + 1].OpCode = OpCodes.Nop; + method.Body.Instructions[instrOffset + 1].Operand = null; + } } } diff --git a/UnConfuserEx/Protections/Constants/NormalResolver.cs b/UnConfuserEx/Protections/Constants/NormalResolver.cs index 9eeb472..b871a5b 100644 --- a/UnConfuserEx/Protections/Constants/NormalResolver.cs +++ b/UnConfuserEx/Protections/Constants/NormalResolver.cs @@ -35,6 +35,8 @@ namespace UnConfuserEx.Protections.Constants new ConstantsCFG(method).RemoveFromMethod(); } + SimplifyStatefulHelperCalls(method); + TypeSig? genericType; int instanceOffset = GetNextInstanceInMethod(getter, method, out genericType); @@ -48,8 +50,8 @@ namespace UnConfuserEx.Protections.Constants } catch (Exception ex) { - Logger.Error($"Failed to compute id at offset {instanceOffset} in {method.FullName} ({ex.Message})"); - instanceOffset = GetNextInstanceInMethod(getter, method, out genericType); + Logger.Debug($"Skipping constant at offset {instanceOffset} in {method.FullName} ({ex.Message})"); + instanceOffset = GetNextInstanceInMethod(getter, method, instanceOffset + 2, out genericType); continue; } @@ -61,11 +63,11 @@ namespace UnConfuserEx.Protections.Constants try { - if (type == stringId) + if (IsStringType(genericType!)) { FixStringConstant(method, instanceOffset, id); } - else if (type == numId) + else if (IsSupportedNumberType(genericType!)) { FixNumberConstant(method, instanceOffset, id, genericType!); } @@ -73,15 +75,30 @@ namespace UnConfuserEx.Protections.Constants { FixObjectConstant(method, instanceOffset, id, genericType!); } + else if (type == stringId) + { + FixStringConstant(method, instanceOffset, id); + } + else if (type == numId) + { + FixNumberConstant(method, instanceOffset, id, genericType!); + } else { FixDefaultConstant(method, instanceOffset, genericType!); } } + catch (NotImplementedException ex) when (ex.Message == "Object constant not handled") + { + Logger.Debug($"Skipping unsupported object constant in method ${method.FullName}"); + instanceOffset = GetNextInstanceInMethod(getter, method, instanceOffset + 2, out genericType); + continue; + } catch (Exception ex) { - Logger.Error($"Failed to remove constants obfuscation from method ${method.FullName} ({ex.Message})"); - return; + Logger.Debug($"Skipping constant in method ${method.FullName} ({ex.Message})"); + instanceOffset = GetNextInstanceInMethod(getter, method, instanceOffset + 2, out genericType); + continue; } diff --git a/UnConfuserEx/Protections/Constants/X86Resolver.cs b/UnConfuserEx/Protections/Constants/X86Resolver.cs index ce771a5..ff95188 100644 --- a/UnConfuserEx/Protections/Constants/X86Resolver.cs +++ b/UnConfuserEx/Protections/Constants/X86Resolver.cs @@ -34,6 +34,8 @@ namespace UnConfuserEx.Protections.Constants new ConstantsCFG(method).RemoveFromMethod(); } + SimplifyStatefulHelperCalls(method); + TypeSig? genericType; int instanceOffset = GetNextInstanceInMethod(getter, method, out genericType); @@ -45,8 +47,8 @@ namespace UnConfuserEx.Protections.Constants } catch (Exception ex) { - Logger.Error($"Failed to compute id at offset {instanceOffset} in {method.FullName} ({ex.Message})"); - instanceOffset = GetNextInstanceInMethod(getter, method, out genericType); + Logger.Debug($"Skipping constant at offset {instanceOffset} in {method.FullName} ({ex.Message})"); + instanceOffset = GetNextInstanceInMethod(getter, method, instanceOffset + 2, out genericType); continue; } @@ -59,11 +61,11 @@ namespace UnConfuserEx.Protections.Constants try { - if (type == stringId) + if (IsStringType(genericType!)) { FixStringConstant(method, instanceOffset, id); } - else if (type == numId) + else if (IsSupportedNumberType(genericType!)) { FixNumberConstant(method, instanceOffset, id, genericType!); } @@ -71,15 +73,30 @@ namespace UnConfuserEx.Protections.Constants { FixObjectConstant(method, instanceOffset, id, genericType!); } + else if (type == stringId) + { + FixStringConstant(method, instanceOffset, id); + } + else if (type == numId) + { + FixNumberConstant(method, instanceOffset, id, genericType!); + } else { FixDefaultConstant(method, instanceOffset, genericType!); } } + catch (NotImplementedException ex) when (ex.Message == "Object constant not handled") + { + Logger.Debug($"Skipping unsupported object constant in method ${method.FullName}"); + instanceOffset = GetNextInstanceInMethod(getter, method, instanceOffset + 2, out genericType); + continue; + } catch (Exception ex) { - Logger.Error($"Failed to remove constants obfuscation from method ${method.FullName} ({ex.Message})"); - return; + Logger.Debug($"Skipping constant in method ${method.FullName} ({ex.Message})"); + instanceOffset = GetNextInstanceInMethod(getter, method, instanceOffset + 2, out genericType); + continue; } diff --git a/UnConfuserEx/Protections/Renamer/UnicodeRemover.cs b/UnConfuserEx/Protections/Renamer/UnicodeRemover.cs index 9d45afc..098b9b7 100644 --- a/UnConfuserEx/Protections/Renamer/UnicodeRemover.cs +++ b/UnConfuserEx/Protections/Renamer/UnicodeRemover.cs @@ -1,5 +1,6 @@ using dnlib.DotNet; using dnlib.DotNet.Writer; +using log4net; using System; using System.Collections.Generic; using System.IO; @@ -12,6 +13,8 @@ namespace UnConfuserEx.Protections { internal class UnicodeRemover : IProtection { + private static readonly ILog Logger = LogManager.GetLogger("Renamer"); + public string Name => "Renamer"; private ModuleDefMD? Module = null; @@ -79,24 +82,43 @@ namespace UnConfuserEx.Protections private void RenameMemberRef(IMemberRef memberRef) { - if (Utils.IsInvalidName(memberRef.Name)) + if (!Utils.IsInvalidName(memberRef.Name)) + return; + + TypeDef? declaringType = null; + try { - var declaringType = memberRef.DeclaringType.ResolveTypeDefThrow(); - var typeInfo = NewTypeInfo[declaringType]; + declaringType = memberRef.DeclaringType.ResolveTypeDef(); + } + catch (TypeResolveException ex) + { + Logger.Warn($"Skipping unresolved member ref {memberRef.FullName} ({ex.Message})"); + return; + } - if (memberRef.IsField) - { - memberRef.Name = typeInfo.FieldNames[memberRef.Name]; - } - else if (memberRef.IsMethod) - { - memberRef.Name = typeInfo.MethodNames[memberRef.Name]; - } + if (declaringType == null || !NewTypeInfo.TryGetValue(declaringType, out var typeInfo)) + { + Logger.Warn($"Skipping member ref with unresolved declaring type {memberRef.FullName}"); + return; + } + + if (memberRef.IsField) + { + if (typeInfo.FieldNames.TryGetValue(memberRef.Name, out var fieldName)) + memberRef.Name = fieldName; else - { - throw new NotImplementedException(); - } - + Logger.Warn($"Skipping unknown field ref {memberRef.FullName}"); + } + else if (memberRef.IsMethod) + { + if (typeInfo.MethodNames.TryGetValue(memberRef.Name, out var methodName)) + memberRef.Name = methodName; + else + Logger.Warn($"Skipping unknown method ref {memberRef.FullName}"); + } + else + { + Logger.Warn($"Skipping unsupported member ref {memberRef.FullName}"); } } } diff --git a/UnConfuserEx/Protections/Resources/ResourcesRemover.cs b/UnConfuserEx/Protections/Resources/ResourcesRemover.cs index 74ce056..827894a 100644 --- a/UnConfuserEx/Protections/Resources/ResourcesRemover.cs +++ b/UnConfuserEx/Protections/Resources/ResourcesRemover.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Resources; +using System.Resources.Extensions; using UnConfuserEx.Protections.Resources; namespace UnConfuserEx.Protections @@ -337,9 +338,9 @@ namespace UnConfuserEx.Protections { bool changed = false; using var output = new MemoryStream(); - using var writer = new ResourceWriter(output); + using var writer = new PreserializedResourceWriter(output); using var stream = new MemoryStream(bytes, writable: false); - using var reader = new ResourceReader(stream); + using var reader = new DeserializingResourceReader(stream); var enumerator = reader.GetEnumerator(); while (enumerator.MoveNext()) { @@ -443,6 +444,8 @@ namespace UnConfuserEx.Protections if (!TryDescribePortableExecutable(data, out description)) description = module.IsILOnly ? "managed PE" : "mixed-mode PE"; + UnConfuserEx.PrepareModuleForWrite(module); + if (module.IsILOnly) { var writerOptions = new ModuleWriterOptions(module); diff --git a/UnConfuserEx/RuntimeOptions.cs b/UnConfuserEx/RuntimeOptions.cs index 12660e3..7b3623c 100644 --- a/UnConfuserEx/RuntimeOptions.cs +++ b/UnConfuserEx/RuntimeOptions.cs @@ -3,5 +3,6 @@ namespace UnConfuserEx internal static class RuntimeOptions { public static bool RebuildEmbeddedPe { get; set; } + public static bool EnableSerializedResourceDeserialization { get; set; } } } diff --git a/UnConfuserEx/UnConfuserEx.cs b/UnConfuserEx/UnConfuserEx.cs index cedeb2a..2b45171 100644 --- a/UnConfuserEx/UnConfuserEx.cs +++ b/UnConfuserEx/UnConfuserEx.cs @@ -12,6 +12,7 @@ using UnConfuserEx.Protections.AntiDebug; using UnConfuserEx.Protections.AntiDump; using UnConfuserEx.Protections.AntiTamper; using UnConfuserEx.Protections.Compressor; +using dnlib.DotNet.Emit; namespace UnConfuserEx { @@ -19,6 +20,27 @@ namespace UnConfuserEx { static ILog Logger = LogManager.GetLogger("UnConfuserEx"); + internal static void NormalizeMethodBodiesForWrite(ModuleDef module) + { + foreach (var type in module.GetTypes()) + { + foreach (var method in type.Methods) + { + var body = method.Body; + if (body == null) + continue; + + body.SimplifyBranches(); + body.OptimizeBranches(); + } + } + } + + internal static void PrepareModuleForWrite(ModuleDef module) + { + NormalizeMethodBodiesForWrite(module); + } + sealed class FailureInfo { public required string ProtectionName { get; init; } @@ -60,6 +82,7 @@ namespace UnConfuserEx string? pathArg = null; string? outputArg = null; RuntimeOptions.RebuildEmbeddedPe = false; + RuntimeOptions.EnableSerializedResourceDeserialization = false; foreach (var arg in args) { @@ -69,10 +92,16 @@ namespace UnConfuserEx continue; } + if (arg.Equals("--enable-serialized-resources", StringComparison.OrdinalIgnoreCase)) + { + RuntimeOptions.EnableSerializedResourceDeserialization = true; + continue; + } + if (arg.StartsWith("-", StringComparison.Ordinal)) { Logger.Error($"Unknown option: {arg}"); - Logger.Error("Usage: unconfuser.exe [--rebuild-embedded-pe]"); + Logger.Error("Usage: unconfuser.exe [--rebuild-embedded-pe] [--enable-serialized-resources]"); return 1; } @@ -86,17 +115,20 @@ namespace UnConfuserEx } else { - Logger.Error("Usage: unconfuser.exe [--rebuild-embedded-pe]"); + Logger.Error("Usage: unconfuser.exe [--rebuild-embedded-pe] [--enable-serialized-resources]"); return 1; } } if (pathArg is null) { - Logger.Error("Usage: unconfuser.exe [--rebuild-embedded-pe]"); + Logger.Error("Usage: unconfuser.exe [--rebuild-embedded-pe] [--enable-serialized-resources]"); return 1; } + if (RuntimeOptions.EnableSerializedResourceDeserialization) + AppContext.SetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", true); + var path = Path.GetFullPath(pathArg); if (!File.Exists(path)) { @@ -257,6 +289,8 @@ namespace UnConfuserEx try { + PrepareModuleForWrite(module); + if (module.IsILOnly) { ModuleWriterOptions writerOptions = new ModuleWriterOptions(module); diff --git a/UnConfuserEx/UnConfuserEx.csproj b/UnConfuserEx/UnConfuserEx.csproj index dcb98d2..2ee3d74 100644 --- a/UnConfuserEx/UnConfuserEx.csproj +++ b/UnConfuserEx/UnConfuserEx.csproj @@ -19,6 +19,7 @@ +