From da95fd4737be2be71012f09671de1365ec1c1645 Mon Sep 17 00:00:00 2001 From: Harry Stoltz Date: Sat, 8 Apr 2023 19:58:43 +0100 Subject: [PATCH] Add support for expression predicate in switch obfuscation --- MSILEmulator/ILMethod.cs | 7 +-- .../Protections/Constants/DynamicDecryptor.cs | 9 +--- .../ControlFlow/ControlFlowRemover.cs | 5 +++ .../ControlFlow/SwitchDeobfuscator.cs | 45 +++++++++++++++---- UnConfuserEx/UnConfuserEx.cs | 2 + UnConfuserEx/Utils.cs | 24 ++++++++++ 6 files changed, 70 insertions(+), 22 deletions(-) diff --git a/MSILEmulator/ILMethod.cs b/MSILEmulator/ILMethod.cs index ee5133b..2e20358 100644 --- a/MSILEmulator/ILMethod.cs +++ b/MSILEmulator/ILMethod.cs @@ -30,7 +30,7 @@ namespace MSILEmulator Ctx = new(); } - public ILMethod(IList instructions) + public ILMethod(IEnumerable instructions) { Method = null; Instructions = instructions.ToList(); @@ -47,11 +47,6 @@ namespace MSILEmulator Ctx.Locals[index] = value; } - public void Reset() - { - Ctx = new(); - } - public Context Emulate() { diff --git a/UnConfuserEx/Protections/Constants/DynamicDecryptor.cs b/UnConfuserEx/Protections/Constants/DynamicDecryptor.cs index d0843df..86edd45 100644 --- a/UnConfuserEx/Protections/Constants/DynamicDecryptor.cs +++ b/UnConfuserEx/Protections/Constants/DynamicDecryptor.cs @@ -26,14 +26,9 @@ namespace UnConfuserEx.Protections.Constants { if (decryptInstructions[i + 2].OpCode == OpCodes.Ldelem_U4) { - if (decryptInstructions[i].OpCode == OpCodes.Ldloc_S) + if (decryptInstructions[i].IsLdloc()) { - arrays.Add(((Local)decryptInstructions[i].Operand).Index); - } - else if (decryptInstructions[i].OpCode.Value >= OpCodes.Ldloc_0.Value - && decryptInstructions[i].OpCode.Value <= OpCodes.Ldloc_3.Value) - { - arrays.Add(decryptInstructions[i].OpCode.Value - OpCodes.Ldloc_0.Value); + arrays.Add(Utils.GetLoadLocalIndex(decryptInstructions[i])); } } } diff --git a/UnConfuserEx/Protections/ControlFlow/ControlFlowRemover.cs b/UnConfuserEx/Protections/ControlFlow/ControlFlowRemover.cs index b328235..d949e9a 100644 --- a/UnConfuserEx/Protections/ControlFlow/ControlFlowRemover.cs +++ b/UnConfuserEx/Protections/ControlFlow/ControlFlowRemover.cs @@ -105,6 +105,11 @@ namespace UnConfuserEx.Protections public static bool IsSwitchObfuscation(List instrs) { + if (instrs.Count < 3) + { + return false; + } + for (int i = 0; i < instrs.Count; i++) { if (instrs[i].OpCode == OpCodes.Switch diff --git a/UnConfuserEx/Protections/ControlFlow/SwitchDeobfuscator.cs b/UnConfuserEx/Protections/ControlFlow/SwitchDeobfuscator.cs index d9766bb..d272951 100644 --- a/UnConfuserEx/Protections/ControlFlow/SwitchDeobfuscator.cs +++ b/UnConfuserEx/Protections/ControlFlow/SwitchDeobfuscator.cs @@ -3,6 +3,7 @@ using de4dot.blocks.cflow; using dnlib.DotNet; using dnlib.DotNet.Emit; using log4net; +using MSILEmulator; using System.Collections.Generic; using System.Linq; using X86Emulator; @@ -22,6 +23,9 @@ namespace UnConfuserEx.Protections.ControlFlow private int methodOffset = -1; private X86Method? x86Method = null; + private ILMethod? ilMethod = null; + private int? keyIndex = null; + public SwitchDeobfuscator(ModuleDefMD module) { Module = module; @@ -68,6 +72,16 @@ namespace UnConfuserEx.Protections.ControlFlow methodOffset = i - 5; x86Method = new X86Method(Module, (MethodDef)instrs[i - 5].Operand); } + else if (!switchBlock.FirstInstr.Instruction.IsLdcI4()) + { + var decodeInstrsLength = switchBlock.Instructions.Count - 6; + + ilMethod = new ILMethod(switchBlock.Instructions + .Skip(1) + .Take(decodeInstrsLength) + .Select(instr => instr.Instruction)); + keyIndex = Utils.GetStoreLocalIndex(switchBlock.FirstInstr.Instruction); + } return true; } @@ -95,9 +109,12 @@ namespace UnConfuserEx.Protections.ControlFlow { modified = false; - // Sometimes de4dot doesn't split the ldc.i4 going into the switchblock - // so we need to handle that in a special case - if (switchBlock!.FirstInstr.IsLdcI4()) + + // Sometimes with the x86 predicate, the ldc.i4 going into the switch block + // doesn't get split into a separate block which can cause issues + + // TODO: Not entirely convinced this solution won't cause issues... + if (x86Method != null && switchBlock!.FirstInstr.IsLdcI4()) { var key = switchBlock.FirstInstr.GetLdcI4Value(); var (nextKey, nextCase) = GetNextKeyAndCase(key); @@ -218,17 +235,27 @@ namespace UnConfuserEx.Protections.ControlFlow private (int nextKey, int nextCase) GetNextKeyAndCase(int key) { - if (x86Method == null) + // x86 predicate + if (x86Method != null) + { + var nextKey = x86Method.Emulate(new int[] { key }); + return (nextKey, nextKey % switchCases.Count); + } + // Expression Predicate + else if (ilMethod != null) + { + ilMethod.SetLocal((int)keyIndex!, key); + + var nextKey = (int)ilMethod.Emulate().Stack.Pop(); + return (nextKey, nextKey % switchCases.Count); + } + // Normal predicate + else { var xorKey = switchBlock!.FirstInstr.GetLdcI4Value(); var nextKey = key ^ xorKey; return (nextKey, nextKey % switchCases.Count); } - else - { - var nextKey = x86Method.Emulate(new int[] { key }); - return (nextKey, nextKey % switchCases.Count); - } } private void UpdateKeyInBlock(Block block, int key) diff --git a/UnConfuserEx/UnConfuserEx.cs b/UnConfuserEx/UnConfuserEx.cs index 870dc61..ce99248 100644 --- a/UnConfuserEx/UnConfuserEx.cs +++ b/UnConfuserEx/UnConfuserEx.cs @@ -39,6 +39,8 @@ namespace UnConfuserEx var pipeline = new List { + new ControlFlowRemover(), + // If this is present, it MUST be removed first new AntiTamperRemover(), diff --git a/UnConfuserEx/Utils.cs b/UnConfuserEx/Utils.cs index fa66f0c..7ecdef1 100644 --- a/UnConfuserEx/Utils.cs +++ b/UnConfuserEx/Utils.cs @@ -52,5 +52,29 @@ namespace UnConfuserEx return Encoding.UTF8.GetByteCount(name) != name.Length || (name.Any(c => InvalidChars.Contains(c))); } + + public static int GetStoreLocalIndex(Instruction instr) + { + if (instr.OpCode == OpCodes.Stloc_S || instr.OpCode == OpCodes.Stloc) + { + return ((Local)instr.Operand).Index; + } + else + { + return instr.OpCode.Code - Code.Stloc_0; + } + } + + public static int GetLoadLocalIndex(Instruction instr) + { + if (instr.OpCode == OpCodes.Ldloc_S || instr.OpCode == OpCodes.Ldloc) + { + return ((Local)instr.Operand).Index; + } + else + { + return instr.OpCode.Code - Code.Ldloc_0; + } + } } }