Add support for expression predicate in switch obfuscation

This commit is contained in:
Harry Stoltz
2023-04-08 19:58:43 +01:00
parent b18778beff
commit da95fd4737
6 changed files with 70 additions and 22 deletions
+1 -6
View File
@@ -30,7 +30,7 @@ namespace MSILEmulator
Ctx = new();
}
public ILMethod(IList<Instruction> instructions)
public ILMethod(IEnumerable<Instruction> instructions)
{
Method = null;
Instructions = instructions.ToList();
@@ -47,11 +47,6 @@ namespace MSILEmulator
Ctx.Locals[index] = value;
}
public void Reset()
{
Ctx = new();
}
public Context Emulate()
{
@@ -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]));
}
}
}
@@ -105,6 +105,11 @@ namespace UnConfuserEx.Protections
public static bool IsSwitchObfuscation(List<Instruction> instrs)
{
if (instrs.Count < 3)
{
return false;
}
for (int i = 0; i < instrs.Count; i++)
{
if (instrs[i].OpCode == OpCodes.Switch
@@ -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)
+2
View File
@@ -39,6 +39,8 @@ namespace UnConfuserEx
var pipeline = new List<IProtection>
{
new ControlFlowRemover(),
// If this is present, it MUST be removed first
new AntiTamperRemover(),
+24
View File
@@ -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;
}
}
}
}