From 16c43b206986d0475249b0faa30d2d3a612035a7 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Sun, 26 Feb 2023 20:53:57 +0300 Subject: [PATCH 01/14] Mentioned in the ReadMe other deobfuscators implemented --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index bd2a5f591..2eac4b16a 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,12 @@ Supported obfuscators/packers * Spices.Net * Xenocode +There are some other obfuscators/packers that are not in supported list explicitly, so their status is unclear: + +* Confuser +* Osu +* PCL + Some of the above obfuscators are rarely used (eg. Goliath.NET), so they have had much less testing. Help me out by reporting bugs or problems you find. Warning From cbc27851153937aa9da5be6ea45b254972ddd011 Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Mon, 13 Feb 2017 11:14:22 +0200 Subject: [PATCH 02/14] Implemented ConfuserEx deobfuscator x86 cflow and x86 constant decryption Backport of LINQ (LinqBridge) Shift left/right emulation fixes in de4dot core Block class extended to hold additional information --- de4dot.blocks/cflow/Int32Value.cs | 12 +- .../ConfuserEx/ConstantDecrypter.cs | 663 +++++++++++++++++ .../ConfuserEx/ConstantInliner.cs | 181 +++++ .../ConfuserEx/ControlFlowSolver.cs | 322 ++++++++ .../deobfuscators/ConfuserEx/Deobfuscator.cs | 278 +++++++ .../deobfuscators/ConfuserEx/LzmaFinder.cs | 198 +++++ .../ConfuserEx/ResourceDecrypter.cs | 293 ++++++++ de4dot.code/deobfuscators/ConfuserEx/Utils.cs | 278 +++++++ .../ConfuserEx/x86/Bea/Constants.cs | 191 +++++ .../ConfuserEx/x86/Bea/Engine.cs | 32 + .../ConfuserEx/x86/Bea/Structs.cs | 107 +++ .../ConfuserEx/x86/Instructions/X86ADD.cs | 27 + .../ConfuserEx/x86/Instructions/X86DIV.cs | 22 + .../ConfuserEx/x86/Instructions/X86IMUL.cs | 29 + .../ConfuserEx/x86/Instructions/X86MOV.cs | 33 + .../ConfuserEx/x86/Instructions/X86NEG.cs | 22 + .../ConfuserEx/x86/Instructions/X86NOT.cs | 23 + .../ConfuserEx/x86/Instructions/X86POP.cs | 26 + .../ConfuserEx/x86/Instructions/X86PUSH.cs | 26 + .../ConfuserEx/x86/Instructions/X86SUB.cs | 27 + .../ConfuserEx/x86/Instructions/X86XOR.cs | 27 + .../ConfuserEx/x86/UnmanagedBuff.cs | 18 + .../ConfuserEx/x86/X86Instruction.cs | 72 ++ .../deobfuscators/ConfuserEx/x86/X86Method.cs | 158 ++++ de4dot.code/deobfuscators/Lzma.cs | 688 ++++++++++++++++++ 25 files changed, 3747 insertions(+), 6 deletions(-) create mode 100644 de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/Utils.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs create mode 100644 de4dot.code/deobfuscators/Lzma.cs diff --git a/de4dot.blocks/cflow/Int32Value.cs b/de4dot.blocks/cflow/Int32Value.cs index 7d111fdf2..6e8ba8337 100644 --- a/de4dot.blocks/cflow/Int32Value.cs +++ b/de4dot.blocks/cflow/Int32Value.cs @@ -450,8 +450,8 @@ public static Int32Value Shl(Int32Value a, Int32Value b) { return CreateUnknown(); if (b.Value == 0) return a; - if (b.Value < 0 || b.Value >= sizeof(int) * 8) - return CreateUnknown(); + //if (b.Value < 0 || b.Value >= sizeof(int) * 8) + // return CreateUnknown(); int shift = b.Value; uint validMask = (a.ValidMask << shift) | (uint.MaxValue >> (sizeof(int) * 8 - shift)); return new Int32Value(a.Value << shift, validMask); @@ -462,8 +462,8 @@ public static Int32Value Shr(Int32Value a, Int32Value b) { return CreateUnknown(); if (b.Value == 0) return a; - if (b.Value < 0 || b.Value >= sizeof(int) * 8) - return CreateUnknown(); + //if (b.Value < 0 || b.Value >= sizeof(int) * 8) + // return CreateUnknown(); int shift = b.Value; uint validMask = a.ValidMask >> shift; if (a.IsBitValid(sizeof(int) * 8 - 1)) @@ -476,8 +476,8 @@ public static Int32Value Shr_Un(Int32Value a, Int32Value b) { return CreateUnknown(); if (b.Value == 0) return a; - if (b.Value < 0 || b.Value >= sizeof(int) * 8) - return CreateUnknown(); + //if (b.Value < 0 || b.Value >= sizeof(int) * 8) + // return CreateUnknown(); int shift = b.Value; uint validMask = (a.ValidMask >> shift) | (uint.MaxValue << (sizeof(int) * 8 - shift)); return new Int32Value((int)((uint)a.Value >> shift), validMask); diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs new file mode 100644 index 000000000..bb001de13 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs @@ -0,0 +1,663 @@ +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using de4dot.blocks.cflow; +using de4dot.code.deobfuscators.ConfuserEx.x86; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + public class ConstantDecrypterBase + { + private X86Method _nativeMethod; + private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); + + private int? CalculateKey() + { + var popValue = _instructionEmulator.Peek(); + + if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid()) + return null; + + _instructionEmulator.Pop(); + int result = _nativeMethod.Execute(((Int32Value)popValue).Value); + return result; + } + + public MethodDef Method { get; set; } + public MethodDef NativeMethod { get; set; } + public byte[] Decrypted { get; set; } + public uint Magic1 { get; set; } + public uint Magic2 { get; set; } + public bool CanRemove { get; set; } = true; + + private uint CalculateMagic(uint index) + { + _instructionEmulator.Push(new Int32Value((int)index)); + _nativeMethod = new X86Method(NativeMethod, Method.Module as ModuleDefMD); //TODO: Possible null + int? key = CalculateKey(); + + uint uint_0 = (uint) key.Value; + uint_0 &= 0x3fffffff; + uint_0 <<= 2; + return uint_0; + } + public string DecryptString(uint index) + { + index = CalculateMagic(index); + int count = BitConverter.ToInt32(Decrypted, (int)index); + return string.Intern(Encoding.UTF8.GetString(Decrypted, (int)index + 4, count)); + } + public T DecryptConstant(uint index) + { + index = CalculateMagic(index); + T[] array = new T[1]; + Buffer.BlockCopy(Decrypted, (int)index, array, 0, Marshal.SizeOf(typeof(T))); + return array[0]; + } + public byte[] DecryptArray(uint index) + { + index = CalculateMagic(index); + int count = BitConverter.ToInt32(Decrypted, (int)index); + //int lengt = BitConverter.ToInt32(Decrypted, (int)index+4); we actualy dont need that + byte[] buffer = new byte[count - 4]; + Buffer.BlockCopy(Decrypted, (int)index + 8, buffer, 0, count - 4); + return buffer; + } + } + + public class ConstantsDecrypter + { + TypeDef arrayType; + MethodDef constantsDecInitMethod; + FieldDef decryptedField, arrayField; + List constantDecrpers = new List(); + byte[] decryptedBytes; + bool canRemoveLzma = true; + + public bool CanRemoveLzma + { + get { return canRemoveLzma; } + } + public TypeDef Type + { + get { return arrayType; } + } + public MethodDef Method + { + get { return constantsDecInitMethod; } + } + public List Fields + { + get { return new List() { decryptedField, arrayField }; } + } + public List Decrypters + { + get { return constantDecrpers; } + } + public bool Detected + { + get { return constantsDecInitMethod != null && decryptedBytes != null && constantDecrpers.Count != 0 && decryptedField != null && arrayField != null; } + } + + ModuleDef module; + MethodDef lzmaMethod; + ISimpleDeobfuscator deobfuscator; + public ConstantsDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator) + { + this.module = module; + this.lzmaMethod = lzmaMethod; + this.deobfuscator = deobfsucator; + } + + public void Find() + { + var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); + if (moduleCctor == null) + return; + foreach (var inst in moduleCctor.Body.Instructions) + { + if (inst.OpCode != OpCodes.Call) + continue; + if (!(inst.Operand is MethodDef)) + continue; + var method = inst.Operand as MethodDef; + if (!method.HasBody || !method.IsStatic) + continue; + if (!DotNetUtils.IsMethod(method, "System.Void", "()")) + continue; + deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + if (!isStrDecryptInit(method)) + continue; + constantsDecInitMethod = method; + FindStringDecrypters(moduleCctor.DeclaringType); + } + } + + bool isStrDecryptInit(MethodDef method) + { + var instructions = method.Body.Instructions; + + if (instructions.Count < 15) + return false; + + if (!instructions[0].IsLdcI4()) + return false; + if (!instructions[1].IsStloc()) //uint num = 96u; + return false; + + if (!instructions[2].IsLdcI4()) + return false; + if (instructions[0].GetLdcI4Value() != instructions[2].GetLdcI4Value()) + return false; + if (instructions[3].OpCode != OpCodes.Newarr) + return false; + if (instructions[3].Operand.ToString() != "System.UInt32") + return false; + if (instructions[4].OpCode != OpCodes.Dup) + return false; + if (instructions[5].OpCode != OpCodes.Ldtoken) + return false; + var aField = instructions[5].Operand as FieldDef; + if (aField == null) + return false; + if (aField.InitialValue == null) + return false; + if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA)) + return false; + if (instructions[6].OpCode != OpCodes.Call) + return false; + if (instructions[6].Operand.ToString() != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") + return false; + if (!instructions[7].IsStloc()) // uint[] array = new uint[] {.....}; + return false; + + var l = instructions.Count; + if (!instructions[l - 4].IsLdloc()) + return false; + if (instructions[l - 3].OpCode != OpCodes.Call) + return false; + if (instructions[l - 3].Operand != lzmaMethod) + return false; + if (instructions[l - 2].OpCode != OpCodes.Stsfld) //.byte_0 = .smethod_0(array4); + return false; + var dField = instructions[l - 2].Operand as FieldDef; + if (dField == null) + return false; + try + { + DecryptArray(ConvertArray(aField.InitialValue)); + } + catch (Exception e) { Console.WriteLine(e.Message); canRemoveLzma = false; return false; } + arrayField = aField; + arrayType = DotNetUtils.GetType(module, aField.FieldSig.Type); + decryptedField = dField; + return true; + } + + private T[] ConvertArray(T1[] array) + { + int l = Marshal.SizeOf(typeof(T)); + int l1 = Marshal.SizeOf(typeof(T1)); + var buffer = new T[(array.Length * l1) / l]; + Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); + return buffer; + } + + private void DecryptArray(uint[] array) + { + uint num = 1680u; // array size? + uint[] array2 = new uint[16]; + uint num2 = 3186233426u; + for (int i = 0; i < 16; i++) + { + num2 ^= num2 >> 12; + num2 ^= num2 << 25; + num2 ^= num2 >> 27; + array2[i] = num2; + } + int num3 = 0; + int num4 = 0; + uint[] array3 = new uint[16]; + byte[] array4 = new byte[num * 4u]; + while ((long)num3 < (long)((ulong)num)) + { + for (int j = 0; j < 16; j++) + { + array3[j] = array[num3 + j]; + } + uint num5 = array3[9] >> 23; + array3[0] = array3[0] * 865957733u; + array3[9] = array3[9] << 9; + array3[9] = (array3[9] | num5); + array3[3] = (array3[3] ^ 4272581837u); + array3[8] = (array3[8] ^ array2[8]); + num5 = (array3[14] & 3955221806u); + num5 *= 1105615415u; + array3[14] = (array3[14] & 339745489u); + array3[14] = (array3[14] | (array3[0] & 3955221806u)); + array3[15] = (array3[15] ^ array3[8]); + array3[0] = (array3[0] & 339745489u); + array3[0] = (array3[0] | num5 * 809663367u); + array3[14] = (array3[14] ^ 1753824488u); + num5 = array3[11] << 18; + array3[11] = array3[11] >> 14; + array3[1] = array3[1] - 301755752u; + array3[11] = (array3[11] | num5); + uint num6 = array3[14] << 3; + num5 = (array3[9] & 2370496838u); + array3[10] = array3[10] - 4147007853u; + uint num7 = array3[14] << 4; + num5 *= 2575732745u; + num6 += array3[14] << 4; + array3[9] = (array3[9] & 1924470457u); + array3[9] = (array3[9] | (array3[2] & 2370496838u)); + array3[12] = array3[12] - array3[6]; + array3[2] = (array3[2] & 1924470457u); + array3[0] = array3[0] - array3[4]; + array3[2] = (array3[2] | num5 * 593100345u); + num5 = array3[6] * 993944645u; + array3[6] = array3[5]; + uint num8 = array3[14] * 19u; + array3[5] = num5 * 679153293u; + num5 = (array3[1] & 4141471236u); + num8 += array3[5] * 67u; + num8 += array3[10] * 43u; + array3[13] = (array3[13] ^ array2[13]); + array3[1] = (array3[1] & 153496059u); + num6 += array3[5] * 92u; + num7 += array3[5] * 57u; + num7 += array3[10] * 37u; + num5 *= 831032307u; + array3[1] = (array3[1] | (array3[12] & 4141471236u)); + array3[0] = (array3[0] ^ array2[0]); + array3[12] = (array3[12] & 153496059u); + array3[11] = (array3[11] ^ array2[11]); + num6 += array3[10] << 6; + array3[12] = (array3[12] | num5 * 419693883u); + num7 += array3[15] * 107u; + array3[3] = (array3[3] ^ array2[3]); + num5 = (array3[13] & 2032982899u); + array3[13] = (array3[13] & 2261984396u); + num5 *= 3754449215u; + num8 += array3[15] * 125u; + num6 += array3[15] * 179u; + array3[13] = (array3[13] | (array3[7] & 2032982899u)); + array3[7] = (array3[7] & 2261984396u); + array3[7] = (array3[7] | num5 * 1302730431u); + num5 = array3[14] * 7u; + num5 += array3[5] * 25u; + array3[12] = (array3[12] ^ ~array3[4]); + num5 += array3[10] << 4; + array3[5] = num6; + array3[10] = num7; + num6 = array3[2] >> 19; + num7 = array3[11] >> 19; + num5 += array3[15] * 46u; + array3[15] = num8; + num8 = array3[0] << 2; + array3[2] = array3[2] << 13; + array3[11] = array3[11] << 13; + array3[14] = num5; + array3[0] = array3[0] >> 30; + array3[2] = (array3[2] | num6); + array3[6] = (array3[6] ^ 825592879u); + array3[2] = (array3[2] ^ array2[2]); + array3[11] = (array3[11] | num7); + num5 = array3[15] << 1; + array3[0] = (array3[0] | num8); + num8 = array3[15] * 23u; + num7 = array3[15] * 7u; + num8 += array3[7] * 45u; + num7 += array3[7] * 11u; + num5 += array3[15]; + array3[9] = (array3[9] ^ array2[9]); + num5 += array3[7] * 7u; + num8 += array3[13] << 7; + array3[3] = (array3[3] ^ ~array3[8]); + array3[10] = array3[10] * 2256145475u; + num6 = array3[15] << 2; + num6 += array3[15]; + num7 += array3[13] << 5; + num7 += array3[1] << 1; + num6 += array3[7] << 2; + num6 += array3[7] << 3; + num8 += array3[13]; + num8 += array3[1] * 143u; + num5 += array3[13] << 2; + num6 += array3[13] << 1; + num7 += array3[1] << 5; + num5 += array3[13] << 4; + array3[15] = num7; + num5 += array3[1] * 23u; + num6 += array3[13] << 5; + array3[7] = num5; + num6 += array3[1] * 39u; + array3[1] = num8; + num8 = array3[1] * 26u; + num7 = array3[1] << 6; + array3[7] = (array3[7] ^ array2[7]); + array3[13] = num6; + num5 = array3[1] << 1; + num5 += array3[1] << 3; + num6 = array3[1] * 13u; + num5 += array3[13] << 1; + num7 += array3[1]; + num6 += array3[13] * 45u; + array3[9] = (array3[9] ^ 786150263u); + num8 += array3[13] * 88u; + num6 += array3[2] * 67u; + array3[8] = (array3[8] ^ 110539985u); + num5 += array3[13] << 5; + num6 += array3[11] << 1; + num8 += array3[2] * 133u; + array3[10] = (array3[10] ^ array2[10]); + num8 += array3[11] << 6; + array3[15] = array3[15] - 2992485470u; + array3[0] = array3[0] - array3[6]; + num6 += array3[11] << 5; + num5 += array3[2] * 51u; + num5 += array3[11] * 25u; + array3[12] = (array3[12] ^ ~array3[3]); + num7 += array3[13] * 222u; + array3[13] = num5; + array3[1] = num6; + array3[13] = array3[13] * 3578289835u; + num6 = (array3[10] & 381620437u); + array3[10] = (array3[10] & 3913346858u); + num5 = array3[0] * 14u; + num7 += array3[2] * 333u; + array3[2] = num8; + num5 += array3[3] * 11u; + array3[10] = (array3[10] | (array3[14] & 381620437u)); + array3[7] = (array3[7] ^ ~array3[4]); + num6 *= 3323466531u; + array3[14] = (array3[14] & 3913346858u); + num8 = array3[0] << 2; + num5 += array3[5] * 54u; + array3[14] = (array3[14] | num6 * 1991488651u); + num7 += array3[11] * 164u; + num6 = (array3[2] & 2341248020u); + array3[11] = num7; + num7 = array3[0] * 11u; + num8 += array3[0] << 4; + array3[2] = (array3[2] & 1953719275u); + num8 += array3[3] << 2; + array3[2] = (array3[2] | (array3[11] & 2341248020u)); + num8 += array3[3] << 4; + num6 *= 4030567715u; + array3[14] = (array3[14] ^ array2[14]); + array3[11] = (array3[11] & 1953719275u); + array3[11] = (array3[11] | num6 * 62866059u); + num6 = array3[0] << 2; + num8 += array3[5] * 90u; + num7 += array3[3] << 4; + num7 += array3[5] << 2; + num6 += array3[0]; + array3[12] = (array3[12] ^ array2[12]); + num7 += array3[5] << 6; + num8 += array3[13] * 117u; + array3[9] = (array3[9] ^ array3[5]); + num5 += array3[13] * 52u; + num6 += array3[3] << 1; + num6 += array3[3]; + num7 += array3[13] * 126u; + num6 += array3[5] << 4; + num6 += array3[5]; + array3[5] = num8; + array3[3] = num5; + num6 += array3[13] * 11u; + array3[0] = num6; + array3[13] = num7; + num6 = array3[6] << 1; + num6 += array3[15] << 1; + num5 = array3[12] << 29; + num6 += array3[15] << 2; + num7 = array3[7] << 12; + array3[11] = array3[11] - array3[10]; + array3[7] = array3[7] >> 20; + array3[12] = array3[12] >> 3; + array3[12] = (array3[12] | num5); + array3[14] = (array3[14] ^ ~array3[8]); + array3[1] = (array3[1] ^ array2[1]); + array3[1] = (array3[1] ^ 3215842197u); + num8 = array3[6] * 7u; + array3[7] = (array3[7] | num7); + num8 += array3[15] * 26u; + num5 = array3[6] << 2; + num5 += array3[15] << 2; + array3[9] = array3[9] - array3[2]; + num7 = array3[6] << 2; + array3[4] = (array3[4] ^ array2[4]); + num6 += array3[4] << 4; + array3[3] = (array3[3] ^ 1425746098u); + num5 += array3[15]; + num8 += array3[4] * 69u; + num5 += array3[4] * 15u; + num7 += array3[6]; + num6 += array3[1] * 15u; + num8 += array3[1] * 63u; + array3[6] = num6; + num7 += array3[15] * 11u; + num7 += array3[4] * 31u; + num7 += array3[1] * 30u; + num5 += array3[1] << 4; + array3[5] = (array3[5] ^ array2[5]); + array3[4] = num7; + num7 = (array3[5] & 2375297997u); + array3[6] = (array3[6] ^ array2[6]); + num7 *= 3574473459u; + array3[15] = num5; + array3[5] = (array3[5] & 1919669298u); + array3[5] = (array3[5] | (array3[13] & 2375297997u)); + array3[1] = num8; + array3[15] = (array3[15] ^ array2[15]); + num8 = array3[0] << 5; + array3[13] = (array3[13] & 1919669298u); + array3[13] = (array3[13] | num7 * 2683487803u); + array3[0] = array3[0] >> 27; + array3[0] = (array3[0] | num8); + for (int k = 0; k < 16; k++) + { + uint num9 = array3[k]; + array4[num4++] = (byte)num9; + array4[num4++] = (byte)(num9 >> 8); + array4[num4++] = (byte)(num9 >> 16); + array4[num4++] = (byte)(num9 >> 24); + array2[k] ^= num9; + } + num3 += 16; + } + decryptedBytes = Lzma.Decompress(array4); + } + + private void FindStringDecrypters(TypeDef type) + { + foreach (var method in type.Methods) + { + if (!method.HasBody) + continue; + if (!(method.Signature.ContainsGenericParameter)) + continue; + var sig = method.MethodSig; + if (sig == null) + continue; + if (sig.Params.Count != 1) + continue; + if (sig.Params[0].GetElementType() != ElementType.U4) + continue; + if (!(sig.RetType.RemovePinnedAndModifiers() is GenericMVar)) + continue; + if (sig.GenParamCount != 1) + continue; + deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + IsStringDecrypter(method); + } + } + + string[] strDecryptCalledMethods = { + "System.Text.Encoding System.Text.Encoding::get_UTF8()", + "System.String System.Text.Encoding::GetString(System.Byte[],System.Int32,System.Int32)", + "System.Array System.Array::CreateInstance(System.Type,System.Int32)", + "System.String System.String::Intern(System.String)", + "System.Void System.Buffer::BlockCopy(System.Array,System.Int32,System.Array,System.Int32,System.Int32)", + "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)", + "System.Type System.Type::GetElementType()" + }; + + private void IsStringDecrypter(MethodDef method) + { + var instr = method.Body.Instructions; + if (instr.Count < 25) + return; + + int i = 0; + + if (!instr[i++].IsLdarg()) + return; + + if (instr[i].OpCode != OpCodes.Call) + return; + + var nativeMethod = instr[i++].Operand as MethodDef; + + if (nativeMethod == null || !nativeMethod.IsStatic || !nativeMethod.IsNative) + return; + if (!DotNetUtils.IsMethod(nativeMethod, "System.Int32", "(System.Int32)")) + return; + + if (!instr[i++].IsStarg()) //uint_0 = (uint_0 * 2857448701u ^ 1196001109u); + return; + + if (!instr[i++].IsLdarg()) + return; + if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x1E) + return; + if (instr[i++].OpCode != OpCodes.Shr_Un) + return; + if (!instr[i++].IsStloc()) //uint num = uint_0 >> 30; + return; + i++; + //TODO: Implement + //if (!instr[10].IsLdloca()) + // return; + if (instr[i++].OpCode != OpCodes.Initobj) + return; + if (!instr[i++].IsLdarg()) + return; + if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x3FFFFFFF) + return; + if (instr[i++].OpCode != OpCodes.And) + return; + if (!instr[i++].IsStarg()) //uint_0 &= 1073741823u; + return; + + if (!instr[i++].IsLdarg()) + return; + if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 2) + return; + if (instr[i++].OpCode != OpCodes.Shl) + return; + if (!instr[i++].IsStarg()) //uint_0 <<= 2; + return; + + foreach (var mtd in strDecryptCalledMethods) + if (!DotNetUtils.CallsMethod(method, mtd)) + return; + //TODO: Implement + //if (!DotNetUtils.LoadsField(method, decryptedField)) + // return; + constantDecrpers.Add(new ConstantDecrypterBase() + { + Decrypted = decryptedBytes, + Method = method, + NativeMethod = nativeMethod, + }); + } + + static bool VerifyGenericArg(MethodSpec gim, ElementType etype) + { + if (gim == null) + return false; + var gims = gim.GenericInstMethodSig; + if (gims == null || gims.GenericArguments.Count != 1) + return false; + return gims.GenericArguments[0].GetElementType() == etype; + } + public string DecryptString(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.String)) + return null; + return info.DecryptString(magic1); + } + public object DecryptSByte(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.I1)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptByte(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.U1)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptInt16(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.I2)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptUInt16(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.U2)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptInt32(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.I4)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptUInt32(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.U4)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptInt64(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.I8)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptUInt64(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.U8)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptSingle(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.R4)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptDouble(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.R8)) + return null; + return info.DecryptConstant(magic1); + } + public object DecryptArray(ConstantDecrypterBase info, MethodSpec gim, uint magic1) + { + if (!VerifyGenericArg(gim, ElementType.SZArray)) + return null; + return info.DecryptArray(magic1); + } + } +} \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs new file mode 100644 index 000000000..c59e1bd4b --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs @@ -0,0 +1,181 @@ +using System.Collections.Generic; +using de4dot.blocks; +using de4dot.blocks.cflow; +using System; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + class ConstantsInliner : IBlocksDeobfuscator + { + Blocks blocks; + SByteValueInliner sbyteValueInliner; + ByteValueInliner byteValueInliner; + Int16ValueInliner int16ValueInliner; + UInt16ValueInliner uint16ValueInliner; + Int32ValueInliner int32ValueInliner; + UInt32ValueInliner uint32ValueInliner; + Int64ValueInliner int64ValueInliner; + UInt64ValueInliner uint64ValueInliner; + SingleValueInliner singleValueInliner; + DoubleValueInliner doubleValueInliner; + ArrayValueInliner arrayValueInliner; + + public bool ExecuteIfNotModified { get; set; } + + public ConstantsInliner(SByteValueInliner sbyteValueInliner, ByteValueInliner byteValueInliner, + Int16ValueInliner int16ValueInliner, UInt16ValueInliner uint16ValueInliner, Int32ValueInliner int32ValueInliner, + UInt32ValueInliner uint32ValueInliner, Int64ValueInliner int64ValueInliner, UInt64ValueInliner uint64ValueInliner, + SingleValueInliner singleValueInliner, DoubleValueInliner doubleValueInliner, ArrayValueInliner arrayValueInliner) + { + this.sbyteValueInliner = sbyteValueInliner; + this.byteValueInliner = byteValueInliner; + this.int16ValueInliner = int16ValueInliner; + this.uint16ValueInliner = uint16ValueInliner; + this.int32ValueInliner = int32ValueInliner; + this.uint32ValueInliner = uint32ValueInliner; + this.int64ValueInliner = int64ValueInliner; + this.uint64ValueInliner = uint64ValueInliner; + this.singleValueInliner = singleValueInliner; + this.doubleValueInliner = doubleValueInliner; + this.arrayValueInliner = arrayValueInliner; + } + + public void DeobfuscateBegin(Blocks blocks) + { + this.blocks = blocks; + } + + public bool Deobfuscate(List allBlocks) + { + bool modified = false; + foreach (var block in allBlocks) + { + modified |= sbyteValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= byteValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= int16ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= uint16ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= int32ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= uint32ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= int64ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= uint64ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= singleValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= doubleValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= arrayValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + } + return modified; + } + } + public class SByteValueInliner : ValueInlinerBase + { + protected override void InlineReturnValues(IList callResults) + { + foreach (var callResult in callResults) + { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.SByte"); + Logger.v("Decrypted sbyte: {0}", callResult.returnValue); + } + } + } + public class ByteValueInliner : ValueInlinerBase + { + protected override void InlineReturnValues(IList callResults) + { + foreach (var callResult in callResults) + { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.Byte"); + Logger.v("Decrypted byte: {0}", callResult.returnValue); + } + } + } + public class Int16ValueInliner : ValueInlinerBase + { + protected override void InlineReturnValues(IList callResults) + { + foreach (var callResult in callResults) + { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.Int16"); + Logger.v("Decrypted int16: {0}", callResult.returnValue); + } + } + } + public class UInt16ValueInliner : ValueInlinerBase + { + protected override void InlineReturnValues(IList callResults) + { + foreach (var callResult in callResults) + { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt16"); + Logger.v("Decrypted uint16: {0}", callResult.returnValue); + } + } + } + public class UInt32ValueInliner : ValueInlinerBase + { + protected override void InlineReturnValues(IList callResults) + { + foreach (var callResult in callResults) + { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt32"); + Logger.v("Decrypted uint32: {0}", callResult.returnValue); + } + } + } + public class UInt64ValueInliner : ValueInlinerBase + { + protected override void InlineReturnValues(IList callResults) + { + foreach (var callResult in callResults) + { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + block.Replace(callResult.callStartIndex, num, OpCodes.Ldc_I8.ToInstruction((long)callResult.returnValue)); + RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt64"); + Logger.v("Decrypted uint64: {0}", callResult.returnValue); + } + } + } + public class ArrayValueInliner : ValueInlinerBase + { + InitializedDataCreator initializedDataCreator; + + public ArrayValueInliner(InitializedDataCreator initializedDataCreator) { this.initializedDataCreator = initializedDataCreator; } + protected override void InlineReturnValues(IList callResults) + { + foreach (var callResult in callResults) + { + var block = callResult.block; + int num = callResult.callEndIndex - callResult.callStartIndex + 1; + + var generic = (callResult.GetMethodRef() as MethodSpec).GenericInstMethodSig.GenericArguments; + ITypeDefOrRef sig = generic[0].Next.ToTypeDefOrRef(); + + initializedDataCreator.AddInitializeArrayCode(block, callResult.callStartIndex, num, sig, callResult.returnValue as byte[]); + RemoveUnboxInstruction(block, callResult.callStartIndex + 1, sig.ToString()); //TODO: sig.ToString() ?? + Logger.v("Decrypted array <{1}>: {0}", callResult.returnValue, sig.ToString()); + } + } + } +} \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs new file mode 100644 index 000000000..dcc218dfd --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using de4dot.blocks; +using de4dot.blocks.cflow; +using de4dot.code.deobfuscators.ConfuserEx.x86; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + class ControlFlowSolver : IBlocksDeobfuscator + { + public bool ExecuteIfNotModified { get; } + private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); + + private Blocks _blocks; + private X86Method _nativeMethod; + private Local _switchKey; + + private int? CalculateKey() + { + var popValue = _instructionEmulator.Peek(); + + if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid()) + return null; + + _instructionEmulator.Pop(); + int result = _nativeMethod.Execute(((Int32Value)popValue).Value); + return result; + } + + private int CalculateSwitchCaseIndex(Block block, int nativeKey) + { + _instructionEmulator.Push(new Int32Value(nativeKey)); + _instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1); + + var popValue = _instructionEmulator.Peek(); + _instructionEmulator.Pop(); + return ((Int32Value)popValue).Value; + } + + private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch + { + var targets = switchBlock.Targets; + + _instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value)); + int? key = CalculateKey(); + + if (!key.HasValue) + throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); + + int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + + if (targets.Count < switchCaseIndex) + throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); + + var targetBlock = targets[switchCaseIndex]; + targetBlock.SwitchData.Key = key; + + switchBlock.Instructions.Clear(); + switchBlock.ReplaceLastNonBranchWithBranch(0, targetBlock); + } + + private void ProcessBlock(List switchCaseBlocks, Block block, Block switchBlock) + { + var targets = switchBlock.Targets; + + _instructionEmulator.Emulate(block.Instructions, 0, block.Instructions.Count); + + if (_instructionEmulator.Peek().IsUnknown()) + throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); + + int? key = CalculateKey(); + + if (!key.HasValue) + throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); + + int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + + if (targets.Count < switchCaseIndex) + throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); + + var targetBlock = targets[switchCaseIndex]; + targetBlock.SwitchData.Key = key; + + block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them + block.ReplaceLastNonBranchWithBranch(0, targetBlock); + + ProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, key.Value); + + block.Processed = true; + } + + private void ProcessTernaryBlock(List switchCaseBlocks, Block ternaryBlock, Block switchBlock) + { + var targets = switchBlock.Targets; + + for (int i = 0; i < 2; i++) // loop both source blocks + { + var sourceBlock = ternaryBlock.Sources[0]; + + if(ternaryBlock.SwitchData.Key.HasValue) // single instruction: pop -- no key! + SetLocalSwitchKey(ternaryBlock.SwitchData.Key.Value); // set old key for both iterations! + + _instructionEmulator.Emulate(sourceBlock.Instructions, 0, sourceBlock.Instructions.Count); + _instructionEmulator.Emulate(ternaryBlock.Instructions, 0, ternaryBlock.Instructions.Count); + + if (_instructionEmulator.Peek().IsUnknown()) + throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); + + int? key = CalculateKey(); + + if (!key.HasValue) + throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); + + int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + + if (targets.Count < switchCaseIndex) + throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); + + var targetBlock = targets[switchCaseIndex]; + targetBlock.SwitchData.Key = key; + + sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction()); + sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]); + + ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value); + // the second source block now becomes the first one + } + + //switchCaseBlock.Instructions.Clear(); + ternaryBlock.Add(new Instr(OpCodes.Pop.ToInstruction())); // don't add pop before both iterations have finished + ternaryBlock.Processed = true; + } + + + public void DeobfuscateBegin(Blocks blocks) + { + _blocks = blocks; + _instructionEmulator.Initialize(_blocks, true); + } + + public bool Deobfuscate(List methodBlocks) + { + List switchBlocks = GetSwitchBlocks(methodBlocks); // blocks that contain a switch + int modifications = 0; + + foreach (Block switchBlock in switchBlocks) + { + if (!switchBlock.SwitchData.IsConfuserExSwitch()) + { + Console.WriteLine("Unsupported switch block obfuscation!"); + continue; + } + + if (switchBlock.SwitchData.IsKeyHardCoded) + { + ProcessHardcodedSwitch(switchBlock); + modifications++; + continue; + } + + _switchKey = Instr.GetLocalVar(_blocks.Locals, + switchBlock.Instructions[switchBlock.Instructions.Count - 4]); + + if (DeobfuscateSwitchBlock(methodBlocks, switchBlock)) + modifications++; + } + return modifications > 0; + } + + private bool DeobfuscateSwitchBlock(List methodBlocks, Block switchBlock) + { + List switchFallThroughs = methodBlocks.FindAll(b => b.FallThrough == switchBlock); // blocks that fallthrough to the switch block + _instructionEmulator.Initialize(_blocks, true); //TODO: Remove temporary precaution + + int blocksLeft = switchFallThroughs.Count; // how many blocks left to proccess + int blockIndex = 0; // block that sets the first switch destination + int failedCount = 0; + + while (blocksLeft > 0) + { + if (blockIndex > switchFallThroughs.Count - 1) + { + blockIndex = 0; + } + + if (failedCount > switchFallThroughs.Count) + { + Console.WriteLine("Some blocks couldn't be processed!"); + break; + } + + Block switchCaseBlock = switchFallThroughs[blockIndex]; + + if (switchCaseBlock.Processed) + { + blockIndex++; + continue; + } + + if (NeedSwitchKey(switchCaseBlock)) + { + if (!switchCaseBlock.SwitchData.Key.HasValue) + { + failedCount++; + blockIndex++; + continue; + } + SetLocalSwitchKey(switchCaseBlock.SwitchData.Key.Value); + } + + if (switchCaseBlock.IsTernary()) + { + ProcessTernaryBlock(switchFallThroughs, switchCaseBlock, switchBlock); + } + else + { + ProcessBlock(switchFallThroughs, switchCaseBlock, switchBlock); + } + + failedCount = 0; + blocksLeft--; + blockIndex++; + } + + if (blocksLeft == switchFallThroughs.Count) // Have we modified anything? + return false; + + return true; + } + + + public bool IsSwitchBlock(Block block) + { + if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0) + return false; + + if (!block.SwitchData.IsNative()) + return false; + + _nativeMethod = new X86Method(block.SwitchData.GetNativeMethod(), _blocks.Method.Module as ModuleDefMD); //TODO: Possible null + + return true; + } + + public List GetSwitchBlocks(List blocks) // get the blocks which contain the switch statement + { + List switchBlocks = new List(); + + foreach (Block block in blocks) + if (IsSwitchBlock(block)) + switchBlocks.Add(block); + + return switchBlocks; + } + + + private readonly List _processedFallThroughs = new List(); + + // add the switch key to all appropriate fallthroughs + private void ProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey) + { + DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, switchKey); + _processedFallThroughs.Clear(); + } + + private void DoProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey) + { + if (_processedFallThroughs.Contains(targetBlock)) + return; + + _processedFallThroughs.Add(targetBlock); + + if (targetBlock.FallThrough == switchBlock && switchCaseBlocks.Contains(targetBlock) && !targetBlock.SwitchData.Key.HasValue) + targetBlock.SwitchData.Key = switchKey; + + + var fallThrough = targetBlock.FallThrough; + + if (fallThrough == null) + return; + + if (fallThrough.LastInstr.OpCode != OpCodes.Ret && fallThrough != switchBlock) + DoProcessFallThroughs(switchCaseBlocks, switchBlock, fallThrough, switchKey); + + if (targetBlock.CountTargets() > 1) + foreach (Block targetBlockTarget in targetBlock.Targets) + { + if (targetBlockTarget == switchBlock) + return; + DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlockTarget, switchKey); + } + } + + + private bool NeedSwitchKey(Block block) + { + foreach (var instr in block.Instructions) + if (instr.IsLdloc() && Instr.GetLocalVar(_blocks.Locals, instr) == _switchKey) + return true; + return false; + } + + private int? GetSwitchKey() + { + var val = _instructionEmulator.GetLocal(_switchKey); + if (!val.IsInt32()) + return null; + var value = val as Int32Value; + if (value == null || !value.AllBitsValid()) + return null; + return value.Value; + } + + private void SetLocalSwitchKey(int key) + { + _instructionEmulator.SetLocal(_switchKey, new Int32Value(key)); + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs new file mode 100644 index 000000000..6f5a67fa4 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -0,0 +1,278 @@ +/* + Copyright (C) 2011-2017 TheProxy + + This file is part of modified de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using de4dot.blocks; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System.Collections.Generic; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + + public class DeobfuscatorInfo : DeobfuscatorInfoBase + { + public const string THE_NAME = "ConfuserEx"; + public const string THE_TYPE = "cx"; + const string DEFAULT_REGEX = DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX; + + public DeobfuscatorInfo() + : base(DEFAULT_REGEX) + { + } + + public override string Name + { + get { return THE_NAME; } + } + + public override string Type + { + get { return THE_TYPE; } + } + + public override IDeobfuscator CreateDeobfuscator() + { + return new Deobfuscator(new Deobfuscator.Options + { + RenameResourcesInCode = false, + ValidNameRegex = validNameRegex.Get(), + }); + } + + class Deobfuscator : DeobfuscatorBase + { + + bool detectedConfuserExAttribute = false, deobfuscating = false; + string version = ""; + LzmaFinder lzmaFinder; + ConstantsDecrypter constantDecrypter; + ResourceDecrypter resourceDecrypter; + + #region ConstantInliners + + SByteValueInliner sbyteValueInliner; + ByteValueInliner byteValueInliner; + Int16ValueInliner int16ValueInliner; + UInt16ValueInliner uint16ValueInliner; + Int32ValueInliner int32ValueInliner; + UInt32ValueInliner uint32ValueInliner; + Int64ValueInliner int64ValueInliner; + UInt64ValueInliner uint64ValueInliner; + SingleValueInliner singleValueInliner; + DoubleValueInliner doubleValueInliner; + ArrayValueInliner arrayValueInliner; + + #endregion + + internal class Options : OptionsBase + { + } + + public override string Type + { + get { return DeobfuscatorInfo.THE_TYPE; } + } + + public override string TypeLong + { + get { return DeobfuscatorInfo.THE_NAME; } + } + + public override string Name + { + get { return $"{TypeLong} {version}"; } + } + + public Deobfuscator(Options options) + : base(options) + { + } + + protected override int DetectInternal() + { + int val = 0; + if (detectedConfuserExAttribute) val += 0; + if (lzmaFinder.FoundLzma) val += 10; + if (constantDecrypter.Detected) val += 10; + if (resourceDecrypter.Detected) val += 10; + return val; + } + + protected override void ScanForObfuscator() + { + lzmaFinder = new LzmaFinder(module, DeobfuscatedFile); + lzmaFinder.Find(); + constantDecrypter = new ConstantsDecrypter(module, lzmaFinder.Method, DeobfuscatedFile); + resourceDecrypter = new ResourceDecrypter(module, lzmaFinder.Method, DeobfuscatedFile); + if (lzmaFinder.FoundLzma) + { + constantDecrypter.Find(); + resourceDecrypter.Find(); + } + DetectConfuserExAttribute(); + } + + public void DetectConfuserExAttribute() + { + var versions = new List(); + foreach (var attribute in module.CustomAttributes) + { + if (attribute.TypeFullName != "ConfusedByAttribute") + continue; + foreach (var argument in attribute.ConstructorArguments) + { + if (argument.Type.ElementType != ElementType.String) + continue; + var value = argument.Value.ToString(); + if (!value.Contains("ConfuserEx")) + continue; + detectedConfuserExAttribute = true; + version = value.Replace("ConfuserEx", ""); + return; + } + } + } + + public override void DeobfuscateBegin() + { + if (constantDecrypter.Detected) + { + sbyteValueInliner = new SByteValueInliner(); + byteValueInliner = new ByteValueInliner(); + int16ValueInliner = new Int16ValueInliner(); + uint16ValueInliner = new UInt16ValueInliner(); + int32ValueInliner = new Int32ValueInliner(); + uint32ValueInliner = new UInt32ValueInliner(); + int64ValueInliner = new Int64ValueInliner(); + uint64ValueInliner = new UInt64ValueInliner(); + singleValueInliner = new SingleValueInliner(); + doubleValueInliner = new DoubleValueInliner(); + arrayValueInliner = new ArrayValueInliner(initializedDataCreator); + foreach (var info in constantDecrypter.Decrypters) + { + staticStringInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptString(info, gim, (uint) args[0])); + sbyteValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptSByte(info, gim, (uint) args[0])); + byteValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptByte(info, gim, (uint) args[0])); + int16ValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptInt16(info, gim, (uint) args[0])); + uint16ValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptUInt16(info, gim, (uint) args[0])); + int32ValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptInt32(info, gim, (uint) args[0])); + uint32ValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptUInt32(info, gim, (uint) args[0])); + int64ValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptInt64(info, gim, (uint) args[0])); + uint64ValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptUInt64(info, gim, (uint) args[0])); + singleValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptSingle(info, gim, (uint) args[0])); + doubleValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptDouble(info, gim, (uint) args[0])); + arrayValueInliner.Add(info.Method, + (method, gim, args) => constantDecrypter.DecryptArray(info, gim, (uint) args[0])); + } + deobfuscating = true; + } + if (resourceDecrypter.Detected) + resourceDecrypter.Fix(); + base.DeobfuscateBegin(); + } + + public override IEnumerable BlocksDeobfuscators + { + get + { + var list = new List(); + list.Add(new ControlFlowSolver()); + + if (deobfuscating && int32ValueInliner != null) + list.Add(new ConstantsInliner(sbyteValueInliner, byteValueInliner, int16ValueInliner, + uint16ValueInliner, + int32ValueInliner, uint32ValueInliner, int64ValueInliner, uint64ValueInliner, + singleValueInliner, doubleValueInliner, arrayValueInliner) + {ExecuteIfNotModified = true}); + return list; + } + } + + bool CanRemoveLzma = true; + + public override void DeobfuscateEnd() + { + FindAndRemoveInlinedMethods(); + + List toRemoveFromCctor = new List(); + + if (constantDecrypter.Detected) + if (CanRemoveStringDecrypterType) + { + toRemoveFromCctor.Add(constantDecrypter.Method); + AddMethodToBeRemoved(constantDecrypter.Method, "Constant Decrypter Initializer"); + foreach (var dec in constantDecrypter.Decrypters) + AddMethodToBeRemoved(dec.Method, "Constant Decrypter Method"); + AddFieldsToBeRemoved(constantDecrypter.Fields, "Constant Decrypter Fields"); + AddTypeToBeRemoved(constantDecrypter.Type, "Array field signature type"); + } + else + CanRemoveLzma = false; + + if (resourceDecrypter.Detected && resourceDecrypter.CanRemoveLzma) + { + toRemoveFromCctor.Add(resourceDecrypter.Method); + AddMethodToBeRemoved(resourceDecrypter.Method, "Resource decrypter Initializer method"); + AddMethodToBeRemoved(resourceDecrypter.AssembyResolveMethod, + "Resource decrypter AssemblyResolve method"); + AddFieldsToBeRemoved(resourceDecrypter.Fields, "Constant Decrypter Fields"); + AddTypeToBeRemoved(resourceDecrypter.Type, "Array field signature type"); + } + + if (!constantDecrypter.CanRemoveLzma || !resourceDecrypter.CanRemoveLzma) + CanRemoveLzma = false; + + if (lzmaFinder.FoundLzma && CanRemoveLzma) + { + AddMethodToBeRemoved(lzmaFinder.Method, "Lzma Decompress method"); + AddTypesToBeRemoved(lzmaFinder.Types, "Lzma Nested Types"); + } + + var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); + foreach (var instr in moduleCctor.Body.Instructions) + if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef && + toRemoveFromCctor.Contains(instr.Operand as MethodDef)) + instr.OpCode = OpCodes.Nop; + + //No more mixed! + module.IsILOnly = true; + + base.DeobfuscateEnd(); + } + + public override IEnumerable GetStringDecrypterMethods() + { + var list = new List(); + return list; + } + } + } +} \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs b/de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs new file mode 100644 index 000000000..f1a216f5c --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs @@ -0,0 +1,198 @@ +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + public class LzmaFinder + { + MethodDef decompressMethod; + List types = new List(); + + public MethodDef Method + { + get { return decompressMethod; } + } + public List Types + { + get { return types; } + } + public bool FoundLzma + { + get { return decompressMethod != null && types.Count != 0; } + } + + ModuleDef module; + ISimpleDeobfuscator deobfuscator; + public LzmaFinder(ModuleDef module, ISimpleDeobfuscator deobfuscator) + { + this.module = module; + this.deobfuscator = deobfuscator; + } + + public void Find() + { + var moduleType = DotNetUtils.GetModuleType(module); + if (moduleType == null) + return; + foreach (var method in moduleType.Methods) + { + if (!method.HasBody || !method.IsStatic) + continue; + if (!DotNetUtils.IsMethod(method, "System.Byte[]", "(System.Byte[])")) + continue; + deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + if (!IsLzmaMethod(method)) + continue; + decompressMethod = method; + var type = ((MethodDef)method.Body.Instructions[3].Operand).DeclaringType; + ExtractNestedTypes(type); + } + } + + private bool IsLzmaMethod(MethodDef method) + { + var instructions = method.Body.Instructions; + + if (instructions.Count < 60) + return false; + + var firstInstruction = instructions.FirstOrDefault( + instr => + instr.OpCode == OpCodes.Newobj && + instr.Operand.ToString() == "System.Void System.IO.MemoryStream::.ctor(System.Byte[])"); + + if (firstInstruction == null) + return false; + + int i = instructions.IndexOf(firstInstruction) + 1; + + if (!instructions[i++].IsStloc()) + return false; + if (instructions[i++].OpCode != OpCodes.Newobj) + return false; + if (!instructions[i++].IsStloc()) //.Class1 @class = new .Class1(); + return false; + + if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 5) + return false; + if (instructions[i++].OpCode != OpCodes.Newarr) + return false; + if (!instructions[i++].IsStloc()) //byte[] buffer = new byte[5]; + return false; + + if (!instructions[i++].IsLdloc()) + return false; + if (!instructions[i++].IsLdloc()) + return false; + if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 0) + return false; + if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 5) + return false; + if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != "System.Int32 System.IO.Stream::Read(System.Byte[],System.Int32,System.Int32)") + return false; + if (instructions[i++].OpCode != OpCodes.Pop) //memoryStream.Read(buffer, 0, 5); + return false; + + if (!instructions[i++].IsLdloc()) + return false; + if (!instructions[i++].IsLdloc()) + return false; + if (instructions[i++].OpCode != OpCodes.Callvirt) //@class.method_5(buffer); + return false; + + firstInstruction = + instructions.FirstOrDefault( + instr => + instr.OpCode == OpCodes.Callvirt && + instr.Operand.ToString() == "System.Int32 System.IO.Stream::ReadByte()"); + + if (firstInstruction == null) + return false; + if (i >= instructions.IndexOf(firstInstruction)) + return false; + + i = instructions.IndexOf(firstInstruction) + 1; + + if (!instructions[i++].IsStloc()) //int num2 = memoryStream.ReadByte(); + return false; + + if (!instructions[i++].IsLdloc()) + return false; + if (!instructions[i++].IsLdloc()) + return false; + if (instructions[i++].OpCode != OpCodes.Conv_U1) + return false; + if (instructions[i++].OpCode != OpCodes.Conv_U8) + return false; + if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 8) + return false; + if (!instructions[i++].IsLdloc()) + return false; + if (instructions[i++].OpCode != OpCodes.Mul) + return false; + if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 0x3F) + return false; + if (instructions[i++].OpCode != OpCodes.And) + return false; + if (instructions[i++].OpCode != OpCodes.Shl) + return false; + if (instructions[i++].OpCode != OpCodes.Or) + return false; + if (!instructions[i++].IsStloc()) //num |= (long)((long)((ulong)((byte)num2)) << 8 * i); + return false; + + firstInstruction = + instructions.FirstOrDefault( + instr => + instr.OpCode == OpCodes.Newobj && + instr.Operand.ToString() == "System.Void System.IO.MemoryStream::.ctor(System.Byte[],System.Boolean)"); + + if (firstInstruction == null) + return false; + if (i >= instructions.IndexOf(firstInstruction)) + return false; + + i = instructions.IndexOf(firstInstruction) + 1; + + if (!instructions[i++].IsStloc()) //MemoryStream stream_ = new MemoryStream(array, true); + return false; + + if (!instructions[i++].IsLdloc()) + return false; + if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != "System.Int64 System.IO.Stream::get_Length()") + return false; + if (instructions[i].OpCode != OpCodes.Ldc_I8 || (long)instructions[i++].Operand != 13L) + return false; + if (instructions[i++].OpCode != OpCodes.Sub) + return false; + if (!instructions[i++].IsStloc()) //long long_ = memoryStream.Length - 13L; + return false; + + return true; + } + private void ExtractNestedTypes(TypeDef type) + { + foreach (var method in type.Methods) + if (method.HasBody) + { + var instr = method.Body.Instructions; + foreach (var inst in instr) + if (inst.Operand is MethodDef) + { + var ntype = (inst.Operand as MethodDef).DeclaringType; + if (!ntype.IsNested) + continue; + if (types.Contains(ntype)) + continue; + types.Add(ntype); + ExtractNestedTypes(ntype); + } + } + } + } +} \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs new file mode 100644 index 000000000..4ab77c4a8 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs @@ -0,0 +1,293 @@ +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + public class ResourceDecrypter + { + TypeDef arrayType; + MethodDef resourceDecInitMethod, assembyResolveMethod; + FieldDef arrayField, asmField; + byte[] decryptedBytes; + + bool canRemoveLzma = true; + + public bool CanRemoveLzma + { + get { return canRemoveLzma; } + } + + public TypeDef Type + { + get { return arrayType; } + } + public MethodDef Method + { + get { return resourceDecInitMethod; } + } + public MethodDef AssembyResolveMethod + { + get { return assembyResolveMethod; } + } + public List Fields + { + get { return new List() { arrayField, asmField }; } + } + public bool Detected + { + get + { + return resourceDecInitMethod != null && resourceDecInitMethod != null + && decryptedBytes != null && arrayField != null && asmField != null; + } + } + + ModuleDef module; + MethodDef lzmaMethod; + ISimpleDeobfuscator deobfuscator; + public ResourceDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator) + { + this.module = module; + this.lzmaMethod = lzmaMethod; + this.deobfuscator = deobfsucator; + } + + public void Find() + { + var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); + if (moduleCctor == null) + return; + foreach (var inst in moduleCctor.Body.Instructions) + { + if (inst.OpCode != OpCodes.Call) + continue; + if (!(inst.Operand is MethodDef)) + continue; + var method = inst.Operand as MethodDef; + if (!method.HasBody || !method.IsStatic) + continue; + if (!DotNetUtils.IsMethod(method, "System.Void", "()")) + continue; + deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + if (!isResDecryptInit(method)) + continue; + resourceDecInitMethod = method; + } + } + + bool isResDecryptInit(MethodDef method) + { + var instr = method.Body.Instructions; + + if (instr.Count < 15) + return false; + + if (!instr[0].IsLdcI4()) + return false; + if (!instr[1].IsStloc()) //uint num = 96u; + return false; + + if (!instr[2].IsLdcI4()) + return false; + if (instr[0].GetLdcI4Value() != instr[2].GetLdcI4Value()) + return false; + if (instr[3].OpCode != OpCodes.Newarr) + return false; + if (instr[3].Operand.ToString() != "System.UInt32") + return false; + if (instr[4].OpCode != OpCodes.Dup) + return false; + if (instr[5].OpCode != OpCodes.Ldtoken) + return false; + var aField = instr[5].Operand as FieldDef; + if (aField == null) + return false; + if (aField.InitialValue == null) + return false; + if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA)) + return false; + if (instr[6].OpCode != OpCodes.Call) + return false; + if (instr[6].Operand.ToString() != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") + return false; + if (!instr[7].IsStloc()) // uint[] array = new uint[] {.....}; + return false; + + var l = instr.Count; + if (!instr[l - 10].IsLdloc()) + return false; + if (instr[l - 9].OpCode != OpCodes.Call) + return false; + if (instr[l - 9].Operand != lzmaMethod) + return false; + if (instr[l - 8].OpCode != OpCodes.Call) + return false; + if (instr[l - 8].Operand.ToString() != "System.Reflection.Assembly System.Reflection.Assembly::Load(System.Byte[])") + return false; + if (instr[l - 7].OpCode != OpCodes.Stsfld) //.assembly_0 = Assembly.Load(array4); + return false; + var asField = instr[l - 7].Operand as FieldDef; + if (asField == null) + return false; + + if (instr[l - 6].OpCode != OpCodes.Call) + return false; + if (instr[l - 6].Operand.ToString() != "System.AppDomain System.AppDomain::get_CurrentDomain()") + return false; + if (instr[l - 5].OpCode != OpCodes.Ldnull) + return false; + if (instr[l - 4].OpCode != OpCodes.Ldftn) + return false; + var mtd = instr[l - 4].Operand as MethodDef; + if (mtd == null) + return false; + if (!IsAssembyResolveMethod(mtd, asField)) + return false; + if (instr[l - 3].OpCode != OpCodes.Newobj) + return false; + if (instr[l - 2].OpCode != OpCodes.Callvirt) //AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(.smethod_1); + return false; + try + { + DecryptArray(ConvertArray(aField.InitialValue)); + } + catch { canRemoveLzma = false; return false; } + arrayField = aField; + arrayType = DotNetUtils.GetType(module, aField.FieldSig.Type); + asmField = asField; + assembyResolveMethod = mtd; + return true; + } + private T[] ConvertArray(T1[] array) + { + int l = Marshal.SizeOf(typeof(T)); + int l1 = Marshal.SizeOf(typeof(T1)); + var buffer = new T[(array.Length * l1) / l]; + Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); + return buffer; + } + private void DecryptArray(uint[] array) + { + int num = array.Length; + uint[] array2 = new uint[16]; + uint num2 = 825993394u; + for (int i = 0; i < 16; i++) + { + num2 ^= num2 >> 13; + num2 ^= num2 << 25; + num2 ^= num2 >> 27; + array2[i] = num2; + } + int num3 = 0; + int num4 = 0; + uint[] array3 = new uint[16]; + byte[] array4 = new byte[num * 4u]; + while ((long)num3 < (long)((ulong)num)) + { + for (int j = 0; j < 16; j++) + { + array3[j] = array[num3 + j]; + } + array3[0] = (array3[0] ^ array2[0]); + array3[1] = (array3[1] ^ array2[1]); + array3[2] = (array3[2] ^ array2[2]); + array3[3] = (array3[3] ^ array2[3]); + array3[4] = (array3[4] ^ array2[4]); + array3[5] = (array3[5] ^ array2[5]); + array3[6] = (array3[6] ^ array2[6]); + array3[7] = (array3[7] ^ array2[7]); + array3[8] = (array3[8] ^ array2[8]); + array3[9] = (array3[9] ^ array2[9]); + array3[10] = (array3[10] ^ array2[10]); + array3[11] = (array3[11] ^ array2[11]); + array3[12] = (array3[12] ^ array2[12]); + array3[13] = (array3[13] ^ array2[13]); + array3[14] = (array3[14] ^ array2[14]); + array3[15] = (array3[15] ^ array2[15]); + for (int k = 0; k < 16; k++) + { + uint num5 = array3[k]; + array4[num4++] = (byte)num5; + array4[num4++] = (byte)(num5 >> 8); + array4[num4++] = (byte)(num5 >> 16); + array4[num4++] = (byte)(num5 >> 24); + array2[k] ^= num5; + } + num3 += 16; + } + decryptedBytes = Lzma.Decompress(array4); + } + + private bool IsAssembyResolveMethod(MethodDef method, FieldDef field) + { + if (DotNetUtils.IsMethod(method, "", "()")) + return false; + deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + + var instr = method.Body.Instructions; + if (instr.Count != 10) + return false; + + if (instr[0].OpCode != OpCodes.Ldsfld) + return false; + if (instr[0].Operand != field) + return false; + if (instr[1].OpCode != OpCodes.Callvirt) + return false; + if (instr[1].Operand.ToString() != "System.String System.Reflection.Assembly::get_FullName()") + return false; + if (!instr[2].IsLdarg()) + return false; + if (instr[3].OpCode != OpCodes.Callvirt) + return false; + if (instr[3].Operand.ToString() != "System.String System.ResolveEventArgs::get_Name()") + return false; + if (instr[4].OpCode != OpCodes.Call) + return false; + if (instr[4].Operand.ToString() != "System.Boolean System.String::op_Equality(System.String,System.String)") + return false; + if (!instr[5].IsBrfalse()) + return false; + if (instr[6].OpCode != OpCodes.Ldsfld) + return false; + if (instr[6].Operand != field) + return false; + if (instr[7].OpCode != OpCodes.Ret) + return false; + if (instr[8].OpCode != OpCodes.Ldnull) + return false; + if (instr[9].OpCode != OpCodes.Ret) + return false; + return true; + } + + public void Fix() + { + ModuleDef newModule; + try + { + newModule = ModuleDefMD.Load(decryptedBytes); + } + catch { canRemoveLzma = false; return; } + List toRemove = new List(); + List toAdd = new List(); + foreach (var cryptedResource in module.Resources) + foreach (var resource in newModule.Resources) + if (cryptedResource.Name == resource.Name) + { + toRemove.Add(cryptedResource); + toAdd.Add(resource); + } + + foreach (var resToRemove in toRemove) + module.Resources.Remove(resToRemove); + foreach (var resToAdd in toAdd) + module.Resources.Add(resToAdd); + } + } +} \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/Utils.cs b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs new file mode 100644 index 000000000..5ab31d790 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Text; +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + public static class Utils + { + public static bool IsArithmetical(this Instr instr) + { + switch (instr.OpCode.Code) + { + case Code.Add: + case Code.Add_Ovf: + case Code.Add_Ovf_Un: + case Code.Div: + case Code.Div_Un: + case Code.Mul: + case Code.Mul_Ovf: + case Code.Mul_Ovf_Un: + case Code.Not: + case Code.Shl: + case Code.Shr: + case Code.Shr_Un: + case Code.Sub: + case Code.Sub_Ovf: + case Code.Sub_Ovf_Un: + case Code.Xor: + case Code.And: + case Code.Rem: + case Code.Rem_Un: + case Code.Ceq: + case Code.Cgt: + case Code.Cgt_Un: + case Code.Clt: + case Code.Clt_Un: + case Code.Neg: + case Code.Or: + return true; + } + return false; + } + + public static bool IsConv(this Instr instr) + { + switch (instr.OpCode.Code) + { + case Code.Conv_I1: + case Code.Conv_I2: + case Code.Conv_I4: + case Code.Conv_I8: + case Code.Conv_U1: + case Code.Conv_U2: + case Code.Conv_U4: + case Code.Conv_U8: + case Code.Conv_R4: + case Code.Conv_R8: + case Code.Conv_Ovf_I1: + case Code.Conv_Ovf_I1_Un: + case Code.Conv_Ovf_I2: + case Code.Conv_Ovf_I2_Un: + case Code.Conv_Ovf_I4: + case Code.Conv_Ovf_I4_Un: + case Code.Conv_Ovf_I8: + case Code.Conv_Ovf_I8_Un: + case Code.Conv_Ovf_U1: + case Code.Conv_Ovf_U1_Un: + case Code.Conv_Ovf_U2: + case Code.Conv_Ovf_U2_Un: + case Code.Conv_Ovf_U4: + case Code.Conv_Ovf_U4_Un: + case Code.Conv_Ovf_U8: + case Code.Conv_Ovf_U8_Un: + return true; + } + return false; + } + + public static bool IsLdc(this Instr instr) + { + switch (instr.OpCode.Code) + { + 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: + case Code.Ldc_I8: + case Code.Ldc_R4: + case Code.Ldc_R8: + return true; + } + return false; + } + + public static bool IsLoc(this Instr instr) + { + switch (instr.OpCode.Code) + { + case Code.Ldloc: + case Code.Ldloc_S: + case Code.Ldloc_0: + case Code.Ldloc_1: + case Code.Ldloc_2: + case Code.Ldloc_3: + case Code.Ldloca: + case Code.Ldloca_S: + case Code.Stloc: + case Code.Stloc_S: + case Code.Stloc_0: + case Code.Stloc_1: + case Code.Stloc_2: + case Code.Stloc_3: + return true; + } + return false; + } + + public static bool IsValidInstr(this Instr instr) + { + return IsArithmetical(instr) || instr.IsConv() || IsLdc(instr) || IsLoc(instr) || instr.OpCode == OpCodes.Dup; + } + + public static bool IsDup(this Block block) + { + if (block.Sources.Count != 1) + return false; + if (block.Instructions.Count != 2) + return false; + if (!block.FirstInstr.IsLdcI4()) + return false; + if (block.LastInstr.OpCode != OpCodes.Dup) + if (!block.LastInstr.IsLdcI4() || block.LastInstr.GetLdcI4Value() != block.FirstInstr.GetLdcI4Value()) + return false; + return true; + } + } + + public static class Extensions + { + public static bool IsConfuserExSwitch(this SwitchData switchData) + { + var instructions = switchData.Block.Instructions; + var lastIndex = instructions.Count - 1; + + if (instructions.Count < 4) + return false; + if (!instructions[lastIndex - 3].IsStloc()) + return false; + if (!instructions[lastIndex - 2].IsLdcI4()) + return false; + if (instructions[lastIndex - 1].OpCode != OpCodes.Rem_Un) + return false; + + return true; + } + + public static bool IsNative(this SwitchData switchData) + { + var block = switchData.Block; + + var instr = block.Instructions; + if (instr.Count <= 4) + return false; + + if (instr[0].IsLdcI4() && instr[1].OpCode == OpCodes.Call) + { + switchData.IsKeyHardCoded = true; + block.SwitchData.Key = block.FirstInstr.GetLdcI4Value(); + } + + if (!switchData.IsKeyHardCoded && instr[0].OpCode != OpCodes.Call) + return false; + + var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef; + + if (method == null || !method.IsStatic || !method.IsNative) + return false; + if (!DotNetUtils.IsMethod(method, "System.Int32", "(System.Int32)")) + return false; + for (int i = switchData.IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++) + if (!instr[i].IsValidInstr()) + return false; + + return true; + } + + public static MethodDef GetNativeMethod(this SwitchData switchData) + { + var block = switchData.Block; + + var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef; + return method; + } + + public static bool IsTernaryPredicate(this Block ternaryPredicateBlock) + { + if (!ternaryPredicateBlock.LastInstr.IsConditionalBranch()) + return false; + + if (ternaryPredicateBlock.CountTargets() > 2) + return false; + + var source1 = ternaryPredicateBlock.Targets[0]; + var source2 = ternaryPredicateBlock.FallThrough; + + //if (!IsDup(source1) || !IsDup(source2)) + // return false; + + if (source1.CountTargets() > 1 || source2.CountTargets() > 1) + return false; + + var mainBlock = source1.FallThrough; + + if (mainBlock != source2.FallThrough) + return false; + + if (mainBlock.Sources.Count != 2) + return false; + if (mainBlock.LastInstr.OpCode == OpCodes.Ret) + return false; + + return true; + } + + public static bool IsTernary(this Block block) + { + var sources = block.Sources; + if (sources.Count != 2) + return false; + if (!sources[0].IsDup() || !(sources[1]).IsDup()) //TODO: Case without DUP? + return false; + if (sources[0].CountTargets() > 1 || sources[1].CountTargets() > 1) + return false; + if (sources[0].FallThrough != block || sources[1].FallThrough != block) + return false; + if (sources[0].Sources[0] != sources[1].Sources[0]) + return false; + if (!sources[0].Sources[0].IsConditionalBranch()) + return false; + if (block.LastInstr.OpCode == OpCodes.Ret) + return false; + + return true; + } + + public static List GetTernaryPredicates(this List switchCaseBlocks) + { + List ternaryPredicates = new List(); + + foreach (Block preBlock in switchCaseBlocks) + { + if (IsTernary(preBlock)) + { + // switchCaseBlock -> 2x sourceBlock -> ternaryPredicateBlock + ternaryPredicates.Add(preBlock.Sources[0].Sources[0]); + } + } + + return ternaryPredicates; + } + + public static Block GetTernaryPredicateMainBlock(this Block ternaryPredicateBlock) + { + return ternaryPredicateBlock.FallThrough.FallThrough; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs new file mode 100644 index 000000000..c933f9ac1 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs @@ -0,0 +1,191 @@ + +namespace de4dot.Bea +{ + public class BeaConstants + { + public static int INSTRUCT_LENGTH = 64; + + public enum SegmentRegister : byte + { + ESReg = 1, + DSReg = 2, + FSReg = 3, + GSReg = 4, + CSReg = 5, + SSReg = 6 + } + + public enum PrefixType : byte + { + NotUsedPrefix = 0, + InUsePrefix = 1, + SuperfluousPrefix = 2, + InvalidPrefix = 4, + MandatoryPrefix = 8 + } + + public enum InstructionType : uint + { + GENERAL_PURPOSE_INSTRUCTION = 0x10000, + FPU_INSTRUCTION = 0x20000, + MMX_INSTRUCTION = 0x40000, + SSE_INSTRUCTION = 0x80000, + SSE2_INSTRUCTION = 0x100000, + SSE3_INSTRUCTION = 0x200000, + SSSE3_INSTRUCTION = 0x400000, + SSE41_INSTRUCTION = 0x800000, + SSE42_INSTRUCTION = 0x1000000, + SYSTEM_INSTRUCTION = 0x2000000, + VM_INSTRUCTION = 0x4000000, + UNDOCUMENTED_INSTRUCTION = 0x8000000, + AMD_INSTRUCTION = 0x10000000, + ILLEGAL_INSTRUCTION = 0x20000000, + AES_INSTRUCTION = 0x40000000, + CLMUL_INSTRUCTION = 0x80000000, + + DATA_TRANSFER = 0x1, + ARITHMETIC_INSTRUCTION, + LOGICAL_INSTRUCTION, + SHIFT_ROTATE, + BIT_UInt8, + CONTROL_TRANSFER, + STRING_INSTRUCTION, + InOutINSTRUCTION, + ENTER_LEAVE_INSTRUCTION, + FLAG_CONTROL_INSTRUCTION, + SEGMENT_REGISTER, + MISCELLANEOUS_INSTRUCTION, + COMPARISON_INSTRUCTION, + LOGARITHMIC_INSTRUCTION, + TRIGONOMETRIC_INSTRUCTION, + UNSUPPORTED_INSTRUCTION, + LOAD_CONSTANTS, + FPUCONTROL, + STATE_MANAGEMENT, + CONVERSION_INSTRUCTION, + SHUFFLE_UNPACK, + PACKED_SINGLE_PRECISION, + SIMD128bits, + SIMD64bits, + CACHEABILITY_CONTROL, + FP_INTEGER_CONVERSION, + SPECIALIZED_128bits, + SIMD_FP_PACKED, + SIMD_FP_HORIZONTAL, + AGENT_SYNCHRONISATION, + PACKED_ALIGN_RIGHT, + PACKED_SIGN, + PACKED_BLENDING_INSTRUCTION, + PACKED_TEST, + PACKED_MINMAX, + HORIZONTAL_SEARCH, + PACKED_EQUALITY, + STREAMING_LOAD, + INSERTION_EXTRACTION, + DOT_PRODUCT, + SAD_INSTRUCTION, + ACCELERATOR_INSTRUCTION, + ROUND_INSTRUCTION + } + + public enum EFlagState : byte + { + TE_ = 1, + MO_ = 2, + RE_ = 4, + SE_ = 8, + UN_ = 0x10, + PR_ = 0x20 + } + + public enum BranchType : short + { + JO = 1, + JC, + JE, + JA, + JS, + JP, + JL, + JG, + JB, + JECXZ, + JmpType, + CallType, + RetType, + JNO = -1, + JNC = -2, + JNE = -3, + JNA = -4, + JNS = -5, + JNP = -6, + JNL = -7, + JNG = -8, + JNB = -9 + } + + public enum ArgumentType : uint + { + NO_ARGUMENT = 0x10000000, + REGISTER_TYPE = 0x20000000, + MEMORY_TYPE = 0x40000000, + CONSTANT_TYPE = 0x80000000, + + MMX_REG = 0x10000, + GENERAL_REG = 0x20000, + FPU_REG = 0x40000, + SSE_REG = 0x80000, + CR_REG = 0x100000, + DR_REG = 0x200000, + SPECIAL_REG = 0x400000, + MEMORY_MANAGEMENT_REG = 0x800000, + SEGMENT_REG = 0x1000000, + + RELATIVE_ = 0x4000000, + ABSOLUTE_ = 0x8000000, + + READ = 0x1, + WRITE = 0x2, + + REG0 = 0x1, + REG1 = 0x2, + REG2 = 0x4, + REG3 = 0x8, + REG4 = 0x10, + REG5 = 0x20, + REG6 = 0x40, + REG7 = 0x80, + REG8 = 0x100, + REG9 = 0x200, + REG10 = 0x400, + REG11 = 0x800, + REG12 = 0x1000, + REG13 = 0x2000, + REG14 = 0x4000, + REG15 = 0x8000 + } + + public enum SpecialInfo : int + { + UNKNOWN_OPCODE = -1, + OUT_OF_BLOCK = 0, + + /* === mask = 0xff */ + NoTabulation = 0x00000000, + Tabulation = 0x00000001, + + /* === mask = 0xff00 */ + MasmSyntax = 0x00000000, + GoAsmSyntax = 0x00000100, + NasmSyntax = 0x00000200, + ATSyntax = 0x00000400, + + /* === mask = 0xff0000 */ + PrefixedNumeral = 0x00010000, + SuffixedNumeral = 0x00000000, + + /* === mask = 0xff000000 */ + ShowSegmentRegs = 0x01000000 + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs new file mode 100644 index 000000000..875cf8c26 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs @@ -0,0 +1,32 @@ +using System.Runtime.InteropServices; + +namespace de4dot.Bea +{ + public class BeaEngine + { + [DllImport("BeaEngine")] + public static extern int Disasm([In, Out, MarshalAs(UnmanagedType.LPStruct)] Disasm disasm); + + [DllImport("BeaEngine")] + private static extern string BeaEngineVersion(); + + [DllImport("BeaEngine")] + private static extern string BeaEngineRevision(); + + public static string Version + { + get + { + return BeaEngineVersion(); + } + } + + public static string Revision + { + get + { + return BeaEngineRevision(); + } + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs new file mode 100644 index 000000000..4abd66e3d --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs @@ -0,0 +1,107 @@ +using System; +using System.Runtime.InteropServices; + +namespace de4dot.Bea +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class REX_Struct + { + public byte W_; + public byte R_; + public byte X_; + public byte B_; + public byte state; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class PrefixInfo + { + public int Number; + public int NbUndefined; + public byte LockPrefix; + public byte OperandSize; + public byte AddressSize; + public byte RepnePrefix; + public byte RepPrefix; + public byte FSPrefix; + public byte SSPrefix; + public byte GSPrefix; + public byte ESPrefix; + public byte CSPrefix; + public byte DSPrefix; + public byte BranchTaken; + public byte BranchNotTaken; + public REX_Struct REX; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class EFLStruct + { + public byte OF_; + public byte SF_; + public byte ZF_; + public byte AF_; + public byte PF_; + public byte CF_; + public byte TF_; + public byte IF_; + public byte DF_; + public byte NT_; + public byte RF_; + public byte alignment; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class MemoryType + { + public Int32 BaseRegister; + public Int32 IndexRegister; + public Int32 Scale; + public Int64 Displacement; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class InstructionType + { + public Int32 Category; + public Int32 Opcode; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] + public string Mnemonic; + public Int32 BranchType; + public EFLStruct Flags; + public UInt64 AddrValue; + public Int64 Immediat; + public UInt32 ImplicitModifiedRegs; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ArgumentType + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string ArgMnemonic; + public Int32 ArgType; + public Int32 ArgSize; + public UInt32 AccessMode; + public MemoryType Memory; + public UInt32 SegmentReg; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class Disasm + { + public IntPtr EIP; + public UInt64 VirtualAddr; + public UInt32 SecurityBlock; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] + public string CompleteInstr; + public UInt32 Archi; + public UInt64 Options; + public InstructionType Instruction; + public ArgumentType Argument1; + public ArgumentType Argument2; + public ArgumentType Argument3; + public PrefixInfo Prefix; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40, ArraySubType = UnmanagedType.U4)] + UInt32[] Reserved_; + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs new file mode 100644 index 000000000..3f298859f --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86ADD : X86Instruction + { + public X86ADD(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[2]; + Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[1] = GetOperand(rawInstruction.Argument2); + } + + public override X86OpCode OpCode { get { return X86OpCode.ADD; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + if (Operands[1] is X86ImmediateOperand) + registers[((X86RegisterOperand) Operands[0]).Register.ToString()] += + ((X86ImmediateOperand) Operands[1]).Immediate; + else + registers[((X86RegisterOperand) Operands[0]).Register.ToString()] += + registers[((X86RegisterOperand) Operands[1]).Register.ToString()]; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs new file mode 100644 index 000000000..d80edf192 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86DIV : X86Instruction + { + public X86DIV(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[2]; + Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[1] = GetOperand(rawInstruction.Argument2); + } + + public override X86OpCode OpCode { get { return X86OpCode.DIV; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs new file mode 100644 index 000000000..3dee0d710 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86IMUL : X86Instruction + { + public X86IMUL(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[3]; + Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[1] =GetOperand( rawInstruction.Argument2); + Operands[2] = GetOperand(rawInstruction.Argument3); + } + + public override X86OpCode OpCode { get { return X86OpCode.IMUL; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + var source = ((X86RegisterOperand) Operands[0]).Register.ToString(); + var target1 = ((X86RegisterOperand) Operands[1]).Register.ToString(); + + if (Operands[2] is X86ImmediateOperand) + registers[source] = registers[target1]*((X86ImmediateOperand) Operands[2]).Immediate; + else + registers[source] = registers[target1]*registers[((X86RegisterOperand) Operands[2]).Register.ToString()]; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs new file mode 100644 index 000000000..fa53c9f98 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + internal class X86MOV : X86Instruction + { + public X86MOV(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[2]; + Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[1] = GetOperand(rawInstruction.Argument2); + } + + public override X86OpCode OpCode + { + get { return X86OpCode.MOV; } + } + + public override void Execute(Dictionary registers, Stack localStack) + { + if (Operands[1] is X86ImmediateOperand) + registers[((X86RegisterOperand) Operands[0]).Register.ToString()] = + (Operands[1] as X86ImmediateOperand).Immediate; + else + { + var regOperand = (X86RegisterOperand) Operands[0]; + registers[regOperand.Register.ToString()] = + registers[(Operands[1] as X86RegisterOperand).Register.ToString()]; + } + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs new file mode 100644 index 000000000..5f1e0d895 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86NEG : X86Instruction + { + public X86NEG(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[1]; + Operands[0] = GetOperand(rawInstruction.Argument1); + } + + public override X86OpCode OpCode { get { return X86OpCode.NEG; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + registers[((X86RegisterOperand) Operands[0]).Register.ToString()] = + -registers[((X86RegisterOperand) Operands[0]).Register.ToString()]; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs new file mode 100644 index 000000000..2a8b6d472 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86NOT : X86Instruction + { + public X86NOT(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[1]; + Operands[0] = GetOperand(rawInstruction.Argument1); + } + + public override X86OpCode OpCode { get { return X86OpCode.NOT; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + registers[((X86RegisterOperand)Operands[0]).Register.ToString()] = + ~registers[((X86RegisterOperand) Operands[0]).Register.ToString()]; + } + + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs new file mode 100644 index 000000000..64d92090c --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86POP : X86Instruction + { + public X86POP(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[1]; + Operands[0] = GetOperand(rawInstruction.Argument1); + } + + public override X86OpCode OpCode { get { return X86OpCode.POP; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + // Pretend to pop stack + if (localStack.Count < 1) + return; + + registers[((X86RegisterOperand) Operands[0]).Register.ToString()] = localStack.Pop(); + } + + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs new file mode 100644 index 000000000..e2d320c81 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86PUSH : X86Instruction + { + public X86PUSH(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[1]; + Operands[0] = GetOperand(rawInstruction.Argument1); + } + + public override X86OpCode OpCode { get { return X86OpCode.PUSH; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + // Pretend to pop stack + if (localStack.Count < 1) + return; + + registers[((X86RegisterOperand) Operands[0]).Register.ToString()] = localStack.Pop(); + } + + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs new file mode 100644 index 000000000..69dbe5b03 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86SUB : X86Instruction + { + public X86SUB(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[2]; + Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[1] = GetOperand(rawInstruction.Argument2); + } + + public override X86OpCode OpCode { get { return X86OpCode.SUB; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + if (Operands[1] is X86ImmediateOperand) + registers[((X86RegisterOperand)Operands[0]).Register.ToString()] -= + ((X86ImmediateOperand)Operands[1]).Immediate; + else + registers[((X86RegisterOperand)Operands[0]).Register.ToString()] -= + registers[((X86RegisterOperand)Operands[1]).Register.ToString()]; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs new file mode 100644 index 000000000..c6bbc6b03 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using de4dot.Bea; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions +{ + class X86XOR : X86Instruction + { + public X86XOR(Disasm rawInstruction) : base() + { + Operands = new IX86Operand[2]; + Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[1] = GetOperand(rawInstruction.Argument2); + } + + public override X86OpCode OpCode { get { return X86OpCode.XOR; } } + + public override void Execute(Dictionary registers, Stack localStack) + { + if (Operands[1] is X86ImmediateOperand) + registers[((X86RegisterOperand)Operands[0]).Register.ToString()] ^= + ((X86ImmediateOperand)Operands[1]).Immediate; + else + registers[((X86RegisterOperand)Operands[0]).Register.ToString()] ^= + registers[((X86RegisterOperand)Operands[1]).Register.ToString()]; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs new file mode 100644 index 000000000..3501ea92c --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.InteropServices; + +namespace de4dot.code.x86 +{ + public class UnmanagedBuffer + { + public readonly IntPtr Ptr = IntPtr.Zero; + public readonly int Length = 0; + + public UnmanagedBuffer(byte[] data) + { + Ptr = Marshal.AllocHGlobal(data.Length); + Marshal.Copy(data, 0, Ptr, data.Length); + Length = data.Length; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs new file mode 100644 index 000000000..87c4840ec --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs @@ -0,0 +1,72 @@ +using de4dot.Bea; +using System.Collections.Generic; +using System.Globalization; + +namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86 +{ + public enum X86OpCode + { + MOV, + ADD, + SUB, + IMUL, + DIV, + NEG, + NOT, + XOR, + POP, + PUSH + } + + public enum X86Register + { + EAX = 537001985, + ECX = 537001986, + EDX = 537001988, + EBX = 537001992, + ESP = 537001989, + EBP = 537001990, + ESI = 537002048, + EDI = 537002112 + } + + public interface IX86Operand + { + } + + public class X86RegisterOperand : IX86Operand + { + public X86Register Register { get; set; } + + public X86RegisterOperand(X86Register reg) + { + Register = reg; + } + } + + public class X86ImmediateOperand : IX86Operand + { + public int Immediate { get; set; } + + public X86ImmediateOperand(int imm) + { + Immediate = imm; + } + } + + public abstract class X86Instruction + { + public abstract X86OpCode OpCode { get; } + public IX86Operand[] Operands { get; set; } + public abstract void Execute(Dictionary registers, Stack localStack); + + public static IX86Operand GetOperand(ArgumentType argument) + { + if (argument.ArgType == -2013265920) + return + new X86ImmediateOperand(int.Parse(argument.ArgMnemonic.TrimEnd('h'), + NumberStyles.HexNumber)); + return new X86RegisterOperand((X86Register)argument.ArgType); + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs new file mode 100644 index 000000000..c7a7cdc7a --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using ConfuserDeobfuscator.Engine.Routines.Ex.x86; +using ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions; +using de4dot.Bea; +using de4dot.code.x86; +using dnlib.DotNet; + +namespace de4dot.code.deobfuscators.ConfuserEx.x86 +{ + public sealed class X86Method + { + public List Instructions; + + public Stack LocalStack = new Stack(); + public Dictionary Registers = new Dictionary + { + {"EAX", 0}, + {"EBX", 0}, + {"ECX", 0}, + {"EDX", 0}, + {"ESP", 0}, + {"EBP", 0}, + {"ESI", 0}, + {"EDI", 0} + }; + + readonly ModuleDefMD _module; + public X86Method(MethodDef method,ModuleDefMD module) + { + this._module = module; + Instructions = new List(); + ParseInstructions(method); + } + + private void ParseInstructions(MethodDef method) + { + var rawInstructions = new List(); + + while (true) + { + byte[] bytes = ReadChunk(method, _module); + + var disasm = new Disasm(); + var buff = new UnmanagedBuffer(bytes); + + disasm.EIP = new IntPtr(buff.Ptr.ToInt32()); + + var instruction = BeaEngine.Disasm(disasm); + _readOffset -= 8 - instruction; // revert offset back for each byte that was not a part of this instruction + var mnemonic = disasm.Instruction.Mnemonic.Trim(); + + if (mnemonic == "ret") //TODO: Check if this is the only return in function, e.g. check for jumps that go beyond this address + { + Marshal.FreeHGlobal(buff.Ptr); + break; + } + + rawInstructions.Add(Clone(disasm)); + //disasm.EIP = new IntPtr(disasm.EIP.ToInt32() + instruction); + + Marshal.FreeHGlobal(buff.Ptr); + } + + //while(rawInstructions.First().Instruction.Mnemonic.Trim() == "pop") + // rawInstructions.Remove(rawInstructions.First()); + + while (rawInstructions.Last().Instruction.Mnemonic.Trim() == "pop") + rawInstructions.Remove(rawInstructions.Last()); + + + foreach (var instr in rawInstructions) + { + switch (instr.Instruction.Mnemonic.Trim()) + { + case "mov": + Instructions.Add(new X86MOV(instr)); + break; + case "add": + Instructions.Add(new X86ADD(instr)); + break; + case "sub": + Instructions.Add(new X86SUB(instr)); + break; + case "imul": + Instructions.Add(new X86IMUL(instr)); + break; + case "div": + Instructions.Add(new X86DIV(instr)); + break; + case "neg": + Instructions.Add(new X86NEG(instr)); + break; + case "not": + Instructions.Add(new X86NOT(instr)); + break; + case "xor": + Instructions.Add(new X86XOR(instr)); + break; + case "pop": + Instructions.Add(new X86POP(instr)); + break; + } + } + } + + private uint _readOffset; + public byte[] ReadChunk(MethodDef method, ModuleDefMD module) + { + var stream = module.Metadata.PEImage.CreateReader(); + var offset = module.Metadata.PEImage.ToFileOffset(method.RVA); + + byte[] buffer = new byte[8]; + + if (_readOffset == 0u) //TODO: Don't use hardcoded offset + _readOffset = (uint) offset + 20u; // skip to actual calculation code + + stream.Position = _readOffset; + + stream.ReadBytes(buffer, 0, 8); // read 8 bytes to make sure that's a whole instruction + _readOffset += 8; + + return buffer; + } + + public int Execute(params int[] @params) + { + foreach (var param in @params) + LocalStack.Push(param); + + foreach (var instr in Instructions) + instr.Execute(Registers, LocalStack); + + return Registers["EAX"]; + } + + + public static Disasm Clone(Disasm disasm) + { + return new Disasm + { + Archi = disasm.Archi, + Argument1 = disasm.Argument1, + Argument2 = disasm.Argument2, + Argument3 = disasm.Argument3, + CompleteInstr = disasm.CompleteInstr, + EIP = disasm.EIP, + Instruction = disasm.Instruction, + Options = disasm.Options, + Prefix = disasm.Prefix, + SecurityBlock = disasm.SecurityBlock, + VirtualAddr = disasm.VirtualAddr + }; + } + } +} diff --git a/de4dot.code/deobfuscators/Lzma.cs b/de4dot.code/deobfuscators/Lzma.cs new file mode 100644 index 000000000..7d4641140 --- /dev/null +++ b/de4dot.code/deobfuscators/Lzma.cs @@ -0,0 +1,688 @@ +using System; +using System.IO; + +namespace de4dot.code.deobfuscators +{ + internal static class Lzma + { + const uint kNumStates = 12; + + const int kNumPosSlotBits = 6; + + const uint kNumLenToPosStates = 4; + + const uint kMatchMinLen = 2; + + const int kNumAlignBits = 4; + const uint kAlignTableSize = 1 << kNumAlignBits; + + const uint kStartPosModelIndex = 4; + const uint kEndPosModelIndex = 14; + + const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2); + + const int kNumPosStatesBitsMax = 4; + const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + + const int kNumLowLenBits = 3; + const int kNumMidLenBits = 3; + const int kNumHighLenBits = 8; + const uint kNumLowLenSymbols = 1 << kNumLowLenBits; + const uint kNumMidLenSymbols = 1 << kNumMidLenBits; + + public static byte[] Decompress(byte[] data) + { + var s = new MemoryStream(data); + var decoder = new LzmaDecoder(); + var prop = new byte[5]; + s.Read(prop, 0, 5); + decoder.SetDecoderProperties(prop); + long outSize = 0; + for (int i = 0; i < 8; i++) + { + int v = s.ReadByte(); + outSize |= ((long)(byte)v) << (8 * i); + } + var b = new byte[(int)outSize]; + var z = new MemoryStream(b, true); + long compressedSize = s.Length - 13; + decoder.Code(s, z, compressedSize, outSize); + return b; + } + + struct BitDecoder + { + public const int kNumBitModelTotalBits = 11; + public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); + const int kNumMoveBits = 5; + + uint Prob; + + public void Init() + { + Prob = kBitModelTotal >> 1; + } + + public uint Decode(Decoder rangeDecoder) + { + uint newBound = (rangeDecoder.Range >> kNumBitModelTotalBits) * Prob; + if (rangeDecoder.Code < newBound) + { + rangeDecoder.Range = newBound; + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 0; + } + rangeDecoder.Range -= newBound; + rangeDecoder.Code -= newBound; + Prob -= (Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 1; + } + } + + struct BitTreeDecoder + { + readonly BitDecoder[] Models; + readonly int NumBitLevels; + + public BitTreeDecoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new BitDecoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + + public uint Decode(Decoder rangeDecoder) + { + uint m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--) + m = (m << 1) + Models[m].Decode(rangeDecoder); + return m - ((uint)1 << NumBitLevels); + } + + public uint ReverseDecode(Decoder rangeDecoder) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + + public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex, + Decoder rangeDecoder, int NumBitLevels) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[startIndex + m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + } + + class Decoder + { + public const uint kTopValue = (1 << 24); + public uint Code; + public uint Range; + public Stream Stream; + + public void Init(Stream stream) + { + // Stream.Init(stream); + Stream = stream; + + Code = 0; + Range = 0xFFFFFFFF; + for (int i = 0; i < 5; i++) + Code = (Code << 8) | (byte)Stream.ReadByte(); + } + + public void ReleaseStream() + { + Stream = null; + } + + public void Normalize() + { + while (Range < kTopValue) + { + Code = (Code << 8) | (byte)Stream.ReadByte(); + Range <<= 8; + } + } + + public uint DecodeDirectBits(int numTotalBits) + { + uint range = Range; + uint code = Code; + uint result = 0; + for (int i = numTotalBits; i > 0; i--) + { + range >>= 1; + /* + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + */ + uint t = (code - range) >> 31; + code -= range & (t - 1); + result = (result << 1) | (1 - t); + + if (range < kTopValue) + { + code = (code << 8) | (byte)Stream.ReadByte(); + range <<= 8; + } + } + Range = range; + Code = code; + return result; + } + } + + class LzmaDecoder + { + readonly BitDecoder[] m_IsMatchDecoders = new BitDecoder[kNumStates << kNumPosStatesBitsMax]; + readonly BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[kNumStates << kNumPosStatesBitsMax]; + readonly BitDecoder[] m_IsRepDecoders = new BitDecoder[kNumStates]; + readonly BitDecoder[] m_IsRepG0Decoders = new BitDecoder[kNumStates]; + readonly BitDecoder[] m_IsRepG1Decoders = new BitDecoder[kNumStates]; + readonly BitDecoder[] m_IsRepG2Decoders = new BitDecoder[kNumStates]; + + readonly LenDecoder m_LenDecoder = new LenDecoder(); + + readonly LiteralDecoder m_LiteralDecoder = new LiteralDecoder(); + readonly OutWindow m_OutWindow = new OutWindow(); + readonly BitDecoder[] m_PosDecoders = new BitDecoder[kNumFullDistances - kEndPosModelIndex]; + readonly BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[kNumLenToPosStates]; + readonly Decoder m_RangeDecoder = new Decoder(); + readonly LenDecoder m_RepLenDecoder = new LenDecoder(); + bool _solid = false; + + uint m_DictionarySize; + uint m_DictionarySizeCheck; + BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(kNumAlignBits); + + uint m_PosStateMask; + + public LzmaDecoder() + { + m_DictionarySize = 0xFFFFFFFF; + for (int i = 0; i < kNumLenToPosStates; i++) + m_PosSlotDecoder[i] = new BitTreeDecoder(kNumPosSlotBits); + } + + void SetDictionarySize(uint dictionarySize) + { + if (m_DictionarySize != dictionarySize) + { + m_DictionarySize = dictionarySize; + m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1); + uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12)); + m_OutWindow.Create(blockSize); + } + } + + void SetLiteralProperties(int lp, int lc) + { + m_LiteralDecoder.Create(lp, lc); + } + + void SetPosBitsProperties(int pb) + { + uint numPosStates = (uint)1 << pb; + m_LenDecoder.Create(numPosStates); + m_RepLenDecoder.Create(numPosStates); + m_PosStateMask = numPosStates - 1; + } + + void Init(Stream inStream, Stream outStream) + { + m_RangeDecoder.Init(inStream); + m_OutWindow.Init(outStream, _solid); + + uint i; + for (i = 0; i < kNumStates; i++) + { + for (uint j = 0; j <= m_PosStateMask; j++) + { + uint index = (i << kNumPosStatesBitsMax) + j; + m_IsMatchDecoders[index].Init(); + m_IsRep0LongDecoders[index].Init(); + } + m_IsRepDecoders[i].Init(); + m_IsRepG0Decoders[i].Init(); + m_IsRepG1Decoders[i].Init(); + m_IsRepG2Decoders[i].Init(); + } + + m_LiteralDecoder.Init(); + for (i = 0; i < kNumLenToPosStates; i++) + m_PosSlotDecoder[i].Init(); + // m_PosSpecDecoder.Init(); + for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + m_PosDecoders[i].Init(); + + m_LenDecoder.Init(); + m_RepLenDecoder.Init(); + m_PosAlignDecoder.Init(); + } + + public void Code(Stream inStream, Stream outStream, + Int64 inSize, Int64 outSize) + { + Init(inStream, outStream); + + var state = new State(); + state.Init(); + uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0; + + UInt64 nowPos64 = 0; + var outSize64 = (UInt64)outSize; + if (nowPos64 < outSize64) + { + m_IsMatchDecoders[state.Index << kNumPosStatesBitsMax].Decode(m_RangeDecoder); + state.UpdateChar(); + byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0); + m_OutWindow.PutByte(b); + nowPos64++; + } + while (nowPos64 < outSize64) + { + // UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64); + // while(nowPos64 < next) + { + uint posState = (uint)nowPos64 & m_PosStateMask; + if (m_IsMatchDecoders[(state.Index << kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + byte b; + byte prevByte = m_OutWindow.GetByte(0); + if (!state.IsCharState()) + b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder, + (uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0)); + else + b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte); + m_OutWindow.PutByte(b); + state.UpdateChar(); + nowPos64++; + } + else + { + uint len; + if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1) + { + if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + if (m_IsRep0LongDecoders[(state.Index << kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + state.UpdateShortRep(); + m_OutWindow.PutByte(m_OutWindow.GetByte(rep0)); + nowPos64++; + continue; + } + } + else + { + UInt32 distance; + if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + distance = rep1; + } + else + { + if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + kMatchMinLen; + state.UpdateRep(); + } + else + { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + len = kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState); + state.UpdateMatch(); + uint posSlot = m_PosSlotDecoder[GetLenToPosState(len)].Decode(m_RangeDecoder); + if (posSlot >= kStartPosModelIndex) + { + var numDirectBits = (int)((posSlot >> 1) - 1); + rep0 = ((2 | (posSlot & 1)) << numDirectBits); + if (posSlot < kEndPosModelIndex) + rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders, + rep0 - posSlot - 1, m_RangeDecoder, numDirectBits); + else + { + rep0 += (m_RangeDecoder.DecodeDirectBits( + numDirectBits - kNumAlignBits) << kNumAlignBits); + rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder); + } + } + else + rep0 = posSlot; + } + if (rep0 >= nowPos64 || rep0 >= m_DictionarySizeCheck) + { + if (rep0 == 0xFFFFFFFF) + break; + } + m_OutWindow.CopyBlock(rep0, len); + nowPos64 += len; + } + } + } + m_OutWindow.Flush(); + m_OutWindow.ReleaseStream(); + m_RangeDecoder.ReleaseStream(); + } + + public void SetDecoderProperties(byte[] properties) + { + int lc = properties[0] % 9; + int remainder = properties[0] / 9; + int lp = remainder % 5; + int pb = remainder / 5; + UInt32 dictionarySize = 0; + for (int i = 0; i < 4; i++) + dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8); + SetDictionarySize(dictionarySize); + SetLiteralProperties(lp, lc); + SetPosBitsProperties(pb); + } + + static uint GetLenToPosState(uint len) + { + len -= kMatchMinLen; + if (len < kNumLenToPosStates) + return len; + return unchecked((kNumLenToPosStates - 1)); + } + + class LenDecoder + { + readonly BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[kNumPosStatesMax]; + readonly BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[kNumPosStatesMax]; + BitDecoder m_Choice = new BitDecoder(); + BitDecoder m_Choice2 = new BitDecoder(); + BitTreeDecoder m_HighCoder = new BitTreeDecoder(kNumHighLenBits); + uint m_NumPosStates; + + public void Create(uint numPosStates) + { + for (uint posState = m_NumPosStates; posState < numPosStates; posState++) + { + m_LowCoder[posState] = new BitTreeDecoder(kNumLowLenBits); + m_MidCoder[posState] = new BitTreeDecoder(kNumMidLenBits); + } + m_NumPosStates = numPosStates; + } + + public void Init() + { + m_Choice.Init(); + for (uint posState = 0; posState < m_NumPosStates; posState++) + { + m_LowCoder[posState].Init(); + m_MidCoder[posState].Init(); + } + m_Choice2.Init(); + m_HighCoder.Init(); + } + + public uint Decode(Decoder rangeDecoder, uint posState) + { + if (m_Choice.Decode(rangeDecoder) == 0) + return m_LowCoder[posState].Decode(rangeDecoder); + uint symbol = kNumLowLenSymbols; + if (m_Choice2.Decode(rangeDecoder) == 0) + symbol += m_MidCoder[posState].Decode(rangeDecoder); + else + { + symbol += kNumMidLenSymbols; + symbol += m_HighCoder.Decode(rangeDecoder); + } + return symbol; + } + } + + class LiteralDecoder + { + Decoder2[] m_Coders; + int m_NumPosBits; + int m_NumPrevBits; + uint m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && + m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = ((uint)1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Decoder2[numStates]; + for (uint i = 0; i < numStates; i++) + m_Coders[i].Create(); + } + + public void Init() + { + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + for (uint i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + uint GetState(uint pos, byte prevByte) + { + return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); + } + + public byte DecodeNormal(Decoder rangeDecoder, uint pos, byte prevByte) + { + return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); + } + + public byte DecodeWithMatchByte(Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) + { + return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); + } + + struct Decoder2 + { + BitDecoder[] m_Decoders; + + public void Create() + { + m_Decoders = new BitDecoder[0x300]; + } + + public void Init() + { + for (int i = 0; i < 0x300; i++) m_Decoders[i].Init(); + } + + public byte DecodeNormal(Decoder rangeDecoder) + { + uint symbol = 1; + do + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); while (symbol < 0x100); + return (byte)symbol; + } + + public byte DecodeWithMatchByte(Decoder rangeDecoder, byte matchByte) + { + uint symbol = 1; + do + { + uint matchBit = (uint)(matchByte >> 7) & 1; + matchByte <<= 1; + uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | bit; + if (matchBit != bit) + { + while (symbol < 0x100) + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + break; + } + } while (symbol < 0x100); + return (byte)symbol; + } + } + }; + } + + class OutWindow + { + byte[] _buffer; + uint _pos; + Stream _stream; + uint _streamPos; + uint _windowSize; + + public void Create(uint windowSize) + { + if (_windowSize != windowSize) + { + _buffer = new byte[windowSize]; + } + _windowSize = windowSize; + _pos = 0; + _streamPos = 0; + } + + public void Init(Stream stream, bool solid) + { + ReleaseStream(); + _stream = stream; + if (!solid) + { + _streamPos = 0; + _pos = 0; + } + } + + public void ReleaseStream() + { + Flush(); + _stream = null; + Buffer.BlockCopy(new byte[_buffer.Length], 0, _buffer, 0, _buffer.Length); + } + + public void Flush() + { + uint size = _pos - _streamPos; + if (size == 0) + return; + _stream.Write(_buffer, (int)_streamPos, (int)size); + if (_pos >= _windowSize) + _pos = 0; + _streamPos = _pos; + } + + public void CopyBlock(uint distance, uint len) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + for (; len > 0; len--) + { + if (pos >= _windowSize) + pos = 0; + _buffer[_pos++] = _buffer[pos++]; + if (_pos >= _windowSize) + Flush(); + } + } + + public void PutByte(byte b) + { + _buffer[_pos++] = b; + if (_pos >= _windowSize) + Flush(); + } + + public byte GetByte(uint distance) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + return _buffer[pos]; + } + } + + struct State + { + public uint Index; + + public void Init() + { + Index = 0; + } + + public void UpdateChar() + { + if (Index < 4) Index = 0; + else if (Index < 10) Index -= 3; + else Index -= 6; + } + + public void UpdateMatch() + { + Index = (uint)(Index < 7 ? 7 : 10); + } + + public void UpdateRep() + { + Index = (uint)(Index < 7 ? 8 : 11); + } + + public void UpdateShortRep() + { + Index = (uint)(Index < 7 ? 9 : 11); + } + + public bool IsCharState() + { + return Index < 7; + } + } + } +} \ No newline at end of file From f27b520c6ed5f4bb1270efc8bc6e97cd6a9e4588 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Sun, 26 Feb 2023 20:56:52 +0300 Subject: [PATCH 03/14] Add the link to ConfuserEx ReadMe into the main ReadMe --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2eac4b16a..6514adead 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Supported obfuscators/packers There are some other obfuscators/packers that are not in supported list explicitly, so their status is unclear: * Confuser +* ConfuserEx - Merged from https://github.com/ViRb3/de4dot-cex , [read the full ReadMe by @ViRb3](README-CEx.md) for more info. You may need a native library for [BeaEngine](https://github.com/BeaEngine/beaengine) (version `5.3.0`) for your platform to use the corresponding functionality! If you use another version you may need to recompile with the updated [C# bindings](https://github.com/BeaEngine/beaengine/tree/master/headers/C%23%20headers). * Osu * PCL From bd33e285e82c88145774c2755790da4f4e820de5 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Sun, 26 Feb 2023 16:37:26 +0300 Subject: [PATCH 04/14] Get rid of some bizarre bullshit. --- de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs index c7a7cdc7a..af36ca8dc 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs @@ -49,7 +49,7 @@ private void ParseInstructions(MethodDef method) disasm.EIP = new IntPtr(buff.Ptr.ToInt32()); var instruction = BeaEngine.Disasm(disasm); - _readOffset -= 8 - instruction; // revert offset back for each byte that was not a part of this instruction + //_readOffset -= 8 - instruction; // revert offset back for each byte that was not a part of this instruction var mnemonic = disasm.Instruction.Mnemonic.Trim(); if (mnemonic == "ret") //TODO: Check if this is the only return in function, e.g. check for jumps that go beyond this address From e2163af94731dfbcbdd81f5a549c66dbddde31ea Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Sun, 26 Feb 2023 13:32:03 +0300 Subject: [PATCH 05/14] Upgrade BeaEngine C# bindings to the state of https://github.com/BeaEngine/beaengine/commit/39bde3501021aef66831f9dade77d26deefb8027 --- .../ConfuserEx/x86/Bea/Constants.cs | 121 ++++++++++++------ .../ConfuserEx/x86/Bea/Structs.cs | 53 ++++++-- .../ConfuserEx/x86/Instructions/X86ADD.cs | 4 +- .../ConfuserEx/x86/Instructions/X86DIV.cs | 4 +- .../ConfuserEx/x86/Instructions/X86IMUL.cs | 6 +- .../ConfuserEx/x86/Instructions/X86MOV.cs | 4 +- .../ConfuserEx/x86/Instructions/X86NEG.cs | 2 +- .../ConfuserEx/x86/Instructions/X86NOT.cs | 2 +- .../ConfuserEx/x86/Instructions/X86POP.cs | 2 +- .../ConfuserEx/x86/Instructions/X86PUSH.cs | 2 +- .../ConfuserEx/x86/Instructions/X86SUB.cs | 4 +- .../ConfuserEx/x86/Instructions/X86XOR.cs | 4 +- .../ConfuserEx/x86/X86Instruction.cs | 6 +- .../deobfuscators/ConfuserEx/x86/X86Method.cs | 6 +- 14 files changed, 149 insertions(+), 71 deletions(-) diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs index c933f9ac1..af9c74365 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs @@ -3,16 +3,16 @@ namespace de4dot.Bea { public class BeaConstants { - public static int INSTRUCT_LENGTH = 64; + public static int INSTRUCT_LENGTH = 80; public enum SegmentRegister : byte { - ESReg = 1, - DSReg = 2, - FSReg = 3, - GSReg = 4, - CSReg = 5, - SSReg = 6 + ESReg = 0x1, + DSReg = 0x2, + FSReg = 0x4, + GSReg = 0x8, + CSReg = 0x10, + SSReg = 0x20 } public enum PrefixType : byte @@ -28,20 +28,39 @@ public enum InstructionType : uint { GENERAL_PURPOSE_INSTRUCTION = 0x10000, FPU_INSTRUCTION = 0x20000, - MMX_INSTRUCTION = 0x40000, - SSE_INSTRUCTION = 0x80000, - SSE2_INSTRUCTION = 0x100000, - SSE3_INSTRUCTION = 0x200000, - SSSE3_INSTRUCTION = 0x400000, - SSE41_INSTRUCTION = 0x800000, - SSE42_INSTRUCTION = 0x1000000, - SYSTEM_INSTRUCTION = 0x2000000, - VM_INSTRUCTION = 0x4000000, - UNDOCUMENTED_INSTRUCTION = 0x8000000, - AMD_INSTRUCTION = 0x10000000, - ILLEGAL_INSTRUCTION = 0x20000000, - AES_INSTRUCTION = 0x40000000, - CLMUL_INSTRUCTION = 0x80000000, + MMX_INSTRUCTION = 0x30000, + SSE_INSTRUCTION = 0x40000, + SSE2_INSTRUCTION = 0x50000, + SSE3_INSTRUCTION = 0x60000, + SSSE3_INSTRUCTION = 0x70000, + SSE41_INSTRUCTION = 0x80000, + SSE42_INSTRUCTION = 0x90000, + SYSTEM_INSTRUCTION = 0xa0000, + VM_INSTRUCTION = 0xb0000, + UNDOCUMENTED_INSTRUCTION = 0xc0000, + AMD_INSTRUCTION = 0xd0000, + ILLEGAL_INSTRUCTION = 0xe0000, + AES_INSTRUCTION = 0xf0000, + CLMUL_INSTRUCTION = 0x100000, + AVX_INSTRUCTION = 0x110000, + AVX2_INSTRUCTION = 0x120000, + MPX_INSTRUCTION = 0x130000, + AVX512_INSTRUCTION = 0x140000, + SHA_INSTRUCTION = 0x150000, + BMI2_INSTRUCTION = 0x160000, + CET_INSTRUCTION = 0x170000, + BMI1_INSTRUCTION = 0x180000, + XSAVEOPT_INSTRUCTION = 0x190000, + FSGSBASE_INSTRUCTION = 0x1a0000, + CLWB_INSTRUCTION = 0x1b0000, + CLFLUSHOPT_INSTRUCTION = 0x1c0000, + FXSR_INSTRUCTION = 0x1d0000, + XSAVE_INSTRUCTION = 0x1e0000, + SGX_INSTRUCTION = 0x1f0000, + PCONFIG_INSTRUCTION = 0x200000, + UINTR_INSTRUCTION = 0x210000, + KL_INSTRUCTION = 0x220000, + AMX_INSTRUCTION = 0x230000, DATA_TRANSFER = 0x1, ARITHMETIC_INSTRUCTION, @@ -126,20 +145,25 @@ public enum BranchType : short public enum ArgumentType : uint { - NO_ARGUMENT = 0x10000000, - REGISTER_TYPE = 0x20000000, - MEMORY_TYPE = 0x40000000, - CONSTANT_TYPE = 0x80000000, - - MMX_REG = 0x10000, - GENERAL_REG = 0x20000, - FPU_REG = 0x40000, - SSE_REG = 0x80000, - CR_REG = 0x100000, - DR_REG = 0x200000, - SPECIAL_REG = 0x400000, - MEMORY_MANAGEMENT_REG = 0x800000, - SEGMENT_REG = 0x1000000, + NO_ARGUMENT = 0x10000, + REGISTER_TYPE = 0x20000, + MEMORY_TYPE = 0x30000, + CONSTANT_TYPE = 0x40000, + + GENERAL_REG = 0x1, + MMX_REG = 0x2, + SSE_REG = 0x4, + AVX_REG = 0x8, + AVX512_REG = 0x10, + SPECIAL_REG = 0x20, + CR_REG = 0x40, + DR_REG = 0x80, + MEMORY_MANAGEMENT_REG = 0x100, + MPX_REG = 0x200, + OPMASK_REG = 0x400, + SEGMENT_REG = 0x800, + FPU_REG = 0x1000, + TMM_REG = 0x2000, RELATIVE_ = 0x4000000, ABSOLUTE_ = 0x8000000, @@ -162,13 +186,29 @@ public enum ArgumentType : uint REG12 = 0x1000, REG13 = 0x2000, REG14 = 0x4000, - REG15 = 0x8000 + REG15 = 0x8000, + REG16 = 0x10000, + REG17 = 0x20000, + REG18 = 0x40000, + REG19 = 0x80000, + REG20 = 0x100000, + REG21 = 0x200000, + REG22 = 0x400000, + REG23 = 0x800000, + REG24 = 0x1000000, + REG25 = 0x2000000, + REG26 = 0x4000000, + REG27 = 0x8000000, + REG28 = 0x10000000, + REG29 = 0x20000000, + REG30 = 0x40000000, + REG31 = 0x80000000 } public enum SpecialInfo : int { UNKNOWN_OPCODE = -1, - OUT_OF_BLOCK = 0, + OUT_OF_BLOCK = -2, /* === mask = 0xff */ NoTabulation = 0x00000000, @@ -179,13 +219,18 @@ public enum SpecialInfo : int GoAsmSyntax = 0x00000100, NasmSyntax = 0x00000200, ATSyntax = 0x00000400, + IntrinsicMemSyntax= 0x00000800, /* === mask = 0xff0000 */ PrefixedNumeral = 0x00010000, SuffixedNumeral = 0x00000000, /* === mask = 0xff000000 */ - ShowSegmentRegs = 0x01000000 + ShowSegmentRegs = 0x01000000, + ShowEVEXMasking = 0x02000000, + + LowPosition = 0, + HighPosition = 1 } } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs index 4abd66e3d..c5611cef7 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Structs.cs @@ -32,6 +32,8 @@ public class PrefixInfo public byte BranchTaken; public byte BranchNotTaken; public REX_Struct REX; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] + public string alignment; } [StructLayout(LayoutKind.Sequential, Pack = 1)] @@ -51,6 +53,27 @@ public class EFLStruct public byte alignment; } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class RegisterType + { + public Int64 type; + public Int64 gpr; + public Int64 mmx; + public Int64 xmm; + public Int64 ymm; + public Int64 zmm; + public Int64 special; + public Int64 cr; + public Int64 dr; + public Int64 mem_management; + public Int64 mpx; + public Int64 opmask; + public Int64 segment; + public Int64 fpu; + public Int64 tmm; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] public class MemoryType { @@ -65,24 +88,27 @@ public class InstructionType { public Int32 Category; public Int32 Opcode; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)] public string Mnemonic; public Int32 BranchType; public EFLStruct Flags; public UInt64 AddrValue; public Int64 Immediat; - public UInt32 ImplicitModifiedRegs; + public RegisterType ImplicitModifiedRegs; + public RegisterType ImplicitUsedRegs; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public class ArgumentType { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] - public string ArgMnemonic; - public Int32 ArgType; - public Int32 ArgSize; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)] + public string OpMnemonic; + public Int32 OpType; + public Int32 OpSize; + public Int32 OpPosition; public UInt32 AccessMode; public MemoryType Memory; + public RegisterType Registers; public UInt32 SegmentReg; } @@ -97,11 +123,18 @@ public class Disasm public UInt32 Archi; public UInt64 Options; public InstructionType Instruction; - public ArgumentType Argument1; - public ArgumentType Argument2; - public ArgumentType Argument3; + public ArgumentType Operand1; + public ArgumentType Operand2; + public ArgumentType Operand3; + public ArgumentType Operand4; + public ArgumentType Operand5; + public ArgumentType Operand6; + public ArgumentType Operand7; + public ArgumentType Operand8; + public ArgumentType Operand9; public PrefixInfo Prefix; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40, ArraySubType = UnmanagedType.U4)] + public Int32 Error; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48, ArraySubType = UnmanagedType.U4)] UInt32[] Reserved_; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs index 3f298859f..e972a46e1 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs @@ -8,8 +8,8 @@ class X86ADD : X86Instruction public X86ADD(Disasm rawInstruction) : base() { Operands = new IX86Operand[2]; - Operands[0] = GetOperand(rawInstruction.Argument1); - Operands[1] = GetOperand(rawInstruction.Argument2); + Operands[0] = GetOperand(rawInstruction.Operand1); + Operands[1] = GetOperand(rawInstruction.Operand2); } public override X86OpCode OpCode { get { return X86OpCode.ADD; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs index d80edf192..f2a1c3bd7 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs @@ -8,8 +8,8 @@ class X86DIV : X86Instruction public X86DIV(Disasm rawInstruction) : base() { Operands = new IX86Operand[2]; - Operands[0] = GetOperand(rawInstruction.Argument1); - Operands[1] = GetOperand(rawInstruction.Argument2); + Operands[0] = GetOperand(rawInstruction.Operand1); + Operands[1] = GetOperand(rawInstruction.Operand2); } public override X86OpCode OpCode { get { return X86OpCode.DIV; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs index 3dee0d710..f5668cf75 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs @@ -8,9 +8,9 @@ class X86IMUL : X86Instruction public X86IMUL(Disasm rawInstruction) : base() { Operands = new IX86Operand[3]; - Operands[0] = GetOperand(rawInstruction.Argument1); - Operands[1] =GetOperand( rawInstruction.Argument2); - Operands[2] = GetOperand(rawInstruction.Argument3); + Operands[0] = GetOperand(rawInstruction.Operand1); + Operands[1] =GetOperand( rawInstruction.Operand2); + Operands[2] = GetOperand(rawInstruction.Operand3); } public override X86OpCode OpCode { get { return X86OpCode.IMUL; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs index fa53c9f98..145c04231 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs @@ -8,8 +8,8 @@ internal class X86MOV : X86Instruction public X86MOV(Disasm rawInstruction) : base() { Operands = new IX86Operand[2]; - Operands[0] = GetOperand(rawInstruction.Argument1); - Operands[1] = GetOperand(rawInstruction.Argument2); + Operands[0] = GetOperand(rawInstruction.Operand1); + Operands[1] = GetOperand(rawInstruction.Operand2); } public override X86OpCode OpCode diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs index 5f1e0d895..b28d91007 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs @@ -8,7 +8,7 @@ class X86NEG : X86Instruction public X86NEG(Disasm rawInstruction) : base() { Operands = new IX86Operand[1]; - Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[0] = GetOperand(rawInstruction.Operand1); } public override X86OpCode OpCode { get { return X86OpCode.NEG; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs index 2a8b6d472..07e8877bf 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs @@ -8,7 +8,7 @@ class X86NOT : X86Instruction public X86NOT(Disasm rawInstruction) : base() { Operands = new IX86Operand[1]; - Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[0] = GetOperand(rawInstruction.Operand1); } public override X86OpCode OpCode { get { return X86OpCode.NOT; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs index 64d92090c..251509a15 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs @@ -8,7 +8,7 @@ class X86POP : X86Instruction public X86POP(Disasm rawInstruction) : base() { Operands = new IX86Operand[1]; - Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[0] = GetOperand(rawInstruction.Operand1); } public override X86OpCode OpCode { get { return X86OpCode.POP; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs index e2d320c81..d893c5992 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs @@ -8,7 +8,7 @@ class X86PUSH : X86Instruction public X86PUSH(Disasm rawInstruction) : base() { Operands = new IX86Operand[1]; - Operands[0] = GetOperand(rawInstruction.Argument1); + Operands[0] = GetOperand(rawInstruction.Operand1); } public override X86OpCode OpCode { get { return X86OpCode.PUSH; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs index 69dbe5b03..d43e0281b 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs @@ -8,8 +8,8 @@ class X86SUB : X86Instruction public X86SUB(Disasm rawInstruction) : base() { Operands = new IX86Operand[2]; - Operands[0] = GetOperand(rawInstruction.Argument1); - Operands[1] = GetOperand(rawInstruction.Argument2); + Operands[0] = GetOperand(rawInstruction.Operand1); + Operands[1] = GetOperand(rawInstruction.Operand2); } public override X86OpCode OpCode { get { return X86OpCode.SUB; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs index c6bbc6b03..1b6351f7b 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs @@ -8,8 +8,8 @@ class X86XOR : X86Instruction public X86XOR(Disasm rawInstruction) : base() { Operands = new IX86Operand[2]; - Operands[0] = GetOperand(rawInstruction.Argument1); - Operands[1] = GetOperand(rawInstruction.Argument2); + Operands[0] = GetOperand(rawInstruction.Operand1); + Operands[1] = GetOperand(rawInstruction.Operand2); } public override X86OpCode OpCode { get { return X86OpCode.XOR; } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs index 87c4840ec..9b94573c7 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs @@ -62,11 +62,11 @@ public abstract class X86Instruction public static IX86Operand GetOperand(ArgumentType argument) { - if (argument.ArgType == -2013265920) + if (argument.OpType == -2013265920) return - new X86ImmediateOperand(int.Parse(argument.ArgMnemonic.TrimEnd('h'), + new X86ImmediateOperand(int.Parse(argument.OpMnemonic.TrimEnd('h'), NumberStyles.HexNumber)); - return new X86RegisterOperand((X86Register)argument.ArgType); + return new X86RegisterOperand((X86Register)argument.OpType); } } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs index af36ca8dc..81842623f 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs @@ -142,9 +142,9 @@ public static Disasm Clone(Disasm disasm) return new Disasm { Archi = disasm.Archi, - Argument1 = disasm.Argument1, - Argument2 = disasm.Argument2, - Argument3 = disasm.Argument3, + Operand1 = disasm.Operand1, + Operand2 = disasm.Operand2, + Operand3 = disasm.Operand3, CompleteInstr = disasm.CompleteInstr, EIP = disasm.EIP, Instruction = disasm.Instruction, From d5825e126aa9855bc4e0cae48e921e8e4a7ede6d Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Tue, 25 Jul 2017 17:37:41 +0300 Subject: [PATCH 06/14] ConfuserEx deobfuscator updates and misc changes ConfuserEx changes: * Implement Proxy Call Fixer * Refactor Control Flow Fixer Disable main exception handler to let de4dot throw on error --- .../ConfuserEx/ConstantDecrypter.cs | 404 +++++------ ...ntrolFlowSolver.cs => ControlFlowFixer.cs} | 632 +++++++++--------- .../deobfuscators/ConfuserEx/Deobfuscator.cs | 146 ++-- .../ConfuserEx/ProxyCallFixer.cs | 415 ++++++++++++ .../ConfuserEx/ResourceDecrypter.cs | 2 +- .../deobfuscators/ProxyCallFixerBase.cs | 238 +++++++ de4dot.cui/Program.cs | 1 + 7 files changed, 1230 insertions(+), 608 deletions(-) rename de4dot.code/deobfuscators/ConfuserEx/{ControlFlowSolver.cs => ControlFlowFixer.cs} (94%) create mode 100644 de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs index bb001de13..88f3001a9 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs @@ -207,11 +207,11 @@ private T[] ConvertArray(T1[] array) return buffer; } - private void DecryptArray(uint[] array) + private void DecryptArray(uint[] array) //TODO: Automatic detection { - uint num = 1680u; // array size? + uint num = 960u; // array size? uint[] array2 = new uint[16]; - uint num2 = 3186233426u; + uint num2 = 4136251032u; for (int i = 0; i < 16; i++) { num2 ^= num2 >> 12; @@ -229,237 +229,189 @@ private void DecryptArray(uint[] array) { array3[j] = array[num3 + j]; } - uint num5 = array3[9] >> 23; - array3[0] = array3[0] * 865957733u; - array3[9] = array3[9] << 9; - array3[9] = (array3[9] | num5); - array3[3] = (array3[3] ^ 4272581837u); - array3[8] = (array3[8] ^ array2[8]); - num5 = (array3[14] & 3955221806u); - num5 *= 1105615415u; - array3[14] = (array3[14] & 339745489u); - array3[14] = (array3[14] | (array3[0] & 3955221806u)); - array3[15] = (array3[15] ^ array3[8]); - array3[0] = (array3[0] & 339745489u); - array3[0] = (array3[0] | num5 * 809663367u); - array3[14] = (array3[14] ^ 1753824488u); - num5 = array3[11] << 18; - array3[11] = array3[11] >> 14; - array3[1] = array3[1] - 301755752u; - array3[11] = (array3[11] | num5); - uint num6 = array3[14] << 3; - num5 = (array3[9] & 2370496838u); - array3[10] = array3[10] - 4147007853u; - uint num7 = array3[14] << 4; - num5 *= 2575732745u; - num6 += array3[14] << 4; - array3[9] = (array3[9] & 1924470457u); - array3[9] = (array3[9] | (array3[2] & 2370496838u)); - array3[12] = array3[12] - array3[6]; - array3[2] = (array3[2] & 1924470457u); - array3[0] = array3[0] - array3[4]; - array3[2] = (array3[2] | num5 * 593100345u); - num5 = array3[6] * 993944645u; - array3[6] = array3[5]; - uint num8 = array3[14] * 19u; - array3[5] = num5 * 679153293u; - num5 = (array3[1] & 4141471236u); - num8 += array3[5] * 67u; - num8 += array3[10] * 43u; - array3[13] = (array3[13] ^ array2[13]); - array3[1] = (array3[1] & 153496059u); - num6 += array3[5] * 92u; - num7 += array3[5] * 57u; - num7 += array3[10] * 37u; - num5 *= 831032307u; - array3[1] = (array3[1] | (array3[12] & 4141471236u)); - array3[0] = (array3[0] ^ array2[0]); - array3[12] = (array3[12] & 153496059u); - array3[11] = (array3[11] ^ array2[11]); - num6 += array3[10] << 6; - array3[12] = (array3[12] | num5 * 419693883u); - num7 += array3[15] * 107u; - array3[3] = (array3[3] ^ array2[3]); - num5 = (array3[13] & 2032982899u); - array3[13] = (array3[13] & 2261984396u); - num5 *= 3754449215u; - num8 += array3[15] * 125u; - num6 += array3[15] * 179u; - array3[13] = (array3[13] | (array3[7] & 2032982899u)); - array3[7] = (array3[7] & 2261984396u); - array3[7] = (array3[7] | num5 * 1302730431u); - num5 = array3[14] * 7u; - num5 += array3[5] * 25u; - array3[12] = (array3[12] ^ ~array3[4]); - num5 += array3[10] << 4; - array3[5] = num6; + uint num5 = array3[3] * 41u; + array3[11] = (array3[11] ^ 3634844963u); + uint num6 = array3[3] * 31u; + num6 += array3[9] * 47u; + num5 += array3[9] * 85u; + num5 += array3[10] * 149u; + uint num7 = array3[3] << 1; + num7 += array3[3]; + uint num8 = array3[3] << 1; + num8 += array3[3] << 3; + num7 += array3[9] << 3; + num8 += array3[9] * 13u; + num7 += array3[9]; + num6 += array3[10] * 71u; + num7 += array3[10] << 1; + num6 += array3[1] * 81u; + array3[4] = (array3[4] ^ ~array3[6]); + num8 += array3[10] << 1; + num7 += array3[10] << 4; + array3[9] = num6; + num8 += array3[10] << 4; + array3[6] = array3[6] * 395315459u; + num8 += array3[1] * 19u; + num7 += array3[1] * 23u; + num5 += array3[1] * 184u; + num6 = array3[7] * 19u; array3[10] = num7; - num6 = array3[2] >> 19; - num7 = array3[11] >> 19; - num5 += array3[15] * 46u; - array3[15] = num8; - num8 = array3[0] << 2; - array3[2] = array3[2] << 13; - array3[11] = array3[11] << 13; - array3[14] = num5; - array3[0] = array3[0] >> 30; - array3[2] = (array3[2] | num6); - array3[6] = (array3[6] ^ 825592879u); + num6 += array3[8] * 28u; + array3[14] = (array3[14] ^ array3[0]); + array3[3] = num8; + num6 += array3[12] << 6; + array3[1] = num5; array3[2] = (array3[2] ^ array2[2]); - array3[11] = (array3[11] | num7); - num5 = array3[15] << 1; - array3[0] = (array3[0] | num8); - num8 = array3[15] * 23u; - num7 = array3[15] * 7u; - num8 += array3[7] * 45u; - num7 += array3[7] * 11u; - num5 += array3[15]; - array3[9] = (array3[9] ^ array2[9]); - num5 += array3[7] * 7u; - num8 += array3[13] << 7; - array3[3] = (array3[3] ^ ~array3[8]); - array3[10] = array3[10] * 2256145475u; - num6 = array3[15] << 2; - num6 += array3[15]; - num7 += array3[13] << 5; - num7 += array3[1] << 1; - num6 += array3[7] << 2; - num6 += array3[7] << 3; - num8 += array3[13]; - num8 += array3[1] * 143u; - num5 += array3[13] << 2; - num6 += array3[13] << 1; - num7 += array3[1] << 5; - num5 += array3[13] << 4; + num5 = array3[7] * 28u; + num5 += array3[8] << 2; + num8 = array3[7] << 1; + num7 = array3[7] << 5; + num8 += array3[7] << 3; + num8 += array3[8] * 13u; + num7 += array3[7]; + num6 += array3[12]; + num7 += array3[8] * 42u; + array3[4] = array3[4] - array3[10]; + num8 += array3[12] << 5; + num6 += array3[15] * 85u; + num5 += array3[8] << 5; + array3[7] = num6; + array3[11] = array3[11] - 2867139633u; + num7 += array3[12] * 108u; + num5 += array3[12] * 93u; + num8 += array3[12]; + num5 += array3[15] * 141u; + num8 += array3[15] * 49u; + num7 += array3[15] * 163u; + array3[12] = num5; array3[15] = num7; - num5 += array3[1] * 23u; - num6 += array3[13] << 5; - array3[7] = num5; - num6 += array3[1] * 39u; - array3[1] = num8; - num8 = array3[1] * 26u; - num7 = array3[1] << 6; + array3[8] = num8; + num5 = array3[7] >> 21; + num6 = array3[15] >> 22; + array3[15] = array3[15] << 10; + num8 = array3[1] >> 21; + array3[15] = (array3[15] | num6); + array3[12] = (array3[12] ^ array2[12]); + num6 = (array3[2] & 3262151220u); + array3[1] = array3[1] << 11; + array3[1] = (array3[1] | num8); + array3[7] = array3[7] << 11; + array3[0] = array3[0] - array3[14]; + num7 = array3[13] << 4; + num8 = array3[3] * 954284655u; + array3[3] = array3[5]; + array3[5] = num8 * 3102958735u; + array3[7] = (array3[7] | num5); + num5 = array3[10] << 4; + num8 = array3[9] * 2468501497u; + array3[2] = (array3[2] & 1032816075u); + array3[13] = array3[13] >> 28; + array3[13] = (array3[13] | num7); + array3[7] = array3[7] - 888060325u; + array3[2] = (array3[2] | (array3[8] & 3262151220u)); + array3[12] = array3[12] * 4056148675u; + array3[9] = array3[13]; + num7 = array3[6] << 5; + array3[13] = num8 * 1746582089u; + array3[6] = array3[6] >> 27; + array3[6] = (array3[6] | num7); + array3[8] = (array3[8] & 1032816075u); array3[7] = (array3[7] ^ array2[7]); - array3[13] = num6; - num5 = array3[1] << 1; - num5 += array3[1] << 3; - num6 = array3[1] * 13u; - num5 += array3[13] << 1; - num7 += array3[1]; - num6 += array3[13] * 45u; - array3[9] = (array3[9] ^ 786150263u); - num8 += array3[13] * 88u; - num6 += array3[2] * 67u; - array3[8] = (array3[8] ^ 110539985u); - num5 += array3[13] << 5; - num6 += array3[11] << 1; - num8 += array3[2] * 133u; - array3[10] = (array3[10] ^ array2[10]); - num8 += array3[11] << 6; - array3[15] = array3[15] - 2992485470u; - array3[0] = array3[0] - array3[6]; - num6 += array3[11] << 5; - num5 += array3[2] * 51u; - num5 += array3[11] * 25u; - array3[12] = (array3[12] ^ ~array3[3]); - num7 += array3[13] * 222u; - array3[13] = num5; - array3[1] = num6; - array3[13] = array3[13] * 3578289835u; - num6 = (array3[10] & 381620437u); - array3[10] = (array3[10] & 3913346858u); - num5 = array3[0] * 14u; - num7 += array3[2] * 333u; - array3[2] = num8; - num5 += array3[3] * 11u; - array3[10] = (array3[10] | (array3[14] & 381620437u)); - array3[7] = (array3[7] ^ ~array3[4]); - num6 *= 3323466531u; - array3[14] = (array3[14] & 3913346858u); - num8 = array3[0] << 2; - num5 += array3[5] * 54u; - array3[14] = (array3[14] | num6 * 1991488651u); - num7 += array3[11] * 164u; - num6 = (array3[2] & 2341248020u); + num5 += array3[11] * 46u; + num6 *= 869722291u; + num8 = array3[10] << 1; + num5 += array3[3] * 92u; + num5 += array3[5] * 149u; + array3[7] = array3[7] - 3922202313u; + array3[8] = (array3[8] | num6 * 2576221819u); + num8 += array3[11] * 15u; + num8 += array3[3] * 37u; + num6 = array3[10] * 7u; + array3[8] = (array3[8] ^ 1878284212u); + num8 += array3[5] * 56u; + array3[9] = (array3[9] ^ array2[9]); + num7 = array3[10] << 3; + array3[6] = (array3[6] ^ 2841119440u); + num6 += array3[11] << 4; + array3[2] = (array3[2] ^ 217219923u); + num7 += array3[10]; + num6 += array3[3] * 29u; + array3[6] = (array3[6] ^ array2[6]); + num7 += array3[11] * 26u; + num7 += array3[3] * 52u; + num6 += array3[5] * 49u; + num7 += array3[5] * 84u; + array3[3] = num5; + array3[10] = num6; + num6 = array3[1] * 15u; + array3[12] = (array3[12] ^ 1080861703u); + array3[5] = num8; + num5 = (array3[4] & 3659960635u); + num6 += array3[12] << 1; + array3[4] = (array3[4] & 635006660u); + array3[4] = (array3[4] | (array3[9] & 3659960635u)); + num5 *= 1676034815u; array3[11] = num7; - num7 = array3[0] * 11u; - num8 += array3[0] << 4; - array3[2] = (array3[2] & 1953719275u); - num8 += array3[3] << 2; - array3[2] = (array3[2] | (array3[11] & 2341248020u)); - num8 += array3[3] << 4; - num6 *= 4030567715u; - array3[14] = (array3[14] ^ array2[14]); - array3[11] = (array3[11] & 1953719275u); - array3[11] = (array3[11] | num6 * 62866059u); - num6 = array3[0] << 2; - num8 += array3[5] * 90u; - num7 += array3[3] << 4; - num7 += array3[5] << 2; - num6 += array3[0]; - array3[12] = (array3[12] ^ array2[12]); - num7 += array3[5] << 6; - num8 += array3[13] * 117u; - array3[9] = (array3[9] ^ array3[5]); - num5 += array3[13] * 52u; - num6 += array3[3] << 1; + num7 = array3[1] * 19u; + num6 += array3[12] << 4; + array3[9] = (array3[9] & 635006660u); + num6 += array3[3] << 6; + num7 += array3[12] * 27u; + array3[5] = array3[5] - array3[8]; + array3[9] = (array3[9] | num5 * 1267776767u); + num5 = array3[1] << 2; + num5 += array3[1]; + array3[13] = (array3[13] ^ array2[13]); + num8 = array3[1]; num6 += array3[3]; - num7 += array3[13] * 126u; - num6 += array3[5] << 4; - num6 += array3[5]; - array3[5] = num8; - array3[3] = num5; - num6 += array3[13] * 11u; - array3[0] = num6; - array3[13] = num7; - num6 = array3[6] << 1; - num6 += array3[15] << 1; - num5 = array3[12] << 29; - num6 += array3[15] << 2; - num7 = array3[7] << 12; - array3[11] = array3[11] - array3[10]; - array3[7] = array3[7] >> 20; - array3[12] = array3[12] >> 3; - array3[12] = (array3[12] | num5); - array3[14] = (array3[14] ^ ~array3[8]); - array3[1] = (array3[1] ^ array2[1]); - array3[1] = (array3[1] ^ 3215842197u); - num8 = array3[6] * 7u; - array3[7] = (array3[7] | num7); - num8 += array3[15] * 26u; - num5 = array3[6] << 2; - num5 += array3[15] << 2; - array3[9] = array3[9] - array3[2]; - num7 = array3[6] << 2; - array3[4] = (array3[4] ^ array2[4]); - num6 += array3[4] << 4; - array3[3] = (array3[3] ^ 1425746098u); + num5 += array3[12] << 3; + num8 += array3[12] << 1; + num8 += array3[12]; + num6 += array3[15] * 22u; + num5 += array3[3] * 27u; + num5 += array3[15] << 3; + num7 += array3[3] * 92u; + num8 += array3[3] << 3; + num8 += array3[3]; num5 += array3[15]; - num8 += array3[4] * 69u; - num5 += array3[4] * 15u; - num7 += array3[6]; - num6 += array3[1] * 15u; - num8 += array3[1] * 63u; - array3[6] = num6; - num7 += array3[15] * 11u; - num7 += array3[4] * 31u; - num7 += array3[1] * 30u; - num5 += array3[1] << 4; - array3[5] = (array3[5] ^ array2[5]); - array3[4] = num7; - num7 = (array3[5] & 2375297997u); - array3[6] = (array3[6] ^ array2[6]); - num7 *= 3574473459u; - array3[15] = num5; - array3[5] = (array3[5] & 1919669298u); - array3[5] = (array3[5] | (array3[13] & 2375297997u)); + num8 += array3[15] << 1; + num8 += array3[15]; + array3[3] = num6; + array3[0] = (array3[0] ^ array3[13]); + array3[14] = array3[14] - array3[15]; + num7 += array3[15] << 5; + array3[13] = (array3[13] ^ ~array3[1]); + num6 = array3[10] >> 31; + array3[14] = (array3[14] ^ array2[14]); + array3[8] = (array3[8] ^ array2[8]); + array3[12] = num5; array3[1] = num8; + array3[5] = (array3[5] ^ array2[5]); + array3[11] = (array3[11] ^ array2[11]); + num5 = (array3[11] & 2204625944u); + array3[1] = (array3[1] ^ array2[1]); + array3[4] = (array3[4] ^ array2[4]); + array3[11] = (array3[11] & 2090341351u); + array3[11] = (array3[11] | (array3[4] & 2204625944u)); + array3[15] = num7; + num8 = (array3[14] & 2496954112u); + array3[14] = (array3[14] & 1798013183u); + array3[4] = (array3[4] & 2090341351u); array3[15] = (array3[15] ^ array2[15]); - num8 = array3[0] << 5; - array3[13] = (array3[13] & 1919669298u); - array3[13] = (array3[13] | num7 * 2683487803u); - array3[0] = array3[0] >> 27; - array3[0] = (array3[0] | num8); + array3[10] = array3[10] << 1; + num5 *= 338764649u; + array3[14] = (array3[14] | (array3[9] & 2496954112u)); + array3[15] = array3[15] - array3[0]; + array3[10] = (array3[10] | num6); + array3[10] = (array3[10] ^ array2[10]); + array3[3] = (array3[3] ^ array2[3]); + num8 *= 2292397853u; + array3[0] = (array3[0] ^ array2[0]); + array3[0] = (array3[0] ^ 2814140307u); + array3[2] = (array3[2] ^ ~array3[13]); + array3[4] = (array3[4] | num5 * 587046105u); + array3[9] = (array3[9] & 1798013183u); + array3[9] = (array3[9] | num8 * 1520255797u); for (int k = 0; k < 16; k++) { uint num9 = array3[k]; @@ -471,7 +423,7 @@ private void DecryptArray(uint[] array) } num3 += 16; } - decryptedBytes = Lzma.Decompress(array4); + decryptedBytes = Lzma.Decompress(array4); } private void FindStringDecrypters(TypeDef type) diff --git a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs similarity index 94% rename from de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs rename to de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs index dcc218dfd..50b57c122 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs @@ -1,322 +1,310 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using de4dot.blocks; -using de4dot.blocks.cflow; -using de4dot.code.deobfuscators.ConfuserEx.x86; -using dnlib.DotNet; -using dnlib.DotNet.Emit; - -namespace de4dot.code.deobfuscators.ConfuserEx -{ - class ControlFlowSolver : IBlocksDeobfuscator - { - public bool ExecuteIfNotModified { get; } - private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); - - private Blocks _blocks; - private X86Method _nativeMethod; - private Local _switchKey; - - private int? CalculateKey() - { - var popValue = _instructionEmulator.Peek(); - - if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid()) - return null; - - _instructionEmulator.Pop(); - int result = _nativeMethod.Execute(((Int32Value)popValue).Value); - return result; - } - - private int CalculateSwitchCaseIndex(Block block, int nativeKey) - { - _instructionEmulator.Push(new Int32Value(nativeKey)); - _instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1); - - var popValue = _instructionEmulator.Peek(); - _instructionEmulator.Pop(); - return ((Int32Value)popValue).Value; - } - - private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch - { - var targets = switchBlock.Targets; - - _instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value)); - int? key = CalculateKey(); - - if (!key.HasValue) - throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); - - int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); - - if (targets.Count < switchCaseIndex) - throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); - - var targetBlock = targets[switchCaseIndex]; - targetBlock.SwitchData.Key = key; - - switchBlock.Instructions.Clear(); - switchBlock.ReplaceLastNonBranchWithBranch(0, targetBlock); - } - - private void ProcessBlock(List switchCaseBlocks, Block block, Block switchBlock) - { - var targets = switchBlock.Targets; - - _instructionEmulator.Emulate(block.Instructions, 0, block.Instructions.Count); - - if (_instructionEmulator.Peek().IsUnknown()) - throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); - - int? key = CalculateKey(); - - if (!key.HasValue) - throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); - - int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); - - if (targets.Count < switchCaseIndex) - throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); - - var targetBlock = targets[switchCaseIndex]; - targetBlock.SwitchData.Key = key; - - block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them - block.ReplaceLastNonBranchWithBranch(0, targetBlock); - - ProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, key.Value); - - block.Processed = true; - } - - private void ProcessTernaryBlock(List switchCaseBlocks, Block ternaryBlock, Block switchBlock) - { - var targets = switchBlock.Targets; - - for (int i = 0; i < 2; i++) // loop both source blocks - { - var sourceBlock = ternaryBlock.Sources[0]; - - if(ternaryBlock.SwitchData.Key.HasValue) // single instruction: pop -- no key! - SetLocalSwitchKey(ternaryBlock.SwitchData.Key.Value); // set old key for both iterations! - - _instructionEmulator.Emulate(sourceBlock.Instructions, 0, sourceBlock.Instructions.Count); - _instructionEmulator.Emulate(ternaryBlock.Instructions, 0, ternaryBlock.Instructions.Count); - - if (_instructionEmulator.Peek().IsUnknown()) - throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); - - int? key = CalculateKey(); - - if (!key.HasValue) - throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); - - int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); - - if (targets.Count < switchCaseIndex) - throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); - - var targetBlock = targets[switchCaseIndex]; - targetBlock.SwitchData.Key = key; - - sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction()); - sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]); - - ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value); - // the second source block now becomes the first one - } - - //switchCaseBlock.Instructions.Clear(); - ternaryBlock.Add(new Instr(OpCodes.Pop.ToInstruction())); // don't add pop before both iterations have finished - ternaryBlock.Processed = true; - } - - - public void DeobfuscateBegin(Blocks blocks) - { - _blocks = blocks; - _instructionEmulator.Initialize(_blocks, true); - } - - public bool Deobfuscate(List methodBlocks) - { - List switchBlocks = GetSwitchBlocks(methodBlocks); // blocks that contain a switch - int modifications = 0; - - foreach (Block switchBlock in switchBlocks) - { - if (!switchBlock.SwitchData.IsConfuserExSwitch()) - { - Console.WriteLine("Unsupported switch block obfuscation!"); - continue; - } - - if (switchBlock.SwitchData.IsKeyHardCoded) - { - ProcessHardcodedSwitch(switchBlock); - modifications++; - continue; - } - - _switchKey = Instr.GetLocalVar(_blocks.Locals, - switchBlock.Instructions[switchBlock.Instructions.Count - 4]); - - if (DeobfuscateSwitchBlock(methodBlocks, switchBlock)) - modifications++; - } - return modifications > 0; - } - - private bool DeobfuscateSwitchBlock(List methodBlocks, Block switchBlock) - { - List switchFallThroughs = methodBlocks.FindAll(b => b.FallThrough == switchBlock); // blocks that fallthrough to the switch block - _instructionEmulator.Initialize(_blocks, true); //TODO: Remove temporary precaution - - int blocksLeft = switchFallThroughs.Count; // how many blocks left to proccess - int blockIndex = 0; // block that sets the first switch destination - int failedCount = 0; - - while (blocksLeft > 0) - { - if (blockIndex > switchFallThroughs.Count - 1) - { - blockIndex = 0; - } - - if (failedCount > switchFallThroughs.Count) - { - Console.WriteLine("Some blocks couldn't be processed!"); - break; - } - - Block switchCaseBlock = switchFallThroughs[blockIndex]; - - if (switchCaseBlock.Processed) - { - blockIndex++; - continue; - } - - if (NeedSwitchKey(switchCaseBlock)) - { - if (!switchCaseBlock.SwitchData.Key.HasValue) - { - failedCount++; - blockIndex++; - continue; - } - SetLocalSwitchKey(switchCaseBlock.SwitchData.Key.Value); - } - - if (switchCaseBlock.IsTernary()) - { - ProcessTernaryBlock(switchFallThroughs, switchCaseBlock, switchBlock); - } - else - { - ProcessBlock(switchFallThroughs, switchCaseBlock, switchBlock); - } - - failedCount = 0; - blocksLeft--; - blockIndex++; - } - - if (blocksLeft == switchFallThroughs.Count) // Have we modified anything? - return false; - - return true; - } - - - public bool IsSwitchBlock(Block block) - { - if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0) - return false; - - if (!block.SwitchData.IsNative()) - return false; - - _nativeMethod = new X86Method(block.SwitchData.GetNativeMethod(), _blocks.Method.Module as ModuleDefMD); //TODO: Possible null - - return true; - } - - public List GetSwitchBlocks(List blocks) // get the blocks which contain the switch statement - { - List switchBlocks = new List(); - - foreach (Block block in blocks) - if (IsSwitchBlock(block)) - switchBlocks.Add(block); - - return switchBlocks; - } - - - private readonly List _processedFallThroughs = new List(); - - // add the switch key to all appropriate fallthroughs - private void ProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey) - { - DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, switchKey); - _processedFallThroughs.Clear(); - } - - private void DoProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey) - { - if (_processedFallThroughs.Contains(targetBlock)) - return; - - _processedFallThroughs.Add(targetBlock); - - if (targetBlock.FallThrough == switchBlock && switchCaseBlocks.Contains(targetBlock) && !targetBlock.SwitchData.Key.HasValue) - targetBlock.SwitchData.Key = switchKey; - - - var fallThrough = targetBlock.FallThrough; - - if (fallThrough == null) - return; - - if (fallThrough.LastInstr.OpCode != OpCodes.Ret && fallThrough != switchBlock) - DoProcessFallThroughs(switchCaseBlocks, switchBlock, fallThrough, switchKey); - - if (targetBlock.CountTargets() > 1) - foreach (Block targetBlockTarget in targetBlock.Targets) - { - if (targetBlockTarget == switchBlock) - return; - DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlockTarget, switchKey); - } - } - - - private bool NeedSwitchKey(Block block) - { - foreach (var instr in block.Instructions) - if (instr.IsLdloc() && Instr.GetLocalVar(_blocks.Locals, instr) == _switchKey) - return true; - return false; - } - - private int? GetSwitchKey() - { - var val = _instructionEmulator.GetLocal(_switchKey); - if (!val.IsInt32()) - return null; - var value = val as Int32Value; - if (value == null || !value.AllBitsValid()) - return null; - return value.Value; - } - - private void SetLocalSwitchKey(int key) - { - _instructionEmulator.SetLocal(_switchKey, new Int32Value(key)); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using de4dot.blocks; +using de4dot.blocks.cflow; +using de4dot.code.deobfuscators.ConfuserEx.x86; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + class ControlFlowFixer : IBlocksDeobfuscator + { + public bool ExecuteIfNotModified { get; } + public List NativeMethods = new List(); + + private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); + + private Blocks _blocks; + private X86Method _nativeMethod; + private Local _switchKey; + + private int? CalculateKey() + { + var popValue = _instructionEmulator.Peek(); + + if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid()) + return null; + + _instructionEmulator.Pop(); + int result = _nativeMethod.Execute(((Int32Value)popValue).Value); + return result; + } + + private int CalculateSwitchCaseIndex(Block block, int nativeKey) + { + _instructionEmulator.Push(new Int32Value(nativeKey)); + _instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1); + + var popValue = _instructionEmulator.Peek(); + _instructionEmulator.Pop(); + return ((Int32Value)popValue).Value; + } + + private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch + { + var targets = switchBlock.Targets; + _instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value)); + + int? key = CalculateKey(); + if (!key.HasValue) + throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); + + int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + if (targets.Count < switchCaseIndex) + throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); + + var targetBlock = targets[switchCaseIndex]; + targetBlock.SwitchData.Key = key; + + switchBlock.Instructions.Clear(); + switchBlock.ReplaceLastNonBranchWithBranch(0, targetBlock); + } + + private void ProcessBlock(List switchCaseBlocks, Block block, Block switchBlock) + { + var targets = switchBlock.Targets; + _instructionEmulator.Emulate(block.Instructions, 0, block.Instructions.Count); + + if (_instructionEmulator.Peek().IsUnknown()) + throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); + + int? key = CalculateKey(); + if (!key.HasValue) + throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); + + int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + if (targets.Count < switchCaseIndex) + throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); + + var targetBlock = targets[switchCaseIndex]; + targetBlock.SwitchData.Key = key; + + block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them + block.ReplaceLastNonBranchWithBranch(0, targetBlock); + + ProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, key.Value); + block.Processed = true; + } + + private void ProcessTernaryBlock(List switchCaseBlocks, Block ternaryBlock, Block switchBlock) + { + var targets = switchBlock.Targets; + + for (int i = 0; i < 2; i++) // loop both source blocks + { + var sourceBlock = ternaryBlock.Sources[0]; + + if(ternaryBlock.SwitchData.Key.HasValue) // single instruction: pop -- no key! + SetLocalSwitchKey(ternaryBlock.SwitchData.Key.Value); // set old key for both iterations! + + _instructionEmulator.Emulate(sourceBlock.Instructions, 0, sourceBlock.Instructions.Count); + _instructionEmulator.Emulate(ternaryBlock.Instructions, 0, ternaryBlock.Instructions.Count); + + if (_instructionEmulator.Peek().IsUnknown()) + throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); + + int? key = CalculateKey(); + if (!key.HasValue) + throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); + + int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + if (targets.Count < switchCaseIndex) + throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); + + var targetBlock = targets[switchCaseIndex]; + targetBlock.SwitchData.Key = key; + + sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction()); + sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]); + + ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value); + // the second source block now becomes the first one + } + + //switchCaseBlock.Instructions.Clear(); + ternaryBlock.Add(new Instr(OpCodes.Pop.ToInstruction())); // don't add pop before both iterations have finished + ternaryBlock.Processed = true; + } + + + public void DeobfuscateBegin(Blocks blocks) + { + _blocks = blocks; + _instructionEmulator.Initialize(_blocks, true); + } + + public bool Deobfuscate(List methodBlocks) + { + List switchBlocks = GetSwitchBlocks(methodBlocks); // blocks that contain a switch + int modifications = 0; + + foreach (Block switchBlock in switchBlocks) + { + if (!switchBlock.SwitchData.IsConfuserExSwitch()) + { + Console.WriteLine("Unsupported switch block obfuscation!"); + continue; + } + + if (switchBlock.SwitchData.IsKeyHardCoded) + { + ProcessHardcodedSwitch(switchBlock); + modifications++; + continue; + } + + _switchKey = Instr.GetLocalVar(_blocks.Locals, + switchBlock.Instructions[switchBlock.Instructions.Count - 4]); + + if (DeobfuscateSwitchBlock(methodBlocks, switchBlock)) + modifications++; + } + return modifications > 0; + } + + private bool DeobfuscateSwitchBlock(List methodBlocks, Block switchBlock) + { + List switchFallThroughs = methodBlocks.FindAll(b => b.FallThrough == switchBlock); // blocks that fallthrough to the switch block + _instructionEmulator.Initialize(_blocks, true); //TODO: Remove temporary precaution + + int blocksLeft = switchFallThroughs.Count; // how many blocks left to proccess + int blockIndex = 0; // block that sets the first switch destination + int failedCount = 0; + + while (blocksLeft > 0) + { + if (blockIndex > switchFallThroughs.Count - 1) + blockIndex = 0; + + if (failedCount > switchFallThroughs.Count) + { + Console.WriteLine("Some blocks couldn't be processed!"); + break; + } + + Block switchCaseBlock = switchFallThroughs[blockIndex]; + if (switchCaseBlock.Processed) + { + blockIndex++; + continue; + } + + if (NeedSwitchKey(switchCaseBlock)) + { + if (!switchCaseBlock.SwitchData.Key.HasValue) + { + failedCount++; + blockIndex++; + continue; + } + SetLocalSwitchKey(switchCaseBlock.SwitchData.Key.Value); + } + + if (switchCaseBlock.IsTernary()) { + ProcessTernaryBlock(switchFallThroughs, switchCaseBlock, switchBlock); + } + else { + ProcessBlock(switchFallThroughs, switchCaseBlock, switchBlock); + } + + failedCount = 0; + blocksLeft--; + blockIndex++; + } + + if (blocksLeft == switchFallThroughs.Count) // Have we modified anything? + return false; + + return true; + } + + + public bool IsSwitchBlock(Block block) + { + if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0) + return false; + if (!block.SwitchData.IsNative()) + return false; + + MethodDef nativeMethod = block.SwitchData.GetNativeMethod(); + _nativeMethod = new X86Method(nativeMethod, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null + if (!NativeMethods.Contains(nativeMethod)) + NativeMethods.Add(nativeMethod); + + return true; + } + + public List GetSwitchBlocks(List blocks) // get the blocks which contain the switch statement + { + List switchBlocks = new List(); + + foreach (Block block in blocks) + if (IsSwitchBlock(block)) + switchBlocks.Add(block); + + return switchBlocks; + } + + + private readonly List _processedFallThroughs = new List(); + + // add the switch key to all appropriate fallthroughs + private void ProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey) + { + DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, switchKey); + _processedFallThroughs.Clear(); + } + + private void DoProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey) + { + if (_processedFallThroughs.Contains(targetBlock)) + return; + _processedFallThroughs.Add(targetBlock); + + if (targetBlock.FallThrough == switchBlock && switchCaseBlocks.Contains(targetBlock) && !targetBlock.SwitchData.Key.HasValue) + targetBlock.SwitchData.Key = switchKey; + + var fallThrough = targetBlock.FallThrough; + if (fallThrough == null) + return; + + if (fallThrough.LastInstr.OpCode != OpCodes.Ret && fallThrough != switchBlock) + DoProcessFallThroughs(switchCaseBlocks, switchBlock, fallThrough, switchKey); + + if (targetBlock.CountTargets() > 1) + foreach (Block targetBlockTarget in targetBlock.Targets) + { + if (targetBlockTarget == switchBlock) + return; + DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlockTarget, switchKey); + } + } + + + private bool NeedSwitchKey(Block block) + { + foreach (var instr in block.Instructions) + if (instr.IsLdloc() && Instr.GetLocalVar(_blocks.Locals, instr) == _switchKey) + return true; + return false; + } + + private int? GetSwitchKey() + { + var val = _instructionEmulator.GetLocal(_switchKey); + if (!val.IsInt32()) + return null; + var value = val as Int32Value; + if (value == null || !value.AllBitsValid()) + return null; + return value.Value; + } + + private void SetLocalSwitchKey(int key) + { + _instructionEmulator.SetLocal(_switchKey, new Int32Value(key)); + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index 6f5a67fa4..f51db6d37 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -58,12 +58,13 @@ public override IDeobfuscator CreateDeobfuscator() class Deobfuscator : DeobfuscatorBase { - - bool detectedConfuserExAttribute = false, deobfuscating = false; - string version = ""; - LzmaFinder lzmaFinder; - ConstantsDecrypter constantDecrypter; - ResourceDecrypter resourceDecrypter; + private bool _detectedConfuserExAttribute = false, _deobfuscating = false; + private string _version = ""; + private LzmaFinder _lzmaFinder; + private ConstantsDecrypter _constantDecrypter; + private ResourceDecrypter _resourceDecrypter; + private ProxyCallFixer _proxyCallFixer; + private ControlFlowFixer _controlFlowFixer = new ControlFlowFixer(); #region ConstantInliners @@ -97,7 +98,7 @@ public override string TypeLong public override string Name { - get { return $"{TypeLong} {version}"; } + get { return $"{TypeLong} {_version}"; } } public Deobfuscator(Options options) @@ -108,28 +109,33 @@ public Deobfuscator(Options options) protected override int DetectInternal() { int val = 0; - if (detectedConfuserExAttribute) val += 0; - if (lzmaFinder.FoundLzma) val += 10; - if (constantDecrypter.Detected) val += 10; - if (resourceDecrypter.Detected) val += 10; + if (_detectedConfuserExAttribute) val += 0; + if (_lzmaFinder.FoundLzma) val += 10; + if (_constantDecrypter.Detected) val += 10; + if (_resourceDecrypter.Detected) val += 10; return val; } protected override void ScanForObfuscator() { - lzmaFinder = new LzmaFinder(module, DeobfuscatedFile); - lzmaFinder.Find(); - constantDecrypter = new ConstantsDecrypter(module, lzmaFinder.Method, DeobfuscatedFile); - resourceDecrypter = new ResourceDecrypter(module, lzmaFinder.Method, DeobfuscatedFile); - if (lzmaFinder.FoundLzma) + _lzmaFinder = new LzmaFinder(module, DeobfuscatedFile); + _lzmaFinder.Find(); + _constantDecrypter = new ConstantsDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); + _resourceDecrypter = new ResourceDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); + if (_lzmaFinder.FoundLzma) { - constantDecrypter.Find(); - resourceDecrypter.Find(); + _constantDecrypter.Find(); + _resourceDecrypter.Find(); } + + _proxyCallFixer = new ProxyCallFixer(module, DeobfuscatedFile); + _proxyCallFixer.FindDelegateCreatorMethod(); + _proxyCallFixer.Find(); + DetectConfuserExAttribute(); } - public void DetectConfuserExAttribute() + private void DetectConfuserExAttribute() { var versions = new List(); foreach (var attribute in module.CustomAttributes) @@ -143,8 +149,8 @@ public void DetectConfuserExAttribute() var value = argument.Value.ToString(); if (!value.Contains("ConfuserEx")) continue; - detectedConfuserExAttribute = true; - version = value.Replace("ConfuserEx", ""); + _detectedConfuserExAttribute = true; + _version = value.Replace("ConfuserEx", ""); return; } } @@ -152,8 +158,10 @@ public void DetectConfuserExAttribute() public override void DeobfuscateBegin() { - if (constantDecrypter.Detected) + if (_constantDecrypter.Detected) { + Logger.w("Constants encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove + sbyteValueInliner = new SByteValueInliner(); byteValueInliner = new ByteValueInliner(); int16ValueInliner = new Int16ValueInliner(); @@ -165,37 +173,41 @@ public override void DeobfuscateBegin() singleValueInliner = new SingleValueInliner(); doubleValueInliner = new DoubleValueInliner(); arrayValueInliner = new ArrayValueInliner(initializedDataCreator); - foreach (var info in constantDecrypter.Decrypters) + foreach (var info in _constantDecrypter.Decrypters) { staticStringInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptString(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptString(info, gim, (uint) args[0])); sbyteValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptSByte(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptSByte(info, gim, (uint) args[0])); byteValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptByte(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptByte(info, gim, (uint) args[0])); int16ValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptInt16(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptInt16(info, gim, (uint) args[0])); uint16ValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptUInt16(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptUInt16(info, gim, (uint) args[0])); int32ValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptInt32(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptInt32(info, gim, (uint) args[0])); uint32ValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptUInt32(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptUInt32(info, gim, (uint) args[0])); int64ValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptInt64(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptInt64(info, gim, (uint) args[0])); uint64ValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptUInt64(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptUInt64(info, gim, (uint) args[0])); singleValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptSingle(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptSingle(info, gim, (uint) args[0])); doubleValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptDouble(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptDouble(info, gim, (uint) args[0])); arrayValueInliner.Add(info.Method, - (method, gim, args) => constantDecrypter.DecryptArray(info, gim, (uint) args[0])); + (method, gim, args) => _constantDecrypter.DecryptArray(info, gim, (uint) args[0])); } - deobfuscating = true; + _deobfuscating = true; } - if (resourceDecrypter.Detected) - resourceDecrypter.Fix(); + if (_resourceDecrypter.Detected) + { + Logger.w("Resource encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove + _resourceDecrypter.Fix(); + } + base.DeobfuscateBegin(); } @@ -204,9 +216,9 @@ public override IEnumerable BlocksDeobfuscators get { var list = new List(); - list.Add(new ControlFlowSolver()); + list.Add(_controlFlowFixer); - if (deobfuscating && int32ValueInliner != null) + if (_deobfuscating && int32ValueInliner != null) list.Add(new ConstantsInliner(sbyteValueInliner, byteValueInliner, int16ValueInliner, uint16ValueInliner, int32ValueInliner, uint32ValueInliner, int64ValueInliner, uint64ValueInliner, @@ -216,7 +228,7 @@ public override IEnumerable BlocksDeobfuscators } } - bool CanRemoveLzma = true; + bool _canRemoveLzma = true; public override void DeobfuscateEnd() { @@ -224,38 +236,48 @@ public override void DeobfuscateEnd() List toRemoveFromCctor = new List(); - if (constantDecrypter.Detected) + if (_constantDecrypter.Detected) if (CanRemoveStringDecrypterType) { - toRemoveFromCctor.Add(constantDecrypter.Method); - AddMethodToBeRemoved(constantDecrypter.Method, "Constant Decrypter Initializer"); - foreach (var dec in constantDecrypter.Decrypters) + toRemoveFromCctor.Add(_constantDecrypter.Method); + AddMethodToBeRemoved(_constantDecrypter.Method, "Constant Decrypter Initializer"); + foreach (var dec in _constantDecrypter.Decrypters) AddMethodToBeRemoved(dec.Method, "Constant Decrypter Method"); - AddFieldsToBeRemoved(constantDecrypter.Fields, "Constant Decrypter Fields"); - AddTypeToBeRemoved(constantDecrypter.Type, "Array field signature type"); + AddFieldsToBeRemoved(_constantDecrypter.Fields, "Constant Decrypter Fields"); + AddTypeToBeRemoved(_constantDecrypter.Type, "Array field signature type"); } else - CanRemoveLzma = false; + _canRemoveLzma = false; - if (resourceDecrypter.Detected && resourceDecrypter.CanRemoveLzma) + if (_resourceDecrypter.Detected && _resourceDecrypter.CanRemoveLzma) { - toRemoveFromCctor.Add(resourceDecrypter.Method); - AddMethodToBeRemoved(resourceDecrypter.Method, "Resource decrypter Initializer method"); - AddMethodToBeRemoved(resourceDecrypter.AssembyResolveMethod, + toRemoveFromCctor.Add(_resourceDecrypter.Method); + AddMethodToBeRemoved(_resourceDecrypter.Method, "Resource decrypter Initializer method"); + AddMethodToBeRemoved(_resourceDecrypter.AssembyResolveMethod, "Resource decrypter AssemblyResolve method"); - AddFieldsToBeRemoved(resourceDecrypter.Fields, "Constant Decrypter Fields"); - AddTypeToBeRemoved(resourceDecrypter.Type, "Array field signature type"); + AddFieldsToBeRemoved(_resourceDecrypter.Fields, "Constant Decrypter Fields"); + AddTypeToBeRemoved(_resourceDecrypter.Type, "Array field signature type"); } - if (!constantDecrypter.CanRemoveLzma || !resourceDecrypter.CanRemoveLzma) - CanRemoveLzma = false; + if (!_constantDecrypter.CanRemoveLzma || !_resourceDecrypter.CanRemoveLzma) + _canRemoveLzma = false; - if (lzmaFinder.FoundLzma && CanRemoveLzma) + if (_lzmaFinder.FoundLzma && _canRemoveLzma) { - AddMethodToBeRemoved(lzmaFinder.Method, "Lzma Decompress method"); - AddTypesToBeRemoved(lzmaFinder.Types, "Lzma Nested Types"); + AddMethodToBeRemoved(_lzmaFinder.Method, "Lzma Decompress method"); + AddTypesToBeRemoved(_lzmaFinder.Types, "Lzma Nested Types"); } + if (_proxyCallFixer.Detected) + { + AddTypesToBeRemoved(_proxyCallFixer.DelegateTypes, "Proxy delegates"); + AddMethodsToBeRemoved(_proxyCallFixer.DelegateCreatorMethods, "Proxy creator methods"); + AddTypesToBeRemoved(_proxyCallFixer.AttributeTypes, "Proxy creator attributes"); + AddMethodsToBeRemoved(_proxyCallFixer.NativeMethods, "Proxy native methods"); + } + + AddMethodsToBeRemoved(_controlFlowFixer.NativeMethods, "Control flow native methods"); + var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); foreach (var instr in moduleCctor.Body.Instructions) if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef && @@ -273,6 +295,12 @@ public override IEnumerable GetStringDecrypterMethods() var list = new List(); return list; } + + public override void DeobfuscateMethodEnd(Blocks blocks) + { + _proxyCallFixer.Deobfuscate(blocks); + base.DeobfuscateMethodEnd(blocks); + } } } } \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs new file mode 100644 index 000000000..a19e50a40 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs @@ -0,0 +1,415 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using de4dot.blocks; +using de4dot.blocks.cflow; +using de4dot.code.deobfuscators.ConfuserEx.x86; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + class Context + { + public uint FieldToken; + public int ByteNum; + public MethodDef CreateMethod; + + public Context(uint fieldToken, int byteNum, MethodDef createMethod) + { + this.FieldToken = fieldToken; + this.ByteNum = byteNum; // 2nd parameter of the Delegate CreateMethod + this.CreateMethod = createMethod; + } + } + + class ProxyCallFixer : ProxyCallFixer4 + { + public List NativeMethods = new List(); + public List AttributeTypes = new List(); + public List DelegateCreatorMethods = new List(); + + private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); + private readonly ISimpleDeobfuscator _simpleDeobfuscator; + private readonly List _processedMethods = new List(); + + public ProxyCallFixer(ModuleDefMD module, ISimpleDeobfuscator simpleDeobfuscator) : base(module) + { + _simpleDeobfuscator = simpleDeobfuscator; + } + + public ProxyCallFixer(ModuleDefMD module, ProxyCallFixer4 oldOne) : base(module, oldOne) + { + } + + protected override object CheckCctor(TypeDef type, MethodDef cctor) + { + if (!_processedMethods.Contains(cctor)) + { + _simpleDeobfuscator.Deobfuscate(cctor); + _processedMethods.Add(cctor); + } + + List contexts = new List(); + var instructions = cctor.Body.Instructions; + instructions.SimplifyMacros(cctor.Body.Variables, cctor.Parameters); + for (int i = 0; i < instructions.Count; i++) + { + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldtoken, OpCodes.Ldc_I4, OpCodes.Call); + if (instrs == null) + continue; + + uint fieldToken = ((IField) instrs[0].Operand).MDToken.ToUInt32(); + int byteNum = (int)instrs[1].Operand; + var createMethod = instrs[2].Operand as MethodDef; + + if(!DelegateCreatorMethods.Contains(createMethod)) + DelegateCreatorMethods.Add(createMethod); + + contexts.Add(new Context(fieldToken, byteNum, createMethod)); + } + return contexts.Count == 0 ? null : contexts; + } + + private void DeobfuscateIfNeeded(MethodDef method) + { + if (!_processedMethods.Contains(method)) + { + _simpleDeobfuscator.Deobfuscate(method); + _processedMethods.Add(method); + } + } + + private byte[] GetExtraDataToken(byte[] sigData) + { + byte[] extraData = new byte[4]; + + // [original signature] [extra signature] + // ... X C0 X X X + Array.Copy(sigData, sigData.Length - 3, extraData, 1, 3); // last 3 bytes of signature + extraData[0] = sigData[sigData.Length - 5]; // the byte before C0 + Array.Reverse(extraData); // decryptorMethod reads the bytes backwards + return extraData; + } + + protected override void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, out OpCode callOpcode) + { + var contexts = (List) context; + var ctx = contexts.First(c => c.FieldToken == field.MDToken.ToInt32()); + var originalMethod = DotNetUtils.Clone(ctx.CreateMethod); // backup original method and restore because changes are not universal + DeobfuscateIfNeeded(ctx.CreateMethod); + + var instructions = ctx.CreateMethod.Body.Instructions; + var variables = ctx.CreateMethod.Body.Variables; + var parameters = ctx.CreateMethod.Parameters; + + instructions.SimplifyMacros(variables, parameters); + byte[] sigData = module.ReadBlob(ctx.FieldToken); + byte[] extraDataToken = GetExtraDataToken(sigData); + int modifierMDToken = ((CModOptSig)field.FieldType).Modifier.MDToken.ToInt32(); + + ReplaceMetadataToken(ref instructions, modifierMDToken, variables[0]); + ReplaceFieldNameChars(ref instructions, field.Name, variables[0]); + InlineArrays(ref instructions, extraDataToken, variables[1], variables[2]); + RemoveDecrementorBlock(ref instructions, variables[2]); + + int firstInstruction = GetEmulationStartIndex(instructions, variables[1], variables[2]); + int lastInstruction = + instructions.IndexOf( + instructions.First( + i => i.OpCode == OpCodes.Callvirt && i.Operand.ToString().Contains("GetCustomAttributes"))) - 4; + + bool nativeMode = false; + if (instructions[lastInstruction - 1].OpCode == OpCodes.Call) // x86 protection + { + lastInstruction--; // don't try emulating native method + nativeMode = true; + } + + int result = EmulateManagedMethod(ctx.CreateMethod, firstInstruction, lastInstruction); + if (nativeMode) + { + MethodDef nativeMethod = (MethodDef) instructions[lastInstruction].Operand; + if (!NativeMethods.Contains(nativeMethod)) + NativeMethods.Add(nativeMethod); + result = EmulateNativeMethod(nativeMethod, result); + } + + result *= GetMagicNumber(field.CustomAttributes[0]); + calledMethod = module.ResolveMemberRef(new MDToken(result).Rid); + + if(calledMethod == null) + throw new Exception(); + + int charNum = GetCharNum(instructions, parameters.Last()); + callOpcode = GetCallOpCode(calledMethod, charNum, ctx.ByteNum); + + ctx.CreateMethod.Body = originalMethod.Body; // restore + } + + private OpCode GetCallOpCode(IMethod calledMethod, int charNum, int byteNum) + { + if (calledMethod.ResolveMethodDef().IsStatic) { + return OpCodes.Call; + } + + byte charOpCode = (byte)(charNum ^ byteNum); + + if (charOpCode == 0x28) + return OpCodes.Call; + else if (charOpCode == 0x6F) + return OpCodes.Callvirt; + else if (charOpCode == 0x73) + return OpCodes.Newobj; + else + throw new Exception(); + } + + private int EmulateNativeMethod(MethodDef externalMethod, int parameter) + { + var nativeMethod = new X86Method(externalMethod, module); //TODO: Possible null + return nativeMethod.Execute(parameter); + } + + private int EmulateManagedMethod(MethodDef method, int startIndex, int endIndex, params Tuple[] parameters) + { + _instructionEmulator.Initialize(method, false); + foreach(var parameter in parameters) + _instructionEmulator.SetArg(parameter.Item1, new Int32Value(parameter.Item2)); + + for (int i = startIndex; i < endIndex; i++) { + _instructionEmulator.Emulate(method.Body.Instructions[i]); + } + + return ((Int32Value) _instructionEmulator.Pop()).Value; + } + + private int GetMagicNumber(CustomAttribute customAttribute) + { + TypeDef attributeType = customAttribute.AttributeType.ResolveTypeDef(); + if(!AttributeTypes.Contains(attributeType)) + AttributeTypes.Add(attributeType); + + var ctor = attributeType.FindConstructors().First(); + DeobfuscateIfNeeded(ctor); + + int magicNum = Convert.ToInt32(customAttribute.ConstructorArguments[0].Value); + var parameter = new Tuple(); + parameter.Item1 = ctor.Parameters[1]; + parameter.Item2 = magicNum; + + return EmulateManagedMethod(ctor, 3, ctor.Body.Instructions.Count - 2, parameter); + } + + public void FindDelegateCreatorMethod() + { + var globalType = module.GlobalType; + foreach ( + var method in + globalType.Methods.Where( + m => m.Parameters.Count == 2 && m.Parameters[0].Type.TypeName == "RuntimeFieldHandle")) + { + _simpleDeobfuscator.Deobfuscate(method); + SetDelegateCreatorMethod(method); + } + } //TODO: Improve detection + + + /* 0x000005B7 6F1500000A IL_001F: callvirt instance uint8[][mscorlib] System.Reflection.Module::ResolveSignature(int32) + 0x000005BC FE0E0100 IL_0024: stloc.1 + 0x000005C0 FE0C0100 IL_0028: ldloc.1 + 0x000005C4 8E IL_002C: ldlen + 0x000005C5 69 IL_002D: conv.i4 + 0x000005C6 FE0E0200 IL_002E: stloc.2 */ + private int GetEmulationStartIndex(IList instructions, Local localArray, Local localArraySize) + { + for (int i = 0; i < instructions.Count; i++) + { + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Callvirt, OpCodes.Stloc, + OpCodes.Ldloc, OpCodes.Ldlen, OpCodes.Conv_I4, OpCodes.Stloc); + + if (instrs == null) + continue; + if (!instrs[0].Operand.ToString().Contains("ResolveSignature")) + continue; + if ((Local)instrs[1].Operand != localArray) + continue; + if ((Local)instrs[2].Operand != localArray) + continue; + if ((Local)instrs[5].Operand != localArraySize) + continue; + + return i + 6; + } + return -1; + } + + /* 0x000008F3 03 IL_02BB: ldarg.1 + 0x000008F4 61 IL_02BC: xor */ + private int GetCharNum(IList instructions, Parameter byteParam) + { + for (int i = 0; i < instructions.Count; i++) + { + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldarg, OpCodes.Xor); + + if (instrs == null) + continue; + if ((Parameter) instrs[0].Operand != byteParam) + continue; + + return (int) instructions[i - 5].Operand; + } + throw new Exception(); + } + + + private void ReplaceFieldNameChars(ref IList instructions, string fieldName, Local fieldLocal) + { + bool foundInstrs; + do { + foundInstrs = ReplaceFieldNameChar(ref instructions, fieldName, fieldLocal); + } while (foundInstrs); + } + + /* 0x00000375 06 IL_007D: ldloc.0 + 0x00000376 6F1500000A IL_007E: callvirt + 0x0000037B 19 IL_0083: ldc.i4.3 + 0x0000037C 6F1600000A IL_0084: callvirt */ + private bool ReplaceFieldNameChar(ref IList instructions, string fieldName, Local fieldLocal) + { + for (int i = 0; i < instructions.Count; i++) + { + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Callvirt, + OpCodes.Ldc_I4, OpCodes.Callvirt); + + if(instrs == null) + continue; + if ((Local)instrs[0].Operand != fieldLocal) + continue; + if (!instrs[1].Operand.ToString().Contains("get_Name")) + continue; + if (!instrs[3].Operand.ToString().Contains("get_Chars")) + continue; + + int charIndex = (int)instrs[2].Operand; + int @char = fieldName[charIndex]; + + instructions[i].OpCode = OpCodes.Ldc_I4; + instructions[i].Operand = @char; + instructions[i+1].OpCode = OpCodes.Nop; + instructions[i+2].OpCode = OpCodes.Nop; + instructions[i+3].OpCode = OpCodes.Nop; + return true; + } + return false; + } + + /* 0x0000034A 08 IL_0052: ldloc.2 + 0x0000034B 17 IL_0053: ldc.i4.1 + 0x0000034C 59 IL_0054: sub + 0x0000034D 25 IL_0055: dup + 0x0000034E 0C IL_0056: stloc.2 + 0x0000034F 91 IL_0057: ldelem.u1 */ + private void InlineArrays(ref IList instructions, byte[] values, Local localArray, Local localInt) + { + bool foundInstrs; + int i = 0; + do { + foundInstrs = InlineArray(ref instructions, values[i++], localArray, localInt); + } while (i < 4 && foundInstrs); + } + + private bool InlineArray(ref IList instructions, int value, Local localArray, Local localInt) + { + for (int i = 0; i < instructions.Count; i++) + { + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Ldloc, OpCodes.Ldc_I4, + OpCodes.Sub, OpCodes.Dup, OpCodes.Stloc, OpCodes.Ldelem_U1); + + if (instrs == null) + continue; + if ((Local)instrs[0].Operand != localArray) + continue; + if ((Local)instrs[1].Operand != localInt) + continue; + if ((int)instrs[2].Operand != 1) + continue; + if ((Local)instrs[5].Operand != localInt) + continue; + + instructions[i].OpCode = OpCodes.Ldc_I4; + instructions[i].Operand = value; + instructions[i + 1].OpCode = OpCodes.Nop; + instructions[i + 2].OpCode = OpCodes.Nop; + instructions[i + 3].OpCode = OpCodes.Nop; + instructions[i + 4].OpCode = OpCodes.Nop; + instructions[i + 5].OpCode = OpCodes.Nop; + instructions[i + 6].OpCode = OpCodes.Nop; + return true; + } + return false; + } + + /* 0x00000371 08 IL_0079: ldloc.2 + 0x00000372 17 IL_007A: ldc.i4.1 + 0x00000373 59 IL_007B: sub + 0x00000374 0C IL_007C: stloc.2 */ + private void RemoveDecrementorBlock(ref IList instructions, Local localInt) + { + for (int i = 0; i < instructions.Count; i++) + { + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Ldc_I4, OpCodes.Sub, OpCodes.Stloc); + + if (instrs == null) + continue; + if ((Local)instrs[0].Operand != localInt) + continue; + if ((int)instrs[1].Operand != 1) + continue; + if ((Local)instrs[3].Operand != localInt) + continue; + + instructions[i].OpCode = OpCodes.Nop; + instructions[i + 1].OpCode = OpCodes.Nop; + instructions[i + 2].OpCode = OpCodes.Nop; + instructions[i + 3].OpCode = OpCodes.Nop; + return; + } + } + + /* 0x000005CF FE0C0000 IL_0037: ldloc.0 + 0x000005D3 6F1600000A IL_003B: callvirt instance class [mscorlib] System.Type[][mscorlib] System.Reflection.FieldInfo::GetOptionalCustomModifiers() + 0x000005D8 2000000000 IL_0040: ldc.i4.0 + 0x000005DD 9A IL_0045: ldelem.ref + 0x000005DE 6F1400000A IL_0046: callvirt instance int32[mscorlib] System.Reflection.MemberInfo::get_MetadataToken() */ + private void ReplaceMetadataToken(ref IList instructions, int metadataToken, Local fieldLocal) + { + for (int i = 0; i < instructions.Count; i++) + { + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Callvirt, OpCodes.Ldc_I4, + OpCodes.Ldelem_Ref, OpCodes.Callvirt); + + if (instrs == null) + continue; + if ((Local)instrs[0].Operand != fieldLocal) + continue; + if (!instrs[1].Operand.ToString().Contains("GetOptionalCustomModifiers")) + continue; + if ((int)instrs[2].Operand != 0) + continue; + if (!instrs[4].Operand.ToString().Contains("get_MetadataToken")) + continue; + + instructions[i].OpCode = OpCodes.Ldc_I4; + instructions[i].Operand = metadataToken; + instructions[i + 1].OpCode = OpCodes.Nop; + instructions[i + 2].OpCode = OpCodes.Nop; + instructions[i + 3].OpCode = OpCodes.Nop; + instructions[i + 4].OpCode = OpCodes.Nop; + return; + } + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs index 4ab77c4a8..bfa15899a 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs @@ -171,7 +171,7 @@ private T[] ConvertArray(T1[] array) Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); return buffer; } - private void DecryptArray(uint[] array) + private void DecryptArray(uint[] array) //TODO: Automatic detection { int num = array.Length; uint[] array2 = new uint[16]; diff --git a/de4dot.code/deobfuscators/ProxyCallFixerBase.cs b/de4dot.code/deobfuscators/ProxyCallFixerBase.cs index 4a023e3a8..f31066f0d 100644 --- a/de4dot.code/deobfuscators/ProxyCallFixerBase.cs +++ b/de4dot.code/deobfuscators/ProxyCallFixerBase.cs @@ -493,4 +493,242 @@ static IMethod GetCalledMethod(Instr instr) { return instr.Operand as IMethod; } } + + // + // Combines the above 1st and 2nd templates + // + public abstract class ProxyCallFixer4 : ProxyCallFixerBase + { + FieldDefAndDeclaringTypeDict fieldToDelegateInfo = new FieldDefAndDeclaringTypeDict(); + MethodDefAndDeclaringTypeDict proxyMethodToDelegateInfo = new MethodDefAndDeclaringTypeDict(); + + protected ProxyCallFixer4(ModuleDefMD module) + : base(module) + { + } + + protected ProxyCallFixer4(ModuleDefMD module, ProxyCallFixer4 oldOne) + : base(module, oldOne) + { + foreach (var key in oldOne.fieldToDelegateInfo.GetKeys()) + fieldToDelegateInfo.Add(Lookup(key, "Could not find field"), Copy(oldOne.fieldToDelegateInfo.Find(key))); + + foreach (var oldMethod in oldOne.proxyMethodToDelegateInfo.GetKeys()) + { + var oldDi = oldOne.proxyMethodToDelegateInfo.Find(oldMethod); + var method = Lookup(oldMethod, "Could not find proxy method"); + proxyMethodToDelegateInfo.Add(method, Copy(oldDi)); + } + } + + protected void AddDelegateInfo(DelegateInfo di) + { + fieldToDelegateInfo.Add(di.field, di); + } + + protected DelegateInfo GetDelegateInfo(IField field) + { + if (field == null) + return null; + return fieldToDelegateInfo.Find(field); + } + + public void Find() + { + if (delegateCreatorMethods.Count == 0) + return; + + Logger.v("Finding all proxy delegates"); + foreach (var type in GetDelegateTypes()) + { + var cctor = type.FindStaticConstructor(); + if (cctor == null || !cctor.HasBody) + continue; + if (!type.HasFields) + continue; + + object context = CheckCctor(type, cctor); + if (context == null) + continue; + + Logger.v("Found proxy delegate: {0} ({1:X8})", Utils.RemoveNewlines(type), type.MDToken.ToUInt32()); + RemovedDelegateCreatorCalls++; + var fieldToMethod = GetFieldToMethodDictionary(type); + + Logger.Instance.Indent(); + foreach (var field in type.Fields) + { + MethodDef proxyMethod; + bool supportType1 = fieldToMethod.TryGetValue(field, out proxyMethod); + bool supportType2 = field.IsStatic; + + if (!supportType1 && !supportType2) + continue; + + IMethod calledMethod; + OpCode callOpcode; + GetCallInfo(context, field, out calledMethod, out callOpcode); + + if (calledMethod == null) + continue; + + if (supportType1) + { + Add2(proxyMethod, new DelegateInfo(field, calledMethod, callOpcode)); + } + if (supportType2) + { + AddDelegateInfo(new DelegateInfo(field, calledMethod, callOpcode)); + } + + Logger.v("Field: {0}, Opcode: {1}, Method: {2} ({3:X8})", + Utils.RemoveNewlines(field.Name), + callOpcode, + Utils.RemoveNewlines(calledMethod), + calledMethod.MDToken.Raw); + } + Logger.Instance.DeIndent(); + delegateTypesDict[type] = true; + } + } + + protected void Add2(MethodDef method, DelegateInfo di) + { + proxyMethodToDelegateInfo.Add(method, di); + } + + protected abstract object CheckCctor(TypeDef type, MethodDef cctor); + protected abstract void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, out OpCode callOpcode); + + Dictionary GetFieldToMethodDictionary(TypeDef type) + { + var dict = new Dictionary(); + foreach (var method in type.Methods) + { + if (!method.IsStatic || !method.HasBody || method.Name == ".cctor") + continue; + + var instructions = method.Body.Instructions; + for (int i = 0; i < instructions.Count; i++) + { + var instr = instructions[i]; + if (instr.OpCode.Code != Code.Ldsfld) + continue; + var field = instr.Operand as FieldDef; + if (field == null) + continue; + + dict[field] = method; + break; + } + } + return dict; + } + + protected override bool Deobfuscate(Blocks blocks, IList allBlocks) + { + var removeInfos = new Dictionary>(); + + foreach (var block in allBlocks) + { + var instrs = block.Instructions; + for (int i = 0; i < instrs.Count; i++) + { + var instr = instrs[i]; + + if (instr.OpCode == OpCodes.Call) + { + var method = instr.Operand as IMethod; + if (method == null) + continue; + + var di = proxyMethodToDelegateInfo.Find(method); + if (di == null) + continue; + + Add(removeInfos, block, i, di); + } + else if (instr.OpCode == OpCodes.Ldsfld) + { + var di = GetDelegateInfo(instr.Operand as IField); + if (di == null) + continue; + + var callInfo = FindProxyCall(di, block, i); + if (callInfo != null) + { + Add(removeInfos, block, i, null); + Add(removeInfos, callInfo.Block, callInfo.Index, di); + } + else + { + errors++; + Logger.w("Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})", + Utils.RemoveNewlines(blocks.Method), + blocks.Method.MDToken.ToInt32(), + Utils.RemoveNewlines(di.field.DeclaringType), + di.field.DeclaringType.MDToken.ToInt32()); + } + } + } + } + + return FixProxyCalls(blocks.Method, removeInfos); + } + + protected virtual BlockInstr FindProxyCall(DelegateInfo di, Block block, int index) + { + return FindProxyCall(di, block, index, new Dictionary(), 1); + } + + BlockInstr FindProxyCall(DelegateInfo di, Block block, int index, Dictionary visited, int stack) + { + if (visited.ContainsKey(block)) + return null; + if (index <= 0) + visited[block] = true; + + var instrs = block.Instructions; + for (int i = index + 1; i < instrs.Count; i++) + { + if (stack <= 0) + return null; + var instr = instrs[i]; + instr.Instruction.UpdateStack(ref stack, false); + if (stack < 0) + return null; + + if (instr.OpCode != OpCodes.Call && instr.OpCode != OpCodes.Callvirt) + { + if (stack <= 0) + return null; + continue; + } + var calledMethod = instr.Operand as IMethod; + if (calledMethod == null) + return null; + if (stack != (DotNetUtils.HasReturnValue(calledMethod) ? 1 : 0)) + continue; + if (calledMethod.Name != "Invoke") + return null; + + return new BlockInstr + { + Block = block, + Index = i, + }; + } + if (stack <= 0) + return null; + + foreach (var target in block.GetTargets()) + { + var info = FindProxyCall(di, target, -1, visited, stack); + if (info != null) + return info; + } + + return null; + } + } } diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs index 2aa00488a..6d7aba46f 100644 --- a/de4dot.cui/Program.cs +++ b/de4dot.cui/Program.cs @@ -73,6 +73,7 @@ static IList CreateDeobfuscatorInfos() { new de4dot.code.deobfuscators.CodeVeil.DeobfuscatorInfo(), new de4dot.code.deobfuscators.CodeWall.DeobfuscatorInfo(), new de4dot.code.deobfuscators.Confuser.DeobfuscatorInfo(), + new de4dot.code.deobfuscators.ConfuserEx.DeobfuscatorInfo(), new de4dot.code.deobfuscators.CryptoObfuscator.DeobfuscatorInfo(), new de4dot.code.deobfuscators.DeepSea.DeobfuscatorInfo(), new de4dot.code.deobfuscators.Dotfuscator.DeobfuscatorInfo(), From ed1fea8ff0413b107a9125104f5104c4f980de7a Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Tue, 8 Aug 2017 13:27:21 +0300 Subject: [PATCH 07/14] ConfuserEx deobfuscator code clean-up and refactor --- .../ConfuserEx/ConstantDecrypter.cs | 370 +++++++++--------- .../ConfuserEx/ConstantInliner.cs | 140 ++++--- .../ConfuserEx/ControlFlowFixer.cs | 4 +- .../deobfuscators/ConfuserEx/Deobfuscator.cs | 189 ++++----- .../deobfuscators/ConfuserEx/LzmaFinder.cs | 82 ++-- .../ConfuserEx/ProxyCallFixer.cs | 159 ++++---- .../ConfuserEx/ResourceDecrypter.cs | 216 +++++----- de4dot.code/deobfuscators/ConfuserEx/Utils.cs | 26 +- .../ConfuserEx/x86/Instructions/X86ADD.cs | 1 + .../ConfuserEx/x86/Instructions/X86DIV.cs | 1 + .../ConfuserEx/x86/Instructions/X86IMUL.cs | 1 + .../ConfuserEx/x86/Instructions/X86MOV.cs | 1 + .../ConfuserEx/x86/Instructions/X86NEG.cs | 1 + .../ConfuserEx/x86/Instructions/X86NOT.cs | 1 + .../ConfuserEx/x86/Instructions/X86POP.cs | 1 + .../ConfuserEx/x86/Instructions/X86PUSH.cs | 1 + .../ConfuserEx/x86/Instructions/X86SUB.cs | 1 + .../ConfuserEx/x86/Instructions/X86XOR.cs | 1 + .../ConfuserEx/x86/UnmanagedBuff.cs | 6 +- .../ConfuserEx/x86/X86Instruction.cs | 6 +- .../deobfuscators/ConfuserEx/x86/X86Method.cs | 3 +- 21 files changed, 599 insertions(+), 612 deletions(-) diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs index 88f3001a9..b96218f95 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs @@ -1,19 +1,26 @@ -using de4dot.blocks; -using dnlib.DotNet; -using dnlib.DotNet.Emit; -using System; +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; +using de4dot.blocks; using de4dot.blocks.cflow; using de4dot.code.deobfuscators.ConfuserEx.x86; +using dnlib.DotNet; +using dnlib.DotNet.Emit; namespace de4dot.code.deobfuscators.ConfuserEx { public class ConstantDecrypterBase { - private X86Method _nativeMethod; private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); + private X86Method _nativeMethod; + + public MethodDef Method { get; set; } + public MethodDef NativeMethod { get; set; } + public byte[] Decrypted { get; set; } + public uint Magic1 { get; set; } + public uint Magic2 { get; set; } + public bool CanRemove { get; set; } = true; private int? CalculateKey() { @@ -23,99 +30,93 @@ public class ConstantDecrypterBase return null; _instructionEmulator.Pop(); - int result = _nativeMethod.Execute(((Int32Value)popValue).Value); + var result = _nativeMethod.Execute(((Int32Value) popValue).Value); return result; } - public MethodDef Method { get; set; } - public MethodDef NativeMethod { get; set; } - public byte[] Decrypted { get; set; } - public uint Magic1 { get; set; } - public uint Magic2 { get; set; } - public bool CanRemove { get; set; } = true; - private uint CalculateMagic(uint index) { - _instructionEmulator.Push(new Int32Value((int)index)); + _instructionEmulator.Push(new Int32Value((int) index)); _nativeMethod = new X86Method(NativeMethod, Method.Module as ModuleDefMD); //TODO: Possible null - int? key = CalculateKey(); + var key = CalculateKey(); - uint uint_0 = (uint) key.Value; + var uint_0 = (uint) key.Value; uint_0 &= 0x3fffffff; uint_0 <<= 2; return uint_0; } + public string DecryptString(uint index) { index = CalculateMagic(index); - int count = BitConverter.ToInt32(Decrypted, (int)index); - return string.Intern(Encoding.UTF8.GetString(Decrypted, (int)index + 4, count)); + var count = BitConverter.ToInt32(Decrypted, (int) index); + return string.Intern(Encoding.UTF8.GetString(Decrypted, (int) index + 4, count)); } + public T DecryptConstant(uint index) { index = CalculateMagic(index); - T[] array = new T[1]; - Buffer.BlockCopy(Decrypted, (int)index, array, 0, Marshal.SizeOf(typeof(T))); + var array = new T[1]; + Buffer.BlockCopy(Decrypted, (int) index, array, 0, Marshal.SizeOf(typeof(T))); return array[0]; } + public byte[] DecryptArray(uint index) { index = CalculateMagic(index); - int count = BitConverter.ToInt32(Decrypted, (int)index); + var count = BitConverter.ToInt32(Decrypted, (int) index); //int lengt = BitConverter.ToInt32(Decrypted, (int)index+4); we actualy dont need that - byte[] buffer = new byte[count - 4]; - Buffer.BlockCopy(Decrypted, (int)index + 8, buffer, 0, count - 4); + var buffer = new byte[count - 4]; + Buffer.BlockCopy(Decrypted, (int) index + 8, buffer, 0, count - 4); return buffer; } } public class ConstantsDecrypter { - TypeDef arrayType; - MethodDef constantsDecInitMethod; - FieldDef decryptedField, arrayField; - List constantDecrpers = new List(); - byte[] decryptedBytes; - bool canRemoveLzma = true; - - public bool CanRemoveLzma - { - get { return canRemoveLzma; } - } - public TypeDef Type - { - get { return arrayType; } - } - public MethodDef Method - { - get { return constantsDecInitMethod; } - } - public List Fields - { - get { return new List() { decryptedField, arrayField }; } - } - public List Decrypters - { - get { return constantDecrpers; } - } - public bool Detected + private readonly ISimpleDeobfuscator _deobfuscator; + private readonly MethodDef _lzmaMethod; + + private readonly ModuleDef _module; + + private readonly string[] _strDecryptCalledMethods = { - get { return constantsDecInitMethod != null && decryptedBytes != null && constantDecrpers.Count != 0 && decryptedField != null && arrayField != null; } - } + "System.Text.Encoding System.Text.Encoding::get_UTF8()", + "System.String System.Text.Encoding::GetString(System.Byte[],System.Int32,System.Int32)", + "System.Array System.Array::CreateInstance(System.Type,System.Int32)", + "System.String System.String::Intern(System.String)", + "System.Void System.Buffer::BlockCopy(System.Array,System.Int32,System.Array,System.Int32,System.Int32)", + "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)", + "System.Type System.Type::GetElementType()" + }; + + private byte[] _decryptedBytes; + private FieldDef _decryptedField, _arrayField; + internal TypeDef ArrayType; - ModuleDef module; - MethodDef lzmaMethod; - ISimpleDeobfuscator deobfuscator; public ConstantsDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator) { - this.module = module; - this.lzmaMethod = lzmaMethod; - this.deobfuscator = deobfsucator; + _module = module; + _lzmaMethod = lzmaMethod; + _deobfuscator = deobfsucator; } + public bool CanRemoveLzma { get; private set; } = true; + + public TypeDef Type => ArrayType; + + public MethodDef Method { get; private set; } + + public List Fields => new List {_decryptedField, _arrayField}; + + public List Decrypters { get; } = new List(); + + public bool Detected => Method != null && _decryptedBytes != null && Decrypters.Count != 0 && + _decryptedField != null && _arrayField != null; + public void Find() { - var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); + var moduleCctor = DotNetUtils.GetModuleTypeCctor(_module); if (moduleCctor == null) return; foreach (var inst in moduleCctor.Body.Instructions) @@ -124,20 +125,20 @@ public void Find() continue; if (!(inst.Operand is MethodDef)) continue; - var method = inst.Operand as MethodDef; + var method = (MethodDef) inst.Operand; if (!method.HasBody || !method.IsStatic) continue; if (!DotNetUtils.IsMethod(method, "System.Void", "()")) continue; - deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); - if (!isStrDecryptInit(method)) + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + if (!IsStringDecrypterInit(method)) continue; - constantsDecInitMethod = method; + Method = method; FindStringDecrypters(moduleCctor.DeclaringType); } } - bool isStrDecryptInit(MethodDef method) + private bool IsStringDecrypterInit(MethodDef method) { var instructions = method.Body.Instructions; @@ -146,7 +147,7 @@ bool isStrDecryptInit(MethodDef method) if (!instructions[0].IsLdcI4()) return false; - if (!instructions[1].IsStloc()) //uint num = 96u; + if (!instructions[1].IsStloc()) //uint num = 96u; return false; if (!instructions[2].IsLdcI4()) @@ -162,17 +163,17 @@ bool isStrDecryptInit(MethodDef method) if (instructions[5].OpCode != OpCodes.Ldtoken) return false; var aField = instructions[5].Operand as FieldDef; - if (aField == null) - return false; - if (aField.InitialValue == null) + if (aField?.InitialValue == null) return false; if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA)) return false; if (instructions[6].OpCode != OpCodes.Call) return false; - if (instructions[6].Operand.ToString() != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") + if (instructions[6].Operand.ToString() != + "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)" + ) return false; - if (!instructions[7].IsStloc()) // uint[] array = new uint[] {.....}; + if (!instructions[7].IsStloc()) // uint[] array = new uint[] {.....}; return false; var l = instructions.Count; @@ -180,9 +181,9 @@ bool isStrDecryptInit(MethodDef method) return false; if (instructions[l - 3].OpCode != OpCodes.Call) return false; - if (instructions[l - 3].Operand != lzmaMethod) + if (instructions[l - 3].Operand != _lzmaMethod) return false; - if (instructions[l - 2].OpCode != OpCodes.Stsfld) //.byte_0 = .smethod_0(array4); + if (instructions[l - 2].OpCode != OpCodes.Stsfld) //.byte_0 = .smethod_0(array4); return false; var dField = instructions[l - 2].Operand as FieldDef; if (dField == null) @@ -191,53 +192,56 @@ bool isStrDecryptInit(MethodDef method) { DecryptArray(ConvertArray(aField.InitialValue)); } - catch (Exception e) { Console.WriteLine(e.Message); canRemoveLzma = false; return false; } - arrayField = aField; - arrayType = DotNetUtils.GetType(module, aField.FieldSig.Type); - decryptedField = dField; + catch (Exception e) + { + Console.WriteLine(e.Message); + CanRemoveLzma = false; + return false; + } + _arrayField = aField; + ArrayType = DotNetUtils.GetType(_module, aField.FieldSig.Type); + _decryptedField = dField; return true; } - private T[] ConvertArray(T1[] array) + private static T[] ConvertArray(T1[] array) { - int l = Marshal.SizeOf(typeof(T)); - int l1 = Marshal.SizeOf(typeof(T1)); - var buffer = new T[(array.Length * l1) / l]; + var l = Marshal.SizeOf(typeof(T)); + var l1 = Marshal.SizeOf(typeof(T1)); + var buffer = new T[array.Length * l1 / l]; Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); return buffer; } private void DecryptArray(uint[] array) //TODO: Automatic detection { - uint num = 960u; // array size? - uint[] array2 = new uint[16]; - uint num2 = 4136251032u; - for (int i = 0; i < 16; i++) + var num = 960u; // array size? + var array2 = new uint[16]; + var num2 = 4136251032u; + for (var i = 0; i < 16; i++) { num2 ^= num2 >> 12; num2 ^= num2 << 25; num2 ^= num2 >> 27; array2[i] = num2; } - int num3 = 0; - int num4 = 0; - uint[] array3 = new uint[16]; - byte[] array4 = new byte[num * 4u]; - while ((long)num3 < (long)((ulong)num)) + var num3 = 0; + var num4 = 0; + var array3 = new uint[16]; + var array4 = new byte[num * 4u]; + while (num3 < num) { - for (int j = 0; j < 16; j++) - { + for (var j = 0; j < 16; j++) array3[j] = array[num3 + j]; - } - uint num5 = array3[3] * 41u; - array3[11] = (array3[11] ^ 3634844963u); - uint num6 = array3[3] * 31u; + var num5 = array3[3] * 41u; + array3[11] = array3[11] ^ 3634844963u; + var num6 = array3[3] * 31u; num6 += array3[9] * 47u; num5 += array3[9] * 85u; num5 += array3[10] * 149u; - uint num7 = array3[3] << 1; + var num7 = array3[3] << 1; num7 += array3[3]; - uint num8 = array3[3] << 1; + var num8 = array3[3] << 1; num8 += array3[3] << 3; num7 += array3[9] << 3; num8 += array3[9] * 13u; @@ -245,7 +249,7 @@ private void DecryptArray(uint[] array) //TODO: Automatic detection num6 += array3[10] * 71u; num7 += array3[10] << 1; num6 += array3[1] * 81u; - array3[4] = (array3[4] ^ ~array3[6]); + array3[4] = array3[4] ^ ~array3[6]; num8 += array3[10] << 1; num7 += array3[10] << 4; array3[9] = num6; @@ -257,11 +261,11 @@ private void DecryptArray(uint[] array) //TODO: Automatic detection num6 = array3[7] * 19u; array3[10] = num7; num6 += array3[8] * 28u; - array3[14] = (array3[14] ^ array3[0]); + array3[14] = array3[14] ^ array3[0]; array3[3] = num8; num6 += array3[12] << 6; array3[1] = num5; - array3[2] = (array3[2] ^ array2[2]); + array3[2] = array3[2] ^ array2[2]; num5 = array3[7] * 28u; num5 += array3[8] << 2; num8 = array3[7] << 1; @@ -290,53 +294,53 @@ private void DecryptArray(uint[] array) //TODO: Automatic detection num6 = array3[15] >> 22; array3[15] = array3[15] << 10; num8 = array3[1] >> 21; - array3[15] = (array3[15] | num6); - array3[12] = (array3[12] ^ array2[12]); - num6 = (array3[2] & 3262151220u); + array3[15] = array3[15] | num6; + array3[12] = array3[12] ^ array2[12]; + num6 = array3[2] & 3262151220u; array3[1] = array3[1] << 11; - array3[1] = (array3[1] | num8); + array3[1] = array3[1] | num8; array3[7] = array3[7] << 11; array3[0] = array3[0] - array3[14]; num7 = array3[13] << 4; num8 = array3[3] * 954284655u; array3[3] = array3[5]; array3[5] = num8 * 3102958735u; - array3[7] = (array3[7] | num5); + array3[7] = array3[7] | num5; num5 = array3[10] << 4; num8 = array3[9] * 2468501497u; - array3[2] = (array3[2] & 1032816075u); + array3[2] = array3[2] & 1032816075u; array3[13] = array3[13] >> 28; - array3[13] = (array3[13] | num7); + array3[13] = array3[13] | num7; array3[7] = array3[7] - 888060325u; - array3[2] = (array3[2] | (array3[8] & 3262151220u)); + array3[2] = array3[2] | (array3[8] & 3262151220u); array3[12] = array3[12] * 4056148675u; array3[9] = array3[13]; num7 = array3[6] << 5; array3[13] = num8 * 1746582089u; array3[6] = array3[6] >> 27; - array3[6] = (array3[6] | num7); - array3[8] = (array3[8] & 1032816075u); - array3[7] = (array3[7] ^ array2[7]); + array3[6] = array3[6] | num7; + array3[8] = array3[8] & 1032816075u; + array3[7] = array3[7] ^ array2[7]; num5 += array3[11] * 46u; num6 *= 869722291u; num8 = array3[10] << 1; num5 += array3[3] * 92u; num5 += array3[5] * 149u; array3[7] = array3[7] - 3922202313u; - array3[8] = (array3[8] | num6 * 2576221819u); + array3[8] = array3[8] | (num6 * 2576221819u); num8 += array3[11] * 15u; num8 += array3[3] * 37u; num6 = array3[10] * 7u; - array3[8] = (array3[8] ^ 1878284212u); + array3[8] = array3[8] ^ 1878284212u; num8 += array3[5] * 56u; - array3[9] = (array3[9] ^ array2[9]); + array3[9] = array3[9] ^ array2[9]; num7 = array3[10] << 3; - array3[6] = (array3[6] ^ 2841119440u); + array3[6] = array3[6] ^ 2841119440u; num6 += array3[11] << 4; - array3[2] = (array3[2] ^ 217219923u); + array3[2] = array3[2] ^ 217219923u; num7 += array3[10]; num6 += array3[3] * 29u; - array3[6] = (array3[6] ^ array2[6]); + array3[6] = array3[6] ^ array2[6]; num7 += array3[11] * 26u; num7 += array3[3] * 52u; num6 += array3[5] * 49u; @@ -344,24 +348,24 @@ private void DecryptArray(uint[] array) //TODO: Automatic detection array3[3] = num5; array3[10] = num6; num6 = array3[1] * 15u; - array3[12] = (array3[12] ^ 1080861703u); + array3[12] = array3[12] ^ 1080861703u; array3[5] = num8; - num5 = (array3[4] & 3659960635u); + num5 = array3[4] & 3659960635u; num6 += array3[12] << 1; - array3[4] = (array3[4] & 635006660u); - array3[4] = (array3[4] | (array3[9] & 3659960635u)); + array3[4] = array3[4] & 635006660u; + array3[4] = array3[4] | (array3[9] & 3659960635u); num5 *= 1676034815u; array3[11] = num7; num7 = array3[1] * 19u; num6 += array3[12] << 4; - array3[9] = (array3[9] & 635006660u); + array3[9] = array3[9] & 635006660u; num6 += array3[3] << 6; num7 += array3[12] * 27u; array3[5] = array3[5] - array3[8]; - array3[9] = (array3[9] | num5 * 1267776767u); + array3[9] = array3[9] | (num5 * 1267776767u); num5 = array3[1] << 2; num5 += array3[1]; - array3[13] = (array3[13] ^ array2[13]); + array3[13] = array3[13] ^ array2[13]; num8 = array3[1]; num6 += array3[3]; num5 += array3[12] << 3; @@ -377,53 +381,53 @@ private void DecryptArray(uint[] array) //TODO: Automatic detection num8 += array3[15] << 1; num8 += array3[15]; array3[3] = num6; - array3[0] = (array3[0] ^ array3[13]); + array3[0] = array3[0] ^ array3[13]; array3[14] = array3[14] - array3[15]; num7 += array3[15] << 5; - array3[13] = (array3[13] ^ ~array3[1]); + array3[13] = array3[13] ^ ~array3[1]; num6 = array3[10] >> 31; - array3[14] = (array3[14] ^ array2[14]); - array3[8] = (array3[8] ^ array2[8]); + array3[14] = array3[14] ^ array2[14]; + array3[8] = array3[8] ^ array2[8]; array3[12] = num5; array3[1] = num8; - array3[5] = (array3[5] ^ array2[5]); - array3[11] = (array3[11] ^ array2[11]); - num5 = (array3[11] & 2204625944u); - array3[1] = (array3[1] ^ array2[1]); - array3[4] = (array3[4] ^ array2[4]); - array3[11] = (array3[11] & 2090341351u); - array3[11] = (array3[11] | (array3[4] & 2204625944u)); + array3[5] = array3[5] ^ array2[5]; + array3[11] = array3[11] ^ array2[11]; + num5 = array3[11] & 2204625944u; + array3[1] = array3[1] ^ array2[1]; + array3[4] = array3[4] ^ array2[4]; + array3[11] = array3[11] & 2090341351u; + array3[11] = array3[11] | (array3[4] & 2204625944u); array3[15] = num7; - num8 = (array3[14] & 2496954112u); - array3[14] = (array3[14] & 1798013183u); - array3[4] = (array3[4] & 2090341351u); - array3[15] = (array3[15] ^ array2[15]); + num8 = array3[14] & 2496954112u; + array3[14] = array3[14] & 1798013183u; + array3[4] = array3[4] & 2090341351u; + array3[15] = array3[15] ^ array2[15]; array3[10] = array3[10] << 1; num5 *= 338764649u; - array3[14] = (array3[14] | (array3[9] & 2496954112u)); + array3[14] = array3[14] | (array3[9] & 2496954112u); array3[15] = array3[15] - array3[0]; - array3[10] = (array3[10] | num6); - array3[10] = (array3[10] ^ array2[10]); - array3[3] = (array3[3] ^ array2[3]); + array3[10] = array3[10] | num6; + array3[10] = array3[10] ^ array2[10]; + array3[3] = array3[3] ^ array2[3]; num8 *= 2292397853u; - array3[0] = (array3[0] ^ array2[0]); - array3[0] = (array3[0] ^ 2814140307u); - array3[2] = (array3[2] ^ ~array3[13]); - array3[4] = (array3[4] | num5 * 587046105u); - array3[9] = (array3[9] & 1798013183u); - array3[9] = (array3[9] | num8 * 1520255797u); - for (int k = 0; k < 16; k++) + array3[0] = array3[0] ^ array2[0]; + array3[0] = array3[0] ^ 2814140307u; + array3[2] = array3[2] ^ ~array3[13]; + array3[4] = array3[4] | (num5 * 587046105u); + array3[9] = array3[9] & 1798013183u; + array3[9] = array3[9] | (num8 * 1520255797u); + for (var k = 0; k < 16; k++) { - uint num9 = array3[k]; - array4[num4++] = (byte)num9; - array4[num4++] = (byte)(num9 >> 8); - array4[num4++] = (byte)(num9 >> 16); - array4[num4++] = (byte)(num9 >> 24); + var num9 = array3[k]; + array4[num4++] = (byte) num9; + array4[num4++] = (byte) (num9 >> 8); + array4[num4++] = (byte) (num9 >> 16); + array4[num4++] = (byte) (num9 >> 24); array2[k] ^= num9; } num3 += 16; } - decryptedBytes = Lzma.Decompress(array4); + _decryptedBytes = Lzma.Decompress(array4); } private void FindStringDecrypters(TypeDef type) @@ -432,12 +436,10 @@ private void FindStringDecrypters(TypeDef type) { if (!method.HasBody) continue; - if (!(method.Signature.ContainsGenericParameter)) + if (!method.Signature.ContainsGenericParameter) continue; var sig = method.MethodSig; - if (sig == null) - continue; - if (sig.Params.Count != 1) + if (sig?.Params.Count != 1) continue; if (sig.Params[0].GetElementType() != ElementType.U4) continue; @@ -445,28 +447,18 @@ private void FindStringDecrypters(TypeDef type) continue; if (sig.GenParamCount != 1) continue; - deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); IsStringDecrypter(method); } } - string[] strDecryptCalledMethods = { - "System.Text.Encoding System.Text.Encoding::get_UTF8()", - "System.String System.Text.Encoding::GetString(System.Byte[],System.Int32,System.Int32)", - "System.Array System.Array::CreateInstance(System.Type,System.Int32)", - "System.String System.String::Intern(System.String)", - "System.Void System.Buffer::BlockCopy(System.Array,System.Int32,System.Array,System.Int32,System.Int32)", - "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)", - "System.Type System.Type::GetElementType()" - }; - private void IsStringDecrypter(MethodDef method) { var instr = method.Body.Instructions; if (instr.Count < 25) return; - int i = 0; + var i = 0; if (!instr[i++].IsLdarg()) return; @@ -481,7 +473,7 @@ private void IsStringDecrypter(MethodDef method) if (!DotNetUtils.IsMethod(nativeMethod, "System.Int32", "(System.Int32)")) return; - if (!instr[i++].IsStarg()) //uint_0 = (uint_0 * 2857448701u ^ 1196001109u); + if (!instr[i++].IsStarg()) //uint_0 = (uint_0 * 2857448701u ^ 1196001109u); return; if (!instr[i++].IsLdarg()) @@ -504,7 +496,7 @@ private void IsStringDecrypter(MethodDef method) return; if (instr[i++].OpCode != OpCodes.And) return; - if (!instr[i++].IsStarg()) //uint_0 &= 1073741823u; + if (!instr[i++].IsStarg()) //uint_0 &= 1073741823u; return; if (!instr[i++].IsLdarg()) @@ -513,98 +505,108 @@ private void IsStringDecrypter(MethodDef method) return; if (instr[i++].OpCode != OpCodes.Shl) return; - if (!instr[i++].IsStarg()) //uint_0 <<= 2; + if (!instr[i++].IsStarg()) //uint_0 <<= 2; return; - foreach (var mtd in strDecryptCalledMethods) + foreach (var mtd in _strDecryptCalledMethods) if (!DotNetUtils.CallsMethod(method, mtd)) return; //TODO: Implement //if (!DotNetUtils.LoadsField(method, decryptedField)) // return; - constantDecrpers.Add(new ConstantDecrypterBase() + Decrypters.Add(new ConstantDecrypterBase { - Decrypted = decryptedBytes, + Decrypted = _decryptedBytes, Method = method, - NativeMethod = nativeMethod, + NativeMethod = nativeMethod }); } - static bool VerifyGenericArg(MethodSpec gim, ElementType etype) + private static bool VerifyGenericArg(MethodSpec gim, ElementType etype) { - if (gim == null) - return false; - var gims = gim.GenericInstMethodSig; + var gims = gim?.GenericInstMethodSig; if (gims == null || gims.GenericArguments.Count != 1) return false; return gims.GenericArguments[0].GetElementType() == etype; } + public string DecryptString(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.String)) return null; return info.DecryptString(magic1); } + public object DecryptSByte(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.I1)) return null; return info.DecryptConstant(magic1); } + public object DecryptByte(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.U1)) return null; return info.DecryptConstant(magic1); } + public object DecryptInt16(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.I2)) return null; return info.DecryptConstant(magic1); } + public object DecryptUInt16(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.U2)) return null; return info.DecryptConstant(magic1); } + public object DecryptInt32(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.I4)) return null; return info.DecryptConstant(magic1); } + public object DecryptUInt32(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.U4)) return null; return info.DecryptConstant(magic1); } + public object DecryptInt64(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.I8)) return null; return info.DecryptConstant(magic1); } + public object DecryptUInt64(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.U8)) return null; return info.DecryptConstant(magic1); } + public object DecryptSingle(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.R4)) return null; return info.DecryptConstant(magic1); } + public object DecryptDouble(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.R8)) return null; return info.DecryptConstant(magic1); } + public object DecryptArray(ConstantDecrypterBase info, MethodSpec gim, uint magic1) { if (!VerifyGenericArg(gim, ElementType.SZArray)) diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs index c59e1bd4b..a04c0d642 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantInliner.cs @@ -1,72 +1,76 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using de4dot.blocks; using de4dot.blocks.cflow; -using System; using dnlib.DotNet; using dnlib.DotNet.Emit; namespace de4dot.code.deobfuscators.ConfuserEx { - class ConstantsInliner : IBlocksDeobfuscator + internal class ConstantsInliner : IBlocksDeobfuscator { - Blocks blocks; - SByteValueInliner sbyteValueInliner; - ByteValueInliner byteValueInliner; - Int16ValueInliner int16ValueInliner; - UInt16ValueInliner uint16ValueInliner; - Int32ValueInliner int32ValueInliner; - UInt32ValueInliner uint32ValueInliner; - Int64ValueInliner int64ValueInliner; - UInt64ValueInliner uint64ValueInliner; - SingleValueInliner singleValueInliner; - DoubleValueInliner doubleValueInliner; - ArrayValueInliner arrayValueInliner; - - public bool ExecuteIfNotModified { get; set; } + private readonly ArrayValueInliner _arrayValueInliner; + private readonly ByteValueInliner _byteValueInliner; + private readonly DoubleValueInliner _doubleValueInliner; + private readonly Int16ValueInliner _int16ValueInliner; + private readonly Int32ValueInliner _int32ValueInliner; + private readonly Int64ValueInliner _int64ValueInliner; + private readonly SByteValueInliner _sbyteValueInliner; + private readonly SingleValueInliner _singleValueInliner; + private readonly UInt16ValueInliner _uint16ValueInliner; + private readonly UInt32ValueInliner _uint32ValueInliner; + private readonly UInt64ValueInliner _uint64ValueInliner; + private Blocks _blocks; public ConstantsInliner(SByteValueInliner sbyteValueInliner, ByteValueInliner byteValueInliner, - Int16ValueInliner int16ValueInliner, UInt16ValueInliner uint16ValueInliner, Int32ValueInliner int32ValueInliner, - UInt32ValueInliner uint32ValueInliner, Int64ValueInliner int64ValueInliner, UInt64ValueInliner uint64ValueInliner, - SingleValueInliner singleValueInliner, DoubleValueInliner doubleValueInliner, ArrayValueInliner arrayValueInliner) + Int16ValueInliner int16ValueInliner, UInt16ValueInliner uint16ValueInliner, + Int32ValueInliner int32ValueInliner, + UInt32ValueInliner uint32ValueInliner, Int64ValueInliner int64ValueInliner, + UInt64ValueInliner uint64ValueInliner, + SingleValueInliner singleValueInliner, DoubleValueInliner doubleValueInliner, + ArrayValueInliner arrayValueInliner) { - this.sbyteValueInliner = sbyteValueInliner; - this.byteValueInliner = byteValueInliner; - this.int16ValueInliner = int16ValueInliner; - this.uint16ValueInliner = uint16ValueInliner; - this.int32ValueInliner = int32ValueInliner; - this.uint32ValueInliner = uint32ValueInliner; - this.int64ValueInliner = int64ValueInliner; - this.uint64ValueInliner = uint64ValueInliner; - this.singleValueInliner = singleValueInliner; - this.doubleValueInliner = doubleValueInliner; - this.arrayValueInliner = arrayValueInliner; + _sbyteValueInliner = sbyteValueInliner; + _byteValueInliner = byteValueInliner; + _int16ValueInliner = int16ValueInliner; + _uint16ValueInliner = uint16ValueInliner; + _int32ValueInliner = int32ValueInliner; + _uint32ValueInliner = uint32ValueInliner; + _int64ValueInliner = int64ValueInliner; + _uint64ValueInliner = uint64ValueInliner; + _singleValueInliner = singleValueInliner; + _doubleValueInliner = doubleValueInliner; + _arrayValueInliner = arrayValueInliner; } + public bool ExecuteIfNotModified { get; set; } + public void DeobfuscateBegin(Blocks blocks) { - this.blocks = blocks; + _blocks = blocks; } public bool Deobfuscate(List allBlocks) { - bool modified = false; + var modified = false; foreach (var block in allBlocks) { - modified |= sbyteValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= byteValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= int16ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= uint16ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= int32ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= uint32ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= int64ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= uint64ValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= singleValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= doubleValueInliner.Decrypt(blocks.Method, allBlocks) != 0; - modified |= arrayValueInliner.Decrypt(blocks.Method, allBlocks) != 0; + modified |= _sbyteValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _byteValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _int16ValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _uint16ValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _int32ValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _uint32ValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _int64ValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _uint64ValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _singleValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _doubleValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; + modified |= _arrayValueInliner.Decrypt(_blocks.Method, allBlocks) != 0; } return modified; } } + public class SByteValueInliner : ValueInlinerBase { protected override void InlineReturnValues(IList callResults) @@ -74,14 +78,15 @@ protected override void InlineReturnValues(IList callResults) foreach (var callResult in callResults) { var block = callResult.block; - int num = callResult.callEndIndex - callResult.callStartIndex + 1; + var num = callResult.callEndIndex - callResult.callStartIndex + 1; - block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int) callResult.returnValue)); RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.SByte"); Logger.v("Decrypted sbyte: {0}", callResult.returnValue); } } } + public class ByteValueInliner : ValueInlinerBase { protected override void InlineReturnValues(IList callResults) @@ -89,14 +94,15 @@ protected override void InlineReturnValues(IList callResults) foreach (var callResult in callResults) { var block = callResult.block; - int num = callResult.callEndIndex - callResult.callStartIndex + 1; + var num = callResult.callEndIndex - callResult.callStartIndex + 1; - block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int) callResult.returnValue)); RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.Byte"); Logger.v("Decrypted byte: {0}", callResult.returnValue); } } } + public class Int16ValueInliner : ValueInlinerBase { protected override void InlineReturnValues(IList callResults) @@ -104,14 +110,15 @@ protected override void InlineReturnValues(IList callResults) foreach (var callResult in callResults) { var block = callResult.block; - int num = callResult.callEndIndex - callResult.callStartIndex + 1; + var num = callResult.callEndIndex - callResult.callStartIndex + 1; - block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int) callResult.returnValue)); RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.Int16"); Logger.v("Decrypted int16: {0}", callResult.returnValue); } } } + public class UInt16ValueInliner : ValueInlinerBase { protected override void InlineReturnValues(IList callResults) @@ -119,14 +126,15 @@ protected override void InlineReturnValues(IList callResults) foreach (var callResult in callResults) { var block = callResult.block; - int num = callResult.callEndIndex - callResult.callStartIndex + 1; + var num = callResult.callEndIndex - callResult.callStartIndex + 1; - block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int) callResult.returnValue)); RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt16"); Logger.v("Decrypted uint16: {0}", callResult.returnValue); } } } + public class UInt32ValueInliner : ValueInlinerBase { protected override void InlineReturnValues(IList callResults) @@ -134,14 +142,15 @@ protected override void InlineReturnValues(IList callResults) foreach (var callResult in callResults) { var block = callResult.block; - int num = callResult.callEndIndex - callResult.callStartIndex + 1; + var num = callResult.callEndIndex - callResult.callStartIndex + 1; - block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int)callResult.returnValue)); + block.Replace(callResult.callStartIndex, num, Instruction.CreateLdcI4((int) callResult.returnValue)); RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt32"); Logger.v("Decrypted uint32: {0}", callResult.returnValue); } } } + public class UInt64ValueInliner : ValueInlinerBase { protected override void InlineReturnValues(IList callResults) @@ -149,30 +158,37 @@ protected override void InlineReturnValues(IList callResults) foreach (var callResult in callResults) { var block = callResult.block; - int num = callResult.callEndIndex - callResult.callStartIndex + 1; + var num = callResult.callEndIndex - callResult.callStartIndex + 1; - block.Replace(callResult.callStartIndex, num, OpCodes.Ldc_I8.ToInstruction((long)callResult.returnValue)); + block.Replace(callResult.callStartIndex, num, + OpCodes.Ldc_I8.ToInstruction((long) callResult.returnValue)); RemoveUnboxInstruction(block, callResult.callStartIndex + 1, "System.UInt64"); Logger.v("Decrypted uint64: {0}", callResult.returnValue); } } } + public class ArrayValueInliner : ValueInlinerBase { - InitializedDataCreator initializedDataCreator; + private readonly InitializedDataCreator _initializedDataCreator; + + public ArrayValueInliner(InitializedDataCreator initializedDataCreator) + { + _initializedDataCreator = initializedDataCreator; + } - public ArrayValueInliner(InitializedDataCreator initializedDataCreator) { this.initializedDataCreator = initializedDataCreator; } protected override void InlineReturnValues(IList callResults) { foreach (var callResult in callResults) { var block = callResult.block; - int num = callResult.callEndIndex - callResult.callStartIndex + 1; + var num = callResult.callEndIndex - callResult.callStartIndex + 1; - var generic = (callResult.GetMethodRef() as MethodSpec).GenericInstMethodSig.GenericArguments; - ITypeDefOrRef sig = generic[0].Next.ToTypeDefOrRef(); + var generic = ((MethodSpec) callResult.GetMethodRef()).GenericInstMethodSig.GenericArguments; + var sig = generic[0].Next.ToTypeDefOrRef(); - initializedDataCreator.AddInitializeArrayCode(block, callResult.callStartIndex, num, sig, callResult.returnValue as byte[]); + _initializedDataCreator.AddInitializeArrayCode(block, callResult.callStartIndex, num, sig, + callResult.returnValue as byte[]); RemoveUnboxInstruction(block, callResult.callStartIndex + 1, sig.ToString()); //TODO: sig.ToString() ?? Logger.v("Decrypted array <{1}>: {0}", callResult.returnValue, sig.ToString()); } diff --git a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs index 50b57c122..62b7b456c 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs @@ -9,9 +9,9 @@ namespace de4dot.code.deobfuscators.ConfuserEx { - class ControlFlowFixer : IBlocksDeobfuscator + internal class ControlFlowFixer : IBlocksDeobfuscator { - public bool ExecuteIfNotModified { get; } + public bool ExecuteIfNotModified { get; } = false; public List NativeMethods = new List(); private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index f51db6d37..23d06208c 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -17,98 +17,78 @@ You should have received a copy of the GNU General Public License along with de4dot. If not, see . */ +using System.Collections.Generic; using de4dot.blocks; using de4dot.blocks.cflow; using dnlib.DotNet; using dnlib.DotNet.Emit; -using System.Collections.Generic; namespace de4dot.code.deobfuscators.ConfuserEx { - public class DeobfuscatorInfo : DeobfuscatorInfoBase { - public const string THE_NAME = "ConfuserEx"; + internal const string THE_NAME = "ConfuserEx"; public const string THE_TYPE = "cx"; - const string DEFAULT_REGEX = DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX; + private const string DEFAULT_REGEX = DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { } - public override string Name - { - get { return THE_NAME; } - } - - public override string Type - { - get { return THE_TYPE; } - } + public override string Name => THE_NAME; + public override string Type => THE_TYPE; public override IDeobfuscator CreateDeobfuscator() { return new Deobfuscator(new Deobfuscator.Options { RenameResourcesInCode = false, - ValidNameRegex = validNameRegex.Get(), + ValidNameRegex = validNameRegex.Get() }); } - class Deobfuscator : DeobfuscatorBase + private class Deobfuscator : DeobfuscatorBase { - private bool _detectedConfuserExAttribute = false, _deobfuscating = false; - private string _version = ""; - private LzmaFinder _lzmaFinder; + private readonly ControlFlowFixer _controlFlowFixer = new ControlFlowFixer(); + + private bool _canRemoveLzma = true; private ConstantsDecrypter _constantDecrypter; - private ResourceDecrypter _resourceDecrypter; + private bool _detectedConfuserExAttribute, _deobfuscating; + private LzmaFinder _lzmaFinder; private ProxyCallFixer _proxyCallFixer; - private ControlFlowFixer _controlFlowFixer = new ControlFlowFixer(); - - #region ConstantInliners - - SByteValueInliner sbyteValueInliner; - ByteValueInliner byteValueInliner; - Int16ValueInliner int16ValueInliner; - UInt16ValueInliner uint16ValueInliner; - Int32ValueInliner int32ValueInliner; - UInt32ValueInliner uint32ValueInliner; - Int64ValueInliner int64ValueInliner; - UInt64ValueInliner uint64ValueInliner; - SingleValueInliner singleValueInliner; - DoubleValueInliner doubleValueInliner; - ArrayValueInliner arrayValueInliner; - - #endregion - - internal class Options : OptionsBase - { - } + private ResourceDecrypter _resourceDecrypter; + private string _version = ""; - public override string Type + public Deobfuscator(Options options) + : base(options) { - get { return DeobfuscatorInfo.THE_TYPE; } } - public override string TypeLong - { - get { return DeobfuscatorInfo.THE_NAME; } - } + public override string Type => THE_TYPE; + public override string TypeLong => THE_NAME; + public override string Name => $"{TypeLong} {_version}"; - public override string Name + public override IEnumerable BlocksDeobfuscators { - get { return $"{TypeLong} {_version}"; } - } + get + { + var list = new List(); + list.Add(_controlFlowFixer); - public Deobfuscator(Options options) - : base(options) - { + if (_deobfuscating && _int32ValueInliner != null) + list.Add(new ConstantsInliner(_sbyteValueInliner, _byteValueInliner, _int16ValueInliner, + _uint16ValueInliner, + _int32ValueInliner, _uint32ValueInliner, _int64ValueInliner, _uint64ValueInliner, + _singleValueInliner, _doubleValueInliner, _arrayValueInliner) + {ExecuteIfNotModified = true}); + return list; + } } protected override int DetectInternal() { - int val = 0; + var val = 0; if (_detectedConfuserExAttribute) val += 0; if (_lzmaFinder.FoundLzma) val += 10; if (_constantDecrypter.Detected) val += 10; @@ -137,7 +117,6 @@ protected override void ScanForObfuscator() private void DetectConfuserExAttribute() { - var versions = new List(); foreach (var attribute in module.CustomAttributes) { if (attribute.TypeFullName != "ConfusedByAttribute") @@ -160,81 +139,64 @@ public override void DeobfuscateBegin() { if (_constantDecrypter.Detected) { - Logger.w("Constants encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove - - sbyteValueInliner = new SByteValueInliner(); - byteValueInliner = new ByteValueInliner(); - int16ValueInliner = new Int16ValueInliner(); - uint16ValueInliner = new UInt16ValueInliner(); - int32ValueInliner = new Int32ValueInliner(); - uint32ValueInliner = new UInt32ValueInliner(); - int64ValueInliner = new Int64ValueInliner(); - uint64ValueInliner = new UInt64ValueInliner(); - singleValueInliner = new SingleValueInliner(); - doubleValueInliner = new DoubleValueInliner(); - arrayValueInliner = new ArrayValueInliner(initializedDataCreator); + Logger.w( + "Constants encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove + + _sbyteValueInliner = new SByteValueInliner(); + _byteValueInliner = new ByteValueInliner(); + _int16ValueInliner = new Int16ValueInliner(); + _uint16ValueInliner = new UInt16ValueInliner(); + _int32ValueInliner = new Int32ValueInliner(); + _uint32ValueInliner = new UInt32ValueInliner(); + _int64ValueInliner = new Int64ValueInliner(); + _uint64ValueInliner = new UInt64ValueInliner(); + _singleValueInliner = new SingleValueInliner(); + _doubleValueInliner = new DoubleValueInliner(); + _arrayValueInliner = new ArrayValueInliner(initializedDataCreator); foreach (var info in _constantDecrypter.Decrypters) { staticStringInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptString(info, gim, (uint) args[0])); - sbyteValueInliner.Add(info.Method, + _sbyteValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptSByte(info, gim, (uint) args[0])); - byteValueInliner.Add(info.Method, + _byteValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptByte(info, gim, (uint) args[0])); - int16ValueInliner.Add(info.Method, + _int16ValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptInt16(info, gim, (uint) args[0])); - uint16ValueInliner.Add(info.Method, + _uint16ValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptUInt16(info, gim, (uint) args[0])); - int32ValueInliner.Add(info.Method, + _int32ValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptInt32(info, gim, (uint) args[0])); - uint32ValueInliner.Add(info.Method, + _uint32ValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptUInt32(info, gim, (uint) args[0])); - int64ValueInliner.Add(info.Method, + _int64ValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptInt64(info, gim, (uint) args[0])); - uint64ValueInliner.Add(info.Method, + _uint64ValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptUInt64(info, gim, (uint) args[0])); - singleValueInliner.Add(info.Method, + _singleValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptSingle(info, gim, (uint) args[0])); - doubleValueInliner.Add(info.Method, + _doubleValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptDouble(info, gim, (uint) args[0])); - arrayValueInliner.Add(info.Method, + _arrayValueInliner.Add(info.Method, (method, gim, args) => _constantDecrypter.DecryptArray(info, gim, (uint) args[0])); } _deobfuscating = true; } if (_resourceDecrypter.Detected) { - Logger.w("Resource encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove + Logger.w( + "Resource encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove _resourceDecrypter.Fix(); } base.DeobfuscateBegin(); } - public override IEnumerable BlocksDeobfuscators - { - get - { - var list = new List(); - list.Add(_controlFlowFixer); - - if (_deobfuscating && int32ValueInliner != null) - list.Add(new ConstantsInliner(sbyteValueInliner, byteValueInliner, int16ValueInliner, - uint16ValueInliner, - int32ValueInliner, uint32ValueInliner, int64ValueInliner, uint64ValueInliner, - singleValueInliner, doubleValueInliner, arrayValueInliner) - {ExecuteIfNotModified = true}); - return list; - } - } - - bool _canRemoveLzma = true; - public override void DeobfuscateEnd() { FindAndRemoveInlinedMethods(); - List toRemoveFromCctor = new List(); + var toRemoveFromCctor = new List(); if (_constantDecrypter.Detected) if (CanRemoveStringDecrypterType) @@ -247,7 +209,9 @@ public override void DeobfuscateEnd() AddTypeToBeRemoved(_constantDecrypter.Type, "Array field signature type"); } else + { _canRemoveLzma = false; + } if (_resourceDecrypter.Detected && _resourceDecrypter.CanRemoveLzma) { @@ -275,13 +239,13 @@ public override void DeobfuscateEnd() AddTypesToBeRemoved(_proxyCallFixer.AttributeTypes, "Proxy creator attributes"); AddMethodsToBeRemoved(_proxyCallFixer.NativeMethods, "Proxy native methods"); } - + AddMethodsToBeRemoved(_controlFlowFixer.NativeMethods, "Control flow native methods"); var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); foreach (var instr in moduleCctor.Body.Instructions) if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef && - toRemoveFromCctor.Contains(instr.Operand as MethodDef)) + toRemoveFromCctor.Contains((MethodDef) instr.Operand)) instr.OpCode = OpCodes.Nop; //No more mixed! @@ -292,8 +256,7 @@ public override void DeobfuscateEnd() public override IEnumerable GetStringDecrypterMethods() { - var list = new List(); - return list; + return new List(); } public override void DeobfuscateMethodEnd(Blocks blocks) @@ -301,6 +264,26 @@ public override void DeobfuscateMethodEnd(Blocks blocks) _proxyCallFixer.Deobfuscate(blocks); base.DeobfuscateMethodEnd(blocks); } + + internal class Options : OptionsBase + { + } + + #region ConstantInliners + + private SByteValueInliner _sbyteValueInliner; + private ByteValueInliner _byteValueInliner; + private Int16ValueInliner _int16ValueInliner; + private UInt16ValueInliner _uint16ValueInliner; + private Int32ValueInliner _int32ValueInliner; + private UInt32ValueInliner _uint32ValueInliner; + private Int64ValueInliner _int64ValueInliner; + private UInt64ValueInliner _uint64ValueInliner; + private SingleValueInliner _singleValueInliner; + private DoubleValueInliner _doubleValueInliner; + private ArrayValueInliner _arrayValueInliner; + + #endregion } } } \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs b/de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs index f1a216f5c..a5d6aed32 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/LzmaFinder.cs @@ -1,42 +1,32 @@ -using de4dot.blocks; +using System.Collections.Generic; +using System.Linq; +using de4dot.blocks; using dnlib.DotNet; using dnlib.DotNet.Emit; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace de4dot.code.deobfuscators.ConfuserEx { public class LzmaFinder { - MethodDef decompressMethod; - List types = new List(); + private readonly ISimpleDeobfuscator _deobfuscator; - public MethodDef Method - { - get { return decompressMethod; } - } - public List Types - { - get { return types; } - } - public bool FoundLzma - { - get { return decompressMethod != null && types.Count != 0; } - } + private readonly ModuleDef _module; - ModuleDef module; - ISimpleDeobfuscator deobfuscator; public LzmaFinder(ModuleDef module, ISimpleDeobfuscator deobfuscator) { - this.module = module; - this.deobfuscator = deobfuscator; + this._module = module; + this._deobfuscator = deobfuscator; } + public MethodDef Method { get; private set; } + + public List Types { get; } = new List(); + + public bool FoundLzma => Method != null && Types.Count != 0; + public void Find() { - var moduleType = DotNetUtils.GetModuleType(module); + var moduleType = DotNetUtils.GetModuleType(_module); if (moduleType == null) return; foreach (var method in moduleType.Methods) @@ -45,11 +35,11 @@ public void Find() continue; if (!DotNetUtils.IsMethod(method, "System.Byte[]", "(System.Byte[])")) continue; - deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); if (!IsLzmaMethod(method)) continue; - decompressMethod = method; - var type = ((MethodDef)method.Body.Instructions[3].Operand).DeclaringType; + Method = method; + var type = ((MethodDef) method.Body.Instructions[3].Operand).DeclaringType; ExtractNestedTypes(type); } } @@ -62,14 +52,14 @@ private bool IsLzmaMethod(MethodDef method) return false; var firstInstruction = instructions.FirstOrDefault( - instr => - instr.OpCode == OpCodes.Newobj && - instr.Operand.ToString() == "System.Void System.IO.MemoryStream::.ctor(System.Byte[])"); + instr => + instr.OpCode == OpCodes.Newobj && + instr.Operand.ToString() == "System.Void System.IO.MemoryStream::.ctor(System.Byte[])"); if (firstInstruction == null) return false; - int i = instructions.IndexOf(firstInstruction) + 1; + var i = instructions.IndexOf(firstInstruction) + 1; if (!instructions[i++].IsStloc()) return false; @@ -82,7 +72,7 @@ private bool IsLzmaMethod(MethodDef method) return false; if (instructions[i++].OpCode != OpCodes.Newarr) return false; - if (!instructions[i++].IsStloc()) //byte[] buffer = new byte[5]; + if (!instructions[i++].IsStloc()) //byte[] buffer = new byte[5]; return false; if (!instructions[i++].IsLdloc()) @@ -93,16 +83,17 @@ private bool IsLzmaMethod(MethodDef method) return false; if (!instructions[i].IsLdcI4() || instructions[i++].GetLdcI4Value() != 5) return false; - if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != "System.Int32 System.IO.Stream::Read(System.Byte[],System.Int32,System.Int32)") + if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != + "System.Int32 System.IO.Stream::Read(System.Byte[],System.Int32,System.Int32)") return false; - if (instructions[i++].OpCode != OpCodes.Pop) //memoryStream.Read(buffer, 0, 5); + if (instructions[i++].OpCode != OpCodes.Pop) //memoryStream.Read(buffer, 0, 5); return false; if (!instructions[i++].IsLdloc()) return false; if (!instructions[i++].IsLdloc()) return false; - if (instructions[i++].OpCode != OpCodes.Callvirt) //@class.method_5(buffer); + if (instructions[i++].OpCode != OpCodes.Callvirt) //@class.method_5(buffer); return false; firstInstruction = @@ -118,7 +109,7 @@ private bool IsLzmaMethod(MethodDef method) i = instructions.IndexOf(firstInstruction) + 1; - if (!instructions[i++].IsStloc()) //int num2 = memoryStream.ReadByte(); + if (!instructions[i++].IsStloc()) //int num2 = memoryStream.ReadByte(); return false; if (!instructions[i++].IsLdloc()) @@ -143,14 +134,15 @@ private bool IsLzmaMethod(MethodDef method) return false; if (instructions[i++].OpCode != OpCodes.Or) return false; - if (!instructions[i++].IsStloc()) //num |= (long)((long)((ulong)((byte)num2)) << 8 * i); + if (!instructions[i++].IsStloc()) //num |= (long)((long)((ulong)((byte)num2)) << 8 * i); return false; firstInstruction = instructions.FirstOrDefault( instr => instr.OpCode == OpCodes.Newobj && - instr.Operand.ToString() == "System.Void System.IO.MemoryStream::.ctor(System.Byte[],System.Boolean)"); + instr.Operand.ToString() == + "System.Void System.IO.MemoryStream::.ctor(System.Byte[],System.Boolean)"); if (firstInstruction == null) return false; @@ -159,22 +151,24 @@ private bool IsLzmaMethod(MethodDef method) i = instructions.IndexOf(firstInstruction) + 1; - if (!instructions[i++].IsStloc()) //MemoryStream stream_ = new MemoryStream(array, true); + if (!instructions[i++].IsStloc()) //MemoryStream stream_ = new MemoryStream(array, true); return false; if (!instructions[i++].IsLdloc()) return false; - if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != "System.Int64 System.IO.Stream::get_Length()") + if (instructions[i].OpCode != OpCodes.Callvirt || instructions[i++].Operand.ToString() != + "System.Int64 System.IO.Stream::get_Length()") return false; - if (instructions[i].OpCode != OpCodes.Ldc_I8 || (long)instructions[i++].Operand != 13L) + if (instructions[i].OpCode != OpCodes.Ldc_I8 || (long) instructions[i++].Operand != 13L) return false; if (instructions[i++].OpCode != OpCodes.Sub) return false; - if (!instructions[i++].IsStloc()) //long long_ = memoryStream.Length - 13L; + if (!instructions[i++].IsStloc()) //long long_ = memoryStream.Length - 13L; return false; return true; } + private void ExtractNestedTypes(TypeDef type) { foreach (var method in type.Methods) @@ -187,9 +181,9 @@ private void ExtractNestedTypes(TypeDef type) var ntype = (inst.Operand as MethodDef).DeclaringType; if (!ntype.IsNested) continue; - if (types.Contains(ntype)) + if (Types.Contains(ntype)) continue; - types.Add(ntype); + Types.Add(ntype); ExtractNestedTypes(ntype); } } diff --git a/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs index a19e50a40..6a8e4611e 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; -using System.Text; using de4dot.blocks; using de4dot.blocks.cflow; using de4dot.code.deobfuscators.ConfuserEx.x86; @@ -11,29 +9,28 @@ namespace de4dot.code.deobfuscators.ConfuserEx { - class Context + internal class Context { - public uint FieldToken; public int ByteNum; public MethodDef CreateMethod; + public uint FieldToken; public Context(uint fieldToken, int byteNum, MethodDef createMethod) { - this.FieldToken = fieldToken; - this.ByteNum = byteNum; // 2nd parameter of the Delegate CreateMethod - this.CreateMethod = createMethod; + FieldToken = fieldToken; + ByteNum = byteNum; // 2nd parameter of the Delegate CreateMethod + CreateMethod = createMethod; } } - class ProxyCallFixer : ProxyCallFixer4 + internal class ProxyCallFixer : ProxyCallFixer4 { - public List NativeMethods = new List(); - public List AttributeTypes = new List(); - public List DelegateCreatorMethods = new List(); - private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); - private readonly ISimpleDeobfuscator _simpleDeobfuscator; private readonly List _processedMethods = new List(); + private readonly ISimpleDeobfuscator _simpleDeobfuscator; + public List AttributeTypes = new List(); + public List DelegateCreatorMethods = new List(); + public List NativeMethods = new List(); public ProxyCallFixer(ModuleDefMD module, ISimpleDeobfuscator simpleDeobfuscator) : base(module) { @@ -52,20 +49,21 @@ protected override object CheckCctor(TypeDef type, MethodDef cctor) _processedMethods.Add(cctor); } - List contexts = new List(); + var contexts = new List(); var instructions = cctor.Body.Instructions; instructions.SimplifyMacros(cctor.Body.Variables, cctor.Parameters); - for (int i = 0; i < instructions.Count; i++) + for (var i = 0; i < instructions.Count; i++) { - var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldtoken, OpCodes.Ldc_I4, OpCodes.Call); + var instrs = + DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldtoken, OpCodes.Ldc_I4, OpCodes.Call); if (instrs == null) continue; - uint fieldToken = ((IField) instrs[0].Operand).MDToken.ToUInt32(); - int byteNum = (int)instrs[1].Operand; + var fieldToken = ((IField) instrs[0].Operand).MDToken.ToUInt32(); + var byteNum = (int) instrs[1].Operand; var createMethod = instrs[2].Operand as MethodDef; - if(!DelegateCreatorMethods.Contains(createMethod)) + if (!DelegateCreatorMethods.Contains(createMethod)) DelegateCreatorMethods.Add(createMethod); contexts.Add(new Context(fieldToken, byteNum, createMethod)); @@ -84,7 +82,7 @@ private void DeobfuscateIfNeeded(MethodDef method) private byte[] GetExtraDataToken(byte[] sigData) { - byte[] extraData = new byte[4]; + var extraData = new byte[4]; // [original signature] [extra signature] // ... X C0 X X X @@ -94,11 +92,14 @@ private byte[] GetExtraDataToken(byte[] sigData) return extraData; } - protected override void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, out OpCode callOpcode) + protected override void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, + out OpCode callOpcode) { var contexts = (List) context; var ctx = contexts.First(c => c.FieldToken == field.MDToken.ToInt32()); - var originalMethod = DotNetUtils.Clone(ctx.CreateMethod); // backup original method and restore because changes are not universal + var originalMethod = + DotNetUtils.Clone(ctx + .CreateMethod); // backup original method and restore because changes are not universal DeobfuscateIfNeeded(ctx.CreateMethod); var instructions = ctx.CreateMethod.Body.Instructions; @@ -106,32 +107,32 @@ protected override void GetCallInfo(object context, FieldDef field, out IMethod var parameters = ctx.CreateMethod.Parameters; instructions.SimplifyMacros(variables, parameters); - byte[] sigData = module.ReadBlob(ctx.FieldToken); - byte[] extraDataToken = GetExtraDataToken(sigData); - int modifierMDToken = ((CModOptSig)field.FieldType).Modifier.MDToken.ToInt32(); + var sigData = module.ReadBlob(ctx.FieldToken); + var extraDataToken = GetExtraDataToken(sigData); + var modifierMDToken = ((CModOptSig) field.FieldType).Modifier.MDToken.ToInt32(); ReplaceMetadataToken(ref instructions, modifierMDToken, variables[0]); ReplaceFieldNameChars(ref instructions, field.Name, variables[0]); InlineArrays(ref instructions, extraDataToken, variables[1], variables[2]); RemoveDecrementorBlock(ref instructions, variables[2]); - int firstInstruction = GetEmulationStartIndex(instructions, variables[1], variables[2]); - int lastInstruction = + var firstInstruction = GetEmulationStartIndex(instructions, variables[1], variables[2]); + var lastInstruction = instructions.IndexOf( instructions.First( i => i.OpCode == OpCodes.Callvirt && i.Operand.ToString().Contains("GetCustomAttributes"))) - 4; - bool nativeMode = false; + var nativeMode = false; if (instructions[lastInstruction - 1].OpCode == OpCodes.Call) // x86 protection { lastInstruction--; // don't try emulating native method nativeMode = true; } - int result = EmulateManagedMethod(ctx.CreateMethod, firstInstruction, lastInstruction); + var result = EmulateManagedMethod(ctx.CreateMethod, firstInstruction, lastInstruction); if (nativeMode) { - MethodDef nativeMethod = (MethodDef) instructions[lastInstruction].Operand; + var nativeMethod = (MethodDef) instructions[lastInstruction].Operand; if (!NativeMethods.Contains(nativeMethod)) NativeMethods.Add(nativeMethod); result = EmulateNativeMethod(nativeMethod, result); @@ -140,10 +141,10 @@ protected override void GetCallInfo(object context, FieldDef field, out IMethod result *= GetMagicNumber(field.CustomAttributes[0]); calledMethod = module.ResolveMemberRef(new MDToken(result).Rid); - if(calledMethod == null) + if (calledMethod == null) throw new Exception(); - int charNum = GetCharNum(instructions, parameters.Last()); + var charNum = GetCharNum(instructions, parameters.Last()); callOpcode = GetCallOpCode(calledMethod, charNum, ctx.ByteNum); ctx.CreateMethod.Body = originalMethod.Body; // restore @@ -151,20 +152,17 @@ protected override void GetCallInfo(object context, FieldDef field, out IMethod private OpCode GetCallOpCode(IMethod calledMethod, int charNum, int byteNum) { - if (calledMethod.ResolveMethodDef().IsStatic) { - return OpCodes.Call; - } + if (calledMethod.ResolveMethodDef().IsStatic) return OpCodes.Call; - byte charOpCode = (byte)(charNum ^ byteNum); + var charOpCode = (byte) (charNum ^ byteNum); if (charOpCode == 0x28) return OpCodes.Call; - else if (charOpCode == 0x6F) + if (charOpCode == 0x6F) return OpCodes.Callvirt; - else if (charOpCode == 0x73) + if (charOpCode == 0x73) return OpCodes.Newobj; - else - throw new Exception(); + throw new Exception(); } private int EmulateNativeMethod(MethodDef externalMethod, int parameter) @@ -173,29 +171,28 @@ private int EmulateNativeMethod(MethodDef externalMethod, int parameter) return nativeMethod.Execute(parameter); } - private int EmulateManagedMethod(MethodDef method, int startIndex, int endIndex, params Tuple[] parameters) + private int EmulateManagedMethod(MethodDef method, int startIndex, int endIndex, + params Tuple[] parameters) { _instructionEmulator.Initialize(method, false); - foreach(var parameter in parameters) + foreach (var parameter in parameters) _instructionEmulator.SetArg(parameter.Item1, new Int32Value(parameter.Item2)); - for (int i = startIndex; i < endIndex; i++) { - _instructionEmulator.Emulate(method.Body.Instructions[i]); - } + for (var i = startIndex; i < endIndex; i++) _instructionEmulator.Emulate(method.Body.Instructions[i]); return ((Int32Value) _instructionEmulator.Pop()).Value; } private int GetMagicNumber(CustomAttribute customAttribute) { - TypeDef attributeType = customAttribute.AttributeType.ResolveTypeDef(); - if(!AttributeTypes.Contains(attributeType)) + var attributeType = customAttribute.AttributeType.ResolveTypeDef(); + if (!AttributeTypes.Contains(attributeType)) AttributeTypes.Add(attributeType); var ctor = attributeType.FindConstructors().First(); DeobfuscateIfNeeded(ctor); - int magicNum = Convert.ToInt32(customAttribute.ConstructorArguments[0].Value); + var magicNum = Convert.ToInt32(customAttribute.ConstructorArguments[0].Value); var parameter = new Tuple(); parameter.Item1 = ctor.Parameters[1]; parameter.Item2 = magicNum; @@ -225,7 +222,7 @@ var method in 0x000005C6 FE0E0200 IL_002E: stloc.2 */ private int GetEmulationStartIndex(IList instructions, Local localArray, Local localArraySize) { - for (int i = 0; i < instructions.Count; i++) + for (var i = 0; i < instructions.Count; i++) { var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Callvirt, OpCodes.Stloc, OpCodes.Ldloc, OpCodes.Ldlen, OpCodes.Conv_I4, OpCodes.Stloc); @@ -234,11 +231,11 @@ private int GetEmulationStartIndex(IList instructions, Local localA continue; if (!instrs[0].Operand.ToString().Contains("ResolveSignature")) continue; - if ((Local)instrs[1].Operand != localArray) + if ((Local) instrs[1].Operand != localArray) continue; - if ((Local)instrs[2].Operand != localArray) + if ((Local) instrs[2].Operand != localArray) continue; - if ((Local)instrs[5].Operand != localArraySize) + if ((Local) instrs[5].Operand != localArraySize) continue; return i + 6; @@ -250,7 +247,7 @@ private int GetEmulationStartIndex(IList instructions, Local localA 0x000008F4 61 IL_02BC: xor */ private int GetCharNum(IList instructions, Parameter byteParam) { - for (int i = 0; i < instructions.Count; i++) + for (var i = 0; i < instructions.Count; i++) { var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldarg, OpCodes.Xor); @@ -268,7 +265,8 @@ private int GetCharNum(IList instructions, Parameter byteParam) private void ReplaceFieldNameChars(ref IList instructions, string fieldName, Local fieldLocal) { bool foundInstrs; - do { + do + { foundInstrs = ReplaceFieldNameChar(ref instructions, fieldName, fieldLocal); } while (foundInstrs); } @@ -279,28 +277,28 @@ private void ReplaceFieldNameChars(ref IList instructions, string f 0x0000037C 6F1600000A IL_0084: callvirt */ private bool ReplaceFieldNameChar(ref IList instructions, string fieldName, Local fieldLocal) { - for (int i = 0; i < instructions.Count; i++) + for (var i = 0; i < instructions.Count; i++) { var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Callvirt, OpCodes.Ldc_I4, OpCodes.Callvirt); - if(instrs == null) + if (instrs == null) continue; - if ((Local)instrs[0].Operand != fieldLocal) + if ((Local) instrs[0].Operand != fieldLocal) continue; if (!instrs[1].Operand.ToString().Contains("get_Name")) continue; if (!instrs[3].Operand.ToString().Contains("get_Chars")) continue; - int charIndex = (int)instrs[2].Operand; + var charIndex = (int) instrs[2].Operand; int @char = fieldName[charIndex]; instructions[i].OpCode = OpCodes.Ldc_I4; instructions[i].Operand = @char; - instructions[i+1].OpCode = OpCodes.Nop; - instructions[i+2].OpCode = OpCodes.Nop; - instructions[i+3].OpCode = OpCodes.Nop; + instructions[i + 1].OpCode = OpCodes.Nop; + instructions[i + 2].OpCode = OpCodes.Nop; + instructions[i + 3].OpCode = OpCodes.Nop; return true; } return false; @@ -315,28 +313,29 @@ private bool ReplaceFieldNameChar(ref IList instructions, string fi private void InlineArrays(ref IList instructions, byte[] values, Local localArray, Local localInt) { bool foundInstrs; - int i = 0; - do { + var i = 0; + do + { foundInstrs = InlineArray(ref instructions, values[i++], localArray, localInt); } while (i < 4 && foundInstrs); } private bool InlineArray(ref IList instructions, int value, Local localArray, Local localInt) { - for (int i = 0; i < instructions.Count; i++) + for (var i = 0; i < instructions.Count; i++) { var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Ldloc, OpCodes.Ldc_I4, OpCodes.Sub, OpCodes.Dup, OpCodes.Stloc, OpCodes.Ldelem_U1); if (instrs == null) continue; - if ((Local)instrs[0].Operand != localArray) + if ((Local) instrs[0].Operand != localArray) continue; - if ((Local)instrs[1].Operand != localInt) + if ((Local) instrs[1].Operand != localInt) continue; - if ((int)instrs[2].Operand != 1) + if ((int) instrs[2].Operand != 1) continue; - if ((Local)instrs[5].Operand != localInt) + if ((Local) instrs[5].Operand != localInt) continue; instructions[i].OpCode = OpCodes.Ldc_I4; @@ -358,17 +357,18 @@ private bool InlineArray(ref IList instructions, int value, Local l 0x00000374 0C IL_007C: stloc.2 */ private void RemoveDecrementorBlock(ref IList instructions, Local localInt) { - for (int i = 0; i < instructions.Count; i++) + for (var i = 0; i < instructions.Count; i++) { - var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Ldc_I4, OpCodes.Sub, OpCodes.Stloc); + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Ldc_I4, OpCodes.Sub, + OpCodes.Stloc); if (instrs == null) continue; - if ((Local)instrs[0].Operand != localInt) - continue; - if ((int)instrs[1].Operand != 1) + if ((Local) instrs[0].Operand != localInt) + continue; + if ((int) instrs[1].Operand != 1) continue; - if ((Local)instrs[3].Operand != localInt) + if ((Local) instrs[3].Operand != localInt) continue; instructions[i].OpCode = OpCodes.Nop; @@ -386,18 +386,19 @@ private void RemoveDecrementorBlock(ref IList instructions, Local l 0x000005DE 6F1400000A IL_0046: callvirt instance int32[mscorlib] System.Reflection.MemberInfo::get_MetadataToken() */ private void ReplaceMetadataToken(ref IList instructions, int metadataToken, Local fieldLocal) { - for (int i = 0; i < instructions.Count; i++) + for (var i = 0; i < instructions.Count; i++) { - var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Callvirt, OpCodes.Ldc_I4, + var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Callvirt, + OpCodes.Ldc_I4, OpCodes.Ldelem_Ref, OpCodes.Callvirt); if (instrs == null) continue; - if ((Local)instrs[0].Operand != fieldLocal) + if ((Local) instrs[0].Operand != fieldLocal) continue; if (!instrs[1].Operand.ToString().Contains("GetOptionalCustomModifiers")) continue; - if ((int)instrs[2].Operand != 0) + if ((int) instrs[2].Operand != 0) continue; if (!instrs[4].Operand.ToString().Contains("get_MetadataToken")) continue; @@ -412,4 +413,4 @@ private void ReplaceMetadataToken(ref IList instructions, int metad } } } -} +} \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs index bfa15899a..f66f57c94 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs @@ -1,65 +1,41 @@ -using de4dot.blocks; -using dnlib.DotNet; -using dnlib.DotNet.Emit; -using System; +using System; using System.Collections.Generic; using System.Runtime.InteropServices; -using System.Text; +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; namespace de4dot.code.deobfuscators.ConfuserEx { public class ResourceDecrypter { - TypeDef arrayType; - MethodDef resourceDecInitMethod, assembyResolveMethod; - FieldDef arrayField, asmField; - byte[] decryptedBytes; + private FieldDef _arrayField, _asmField; - bool canRemoveLzma = true; + private byte[] _decryptedBytes; + private readonly ISimpleDeobfuscator _deobfuscator; + private readonly MethodDef _lzmaMethod; - public bool CanRemoveLzma - { - get { return canRemoveLzma; } - } - - public TypeDef Type - { - get { return arrayType; } - } - public MethodDef Method - { - get { return resourceDecInitMethod; } - } - public MethodDef AssembyResolveMethod - { - get { return assembyResolveMethod; } - } - public List Fields - { - get { return new List() { arrayField, asmField }; } - } - public bool Detected - { - get - { - return resourceDecInitMethod != null && resourceDecInitMethod != null - && decryptedBytes != null && arrayField != null && asmField != null; - } - } + private readonly ModuleDef _module; - ModuleDef module; - MethodDef lzmaMethod; - ISimpleDeobfuscator deobfuscator; public ResourceDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator) { - this.module = module; - this.lzmaMethod = lzmaMethod; - this.deobfuscator = deobfsucator; + this._module = module; + this._lzmaMethod = lzmaMethod; + _deobfuscator = deobfsucator; } + public bool CanRemoveLzma { get; private set; } = true; + + public TypeDef Type { get; private set; } + public MethodDef Method { get; private set; } + public MethodDef AssembyResolveMethod { get; private set; } + public List Fields => new List {_arrayField, _asmField}; + + public bool Detected => Method != null && _decryptedBytes != null && _arrayField != null && _asmField != null; + public void Find() { - var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); + var moduleCctor = DotNetUtils.GetModuleTypeCctor(_module); if (moduleCctor == null) return; foreach (var inst in moduleCctor.Body.Instructions) @@ -73,14 +49,14 @@ public void Find() continue; if (!DotNetUtils.IsMethod(method, "System.Void", "()")) continue; - deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); - if (!isResDecryptInit(method)) + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + if (!IsResDecryptInit(method)) continue; - resourceDecInitMethod = method; + Method = method; } } - bool isResDecryptInit(MethodDef method) + private bool IsResDecryptInit(MethodDef method) { var instr = method.Body.Instructions; @@ -89,7 +65,7 @@ bool isResDecryptInit(MethodDef method) if (!instr[0].IsLdcI4()) return false; - if (!instr[1].IsStloc()) //uint num = 96u; + if (!instr[1].IsStloc()) //uint num = 96u; return false; if (!instr[2].IsLdcI4()) @@ -113,9 +89,11 @@ bool isResDecryptInit(MethodDef method) return false; if (instr[6].OpCode != OpCodes.Call) return false; - if (instr[6].Operand.ToString() != "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)") + if (instr[6].Operand.ToString() != + "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)" + ) return false; - if (!instr[7].IsStloc()) // uint[] array = new uint[] {.....}; + if (!instr[7].IsStloc()) // uint[] array = new uint[] {.....}; return false; var l = instr.Count; @@ -123,13 +101,14 @@ bool isResDecryptInit(MethodDef method) return false; if (instr[l - 9].OpCode != OpCodes.Call) return false; - if (instr[l - 9].Operand != lzmaMethod) + if (instr[l - 9].Operand != _lzmaMethod) return false; if (instr[l - 8].OpCode != OpCodes.Call) return false; - if (instr[l - 8].Operand.ToString() != "System.Reflection.Assembly System.Reflection.Assembly::Load(System.Byte[])") + if (instr[l - 8].Operand.ToString() != + "System.Reflection.Assembly System.Reflection.Assembly::Load(System.Byte[])") return false; - if (instr[l - 7].OpCode != OpCodes.Stsfld) //.assembly_0 = Assembly.Load(array4); + if (instr[l - 7].OpCode != OpCodes.Stsfld) //.assembly_0 = Assembly.Load(array4); return false; var asField = instr[l - 7].Operand as FieldDef; if (asField == null) @@ -150,84 +129,89 @@ bool isResDecryptInit(MethodDef method) return false; if (instr[l - 3].OpCode != OpCodes.Newobj) return false; - if (instr[l - 2].OpCode != OpCodes.Callvirt) //AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(.smethod_1); + if (instr[l - 2].OpCode != OpCodes.Callvirt + ) //AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(.smethod_1); return false; try { DecryptArray(ConvertArray(aField.InitialValue)); } - catch { canRemoveLzma = false; return false; } - arrayField = aField; - arrayType = DotNetUtils.GetType(module, aField.FieldSig.Type); - asmField = asField; - assembyResolveMethod = mtd; + catch + { + CanRemoveLzma = false; + return false; + } + _arrayField = aField; + Type = DotNetUtils.GetType(_module, aField.FieldSig.Type); + _asmField = asField; + AssembyResolveMethod = mtd; return true; } + private T[] ConvertArray(T1[] array) { - int l = Marshal.SizeOf(typeof(T)); - int l1 = Marshal.SizeOf(typeof(T1)); - var buffer = new T[(array.Length * l1) / l]; + var l = Marshal.SizeOf(typeof(T)); + var l1 = Marshal.SizeOf(typeof(T1)); + var buffer = new T[array.Length * l1 / l]; Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); return buffer; } + private void DecryptArray(uint[] array) //TODO: Automatic detection { - int num = array.Length; - uint[] array2 = new uint[16]; - uint num2 = 825993394u; - for (int i = 0; i < 16; i++) + var num = array.Length; + var array2 = new uint[16]; + var num2 = 825993394u; + for (var i = 0; i < 16; i++) { num2 ^= num2 >> 13; num2 ^= num2 << 25; num2 ^= num2 >> 27; array2[i] = num2; } - int num3 = 0; - int num4 = 0; - uint[] array3 = new uint[16]; - byte[] array4 = new byte[num * 4u]; - while ((long)num3 < (long)((ulong)num)) + var num3 = 0; + var num4 = 0; + var array3 = new uint[16]; + var array4 = new byte[num * 4u]; + while (num3 < (long) (ulong) num) { - for (int j = 0; j < 16; j++) - { + for (var j = 0; j < 16; j++) array3[j] = array[num3 + j]; - } - array3[0] = (array3[0] ^ array2[0]); - array3[1] = (array3[1] ^ array2[1]); - array3[2] = (array3[2] ^ array2[2]); - array3[3] = (array3[3] ^ array2[3]); - array3[4] = (array3[4] ^ array2[4]); - array3[5] = (array3[5] ^ array2[5]); - array3[6] = (array3[6] ^ array2[6]); - array3[7] = (array3[7] ^ array2[7]); - array3[8] = (array3[8] ^ array2[8]); - array3[9] = (array3[9] ^ array2[9]); - array3[10] = (array3[10] ^ array2[10]); - array3[11] = (array3[11] ^ array2[11]); - array3[12] = (array3[12] ^ array2[12]); - array3[13] = (array3[13] ^ array2[13]); - array3[14] = (array3[14] ^ array2[14]); - array3[15] = (array3[15] ^ array2[15]); - for (int k = 0; k < 16; k++) + array3[0] = array3[0] ^ array2[0]; + array3[1] = array3[1] ^ array2[1]; + array3[2] = array3[2] ^ array2[2]; + array3[3] = array3[3] ^ array2[3]; + array3[4] = array3[4] ^ array2[4]; + array3[5] = array3[5] ^ array2[5]; + array3[6] = array3[6] ^ array2[6]; + array3[7] = array3[7] ^ array2[7]; + array3[8] = array3[8] ^ array2[8]; + array3[9] = array3[9] ^ array2[9]; + array3[10] = array3[10] ^ array2[10]; + array3[11] = array3[11] ^ array2[11]; + array3[12] = array3[12] ^ array2[12]; + array3[13] = array3[13] ^ array2[13]; + array3[14] = array3[14] ^ array2[14]; + array3[15] = array3[15] ^ array2[15]; + for (var k = 0; k < 16; k++) { - uint num5 = array3[k]; - array4[num4++] = (byte)num5; - array4[num4++] = (byte)(num5 >> 8); - array4[num4++] = (byte)(num5 >> 16); - array4[num4++] = (byte)(num5 >> 24); + var num5 = array3[k]; + array4[num4++] = (byte) num5; + array4[num4++] = (byte) (num5 >> 8); + array4[num4++] = (byte) (num5 >> 16); + array4[num4++] = (byte) (num5 >> 24); array2[k] ^= num5; } num3 += 16; } - decryptedBytes = Lzma.Decompress(array4); + _decryptedBytes = Lzma.Decompress(array4); } private bool IsAssembyResolveMethod(MethodDef method, FieldDef field) { if (DotNetUtils.IsMethod(method, "", "()")) return false; - deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); var instr = method.Body.Instructions; if (instr.Count != 10) @@ -271,23 +255,27 @@ public void Fix() ModuleDef newModule; try { - newModule = ModuleDefMD.Load(decryptedBytes); + newModule = ModuleDefMD.Load(_decryptedBytes); + } + catch + { + CanRemoveLzma = false; + return; } - catch { canRemoveLzma = false; return; } - List toRemove = new List(); - List toAdd = new List(); - foreach (var cryptedResource in module.Resources) - foreach (var resource in newModule.Resources) - if (cryptedResource.Name == resource.Name) - { - toRemove.Add(cryptedResource); - toAdd.Add(resource); - } + var toRemove = new List(); + var toAdd = new List(); + foreach (var cryptedResource in _module.Resources) + foreach (var resource in newModule.Resources) + if (cryptedResource.Name == resource.Name) + { + toRemove.Add(cryptedResource); + toAdd.Add(resource); + } foreach (var resToRemove in toRemove) - module.Resources.Remove(resToRemove); + _module.Resources.Remove(resToRemove); foreach (var resToAdd in toAdd) - module.Resources.Add(resToAdd); + _module.Resources.Add(resToAdd); } } } \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/Utils.cs b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs index 5ab31d790..543554092 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Utils.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; using de4dot.blocks; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -128,7 +126,8 @@ public static bool IsLoc(this Instr instr) public static bool IsValidInstr(this Instr instr) { - return IsArithmetical(instr) || instr.IsConv() || IsLdc(instr) || IsLoc(instr) || instr.OpCode == OpCodes.Dup; + return IsArithmetical(instr) || instr.IsConv() || IsLdc(instr) || IsLoc(instr) || + instr.OpCode == OpCodes.Dup; } public static bool IsDup(this Block block) @@ -188,7 +187,7 @@ public static bool IsNative(this SwitchData switchData) return false; if (!DotNetUtils.IsMethod(method, "System.Int32", "(System.Int32)")) return false; - for (int i = switchData.IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++) + for (var i = switchData.IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++) if (!instr[i].IsValidInstr()) return false; @@ -238,8 +237,8 @@ public static bool IsTernary(this Block block) var sources = block.Sources; if (sources.Count != 2) return false; - if (!sources[0].IsDup() || !(sources[1]).IsDup()) //TODO: Case without DUP? - return false; + if (!sources[0].IsDup() || !sources[1].IsDup()) //TODO: Case without DUP? + return false; if (sources[0].CountTargets() > 1 || sources[1].CountTargets() > 1) return false; if (sources[0].FallThrough != block || sources[1].FallThrough != block) @@ -256,16 +255,11 @@ public static bool IsTernary(this Block block) public static List GetTernaryPredicates(this List switchCaseBlocks) { - List ternaryPredicates = new List(); + var ternaryPredicates = new List(); - foreach (Block preBlock in switchCaseBlocks) - { - if (IsTernary(preBlock)) - { - // switchCaseBlock -> 2x sourceBlock -> ternaryPredicateBlock + foreach (var preBlock in switchCaseBlocks) + if (IsTernary(preBlock)) // switchCaseBlock -> 2x sourceBlock -> ternaryPredicateBlock ternaryPredicates.Add(preBlock.Sources[0].Sources[0]); - } - } return ternaryPredicates; } @@ -275,4 +269,4 @@ public static Block GetTernaryPredicateMainBlock(this Block ternaryPredicateBloc return ternaryPredicateBlock.FallThrough.FallThrough; } } -} +} \ No newline at end of file diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs index e972a46e1..898c3c1f6 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86ADD.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs index f2a1c3bd7..07b4903a8 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86DIV.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs index f5668cf75..c8d4d6163 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86IMUL.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs index 145c04231..fb9533444 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86MOV.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs index b28d91007..68ead3e13 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NEG.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs index 07e8877bf..6b98143ba 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86NOT.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs index 251509a15..adf82a5d2 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86POP.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs index d893c5992..5c92c5a56 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86PUSH.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs index d43e0281b..1967ddba8 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86SUB.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs index 1b6351f7b..4d415d3b6 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Instructions/X86XOR.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using de4dot.Bea; +using de4dot.code.deobfuscators.ConfuserEx.x86; namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs index 3501ea92c..648b95cd1 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/UnmanagedBuff.cs @@ -1,12 +1,12 @@ using System; using System.Runtime.InteropServices; -namespace de4dot.code.x86 +namespace de4dot.code.deobfuscators.ConfuserEx.x86 { public class UnmanagedBuffer { - public readonly IntPtr Ptr = IntPtr.Zero; - public readonly int Length = 0; + public readonly IntPtr Ptr; + public readonly int Length; public UnmanagedBuffer(byte[] data) { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs index 9b94573c7..dd351af10 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Instruction.cs @@ -1,8 +1,8 @@ -using de4dot.Bea; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; +using de4dot.Bea; -namespace ConfuserDeobfuscator.Engine.Routines.Ex.x86 +namespace de4dot.code.deobfuscators.ConfuserEx.x86 { public enum X86OpCode { diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs index 81842623f..a19cf81eb 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs @@ -5,7 +5,6 @@ using ConfuserDeobfuscator.Engine.Routines.Ex.x86; using ConfuserDeobfuscator.Engine.Routines.Ex.x86.Instructions; using de4dot.Bea; -using de4dot.code.x86; using dnlib.DotNet; namespace de4dot.code.deobfuscators.ConfuserEx.x86 @@ -27,7 +26,7 @@ public sealed class X86Method {"EDI", 0} }; - readonly ModuleDefMD _module; + private readonly ModuleDefMD _module; public X86Method(MethodDef method,ModuleDefMD module) { this._module = module; From eff56174939a6c194034ee50cc74402b70eacb01 Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Sat, 19 Aug 2017 17:40:14 +0300 Subject: [PATCH 08/14] misc changes Copy BeaEngine.dll on build Disable more exception handlers to help detect swallowed exceptions Misc refactoring --- de4dot.code/ObfuscatedFile.cs | 10 +- .../ConfuserEx/ConstantDecrypter.cs | 494 +++++++++++------- .../deobfuscators/ConfuserEx/Deobfuscator.cs | 21 +- .../ConfuserEx/x86/Bea/Constants.cs | 2 +- .../ConfuserEx/x86/Bea/Engine.cs | 5 +- .../deobfuscators/ConfuserEx/x86/X86Method.cs | 5 +- de4dot.cui/FilesDeobfuscator.cs | 11 +- 7 files changed, 332 insertions(+), 216 deletions(-) diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index ccdb8faae..611b6a2ec 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -552,9 +552,10 @@ void DeobfuscateMethods() { } int oldIndentLevel = Logger.Instance.IndentLevel; - try { + //TODO: Re-enable exception handler + //try { Deobfuscate(method, cflowDeobfuscator, methodPrinter, isVerbose, isVV); - } + /*} catch (Exception ex) { if (!CanLoadMethodBody(method)) { if (isVerbose) @@ -569,8 +570,9 @@ void DeobfuscateMethods() { } finally { Logger.Instance.IndentLevel = oldIndentLevel; - } - RemoveNoInliningAttribute(method); + }*/ + + RemoveNoInliningAttribute(method); if (isVerbose) Logger.Instance.DeIndent(); diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs index b96218f95..b13bd363f 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs @@ -215,214 +215,322 @@ private static T[] ConvertArray(T1[] array) private void DecryptArray(uint[] array) //TODO: Automatic detection { - var num = 960u; // array size? - var array2 = new uint[16]; - var num2 = 4136251032u; - for (var i = 0; i < 16; i++) + var num = 1888u; // array size? + uint[] array2 = new uint[16]; + uint num2 = 3153506350u; + for (int i = 0; i < 16; i++) { num2 ^= num2 >> 12; num2 ^= num2 << 25; num2 ^= num2 >> 27; array2[i] = num2; } - var num3 = 0; - var num4 = 0; - var array3 = new uint[16]; - var array4 = new byte[num * 4u]; - while (num3 < num) + int num3 = 0; + int num4 = 0; + uint[] array3 = new uint[16]; + byte[] array4 = new byte[num * 4u]; + while ((long)num3 < (long)((ulong)num)) { - for (var j = 0; j < 16; j++) + for (int j = 0; j < 16; j++) + { array3[j] = array[num3 + j]; - var num5 = array3[3] * 41u; - array3[11] = array3[11] ^ 3634844963u; - var num6 = array3[3] * 31u; - num6 += array3[9] * 47u; - num5 += array3[9] * 85u; - num5 += array3[10] * 149u; - var num7 = array3[3] << 1; - num7 += array3[3]; - var num8 = array3[3] << 1; - num8 += array3[3] << 3; - num7 += array3[9] << 3; - num8 += array3[9] * 13u; - num7 += array3[9]; - num6 += array3[10] * 71u; - num7 += array3[10] << 1; - num6 += array3[1] * 81u; - array3[4] = array3[4] ^ ~array3[6]; - num8 += array3[10] << 1; - num7 += array3[10] << 4; - array3[9] = num6; - num8 += array3[10] << 4; - array3[6] = array3[6] * 395315459u; - num8 += array3[1] * 19u; - num7 += array3[1] * 23u; - num5 += array3[1] * 184u; - num6 = array3[7] * 19u; - array3[10] = num7; - num6 += array3[8] * 28u; - array3[14] = array3[14] ^ array3[0]; - array3[3] = num8; - num6 += array3[12] << 6; + } + uint num5 = array3[1] << 1; + uint num6 = array3[1] * 21u; + array3[2] = (array3[2] ^ array3[10]); + num5 += array3[1] << 2; + uint num7 = array3[1] * 21u; + num6 += array3[0] * 67u; + uint num8 = array3[1] * 13u; + num5 += array3[0] * 14u; + num6 += array3[9] * 157u; + num8 += array3[0] << 2; + num5 += array3[9] * 27u; + array3[13] = array3[13] * 748798011u; + num8 += array3[0] << 5; + num7 += array3[0] * 57u; + num6 += array3[3] * 206u; + num5 += array3[3] * 43u; array3[1] = num5; - array3[2] = array3[2] ^ array2[2]; - num5 = array3[7] * 28u; - num5 += array3[8] << 2; - num8 = array3[7] << 1; - num7 = array3[7] << 5; - num8 += array3[7] << 3; - num8 += array3[8] * 13u; - num7 += array3[7]; - num6 += array3[12]; - num7 += array3[8] * 42u; - array3[4] = array3[4] - array3[10]; - num8 += array3[12] << 5; - num6 += array3[15] * 85u; - num5 += array3[8] << 5; - array3[7] = num6; - array3[11] = array3[11] - 2867139633u; - num7 += array3[12] * 108u; - num5 += array3[12] * 93u; - num8 += array3[12]; - num5 += array3[15] * 141u; - num8 += array3[15] * 49u; - num7 += array3[15] * 163u; - array3[12] = num5; - array3[15] = num7; - array3[8] = num8; - num5 = array3[7] >> 21; - num6 = array3[15] >> 22; - array3[15] = array3[15] << 10; - num8 = array3[1] >> 21; - array3[15] = array3[15] | num6; - array3[12] = array3[12] ^ array2[12]; - num6 = array3[2] & 3262151220u; - array3[1] = array3[1] << 11; - array3[1] = array3[1] | num8; - array3[7] = array3[7] << 11; - array3[0] = array3[0] - array3[14]; - num7 = array3[13] << 4; - num8 = array3[3] * 954284655u; - array3[3] = array3[5]; - array3[5] = num8 * 3102958735u; - array3[7] = array3[7] | num5; - num5 = array3[10] << 4; - num8 = array3[9] * 2468501497u; - array3[2] = array3[2] & 1032816075u; - array3[13] = array3[13] >> 28; - array3[13] = array3[13] | num7; - array3[7] = array3[7] - 888060325u; - array3[2] = array3[2] | (array3[8] & 3262151220u); - array3[12] = array3[12] * 4056148675u; - array3[9] = array3[13]; - num7 = array3[6] << 5; - array3[13] = num8 * 1746582089u; - array3[6] = array3[6] >> 27; - array3[6] = array3[6] | num7; - array3[8] = array3[8] & 1032816075u; - array3[7] = array3[7] ^ array2[7]; - num5 += array3[11] * 46u; - num6 *= 869722291u; - num8 = array3[10] << 1; - num5 += array3[3] * 92u; - num5 += array3[5] * 149u; - array3[7] = array3[7] - 3922202313u; - array3[8] = array3[8] | (num6 * 2576221819u); - num8 += array3[11] * 15u; - num8 += array3[3] * 37u; - num6 = array3[10] * 7u; - array3[8] = array3[8] ^ 1878284212u; - num8 += array3[5] * 56u; - array3[9] = array3[9] ^ array2[9]; - num7 = array3[10] << 3; - array3[6] = array3[6] ^ 2841119440u; - num6 += array3[11] << 4; - array3[2] = array3[2] ^ 217219923u; - num7 += array3[10]; - num6 += array3[3] * 29u; - array3[6] = array3[6] ^ array2[6]; - num7 += array3[11] * 26u; - num7 += array3[3] * 52u; - num6 += array3[5] * 49u; - num7 += array3[5] * 84u; + num7 += array3[9] * 133u; + num8 += array3[9] * 77u; + num8 += array3[3] * 110u; + array3[9] = num6; + num6 = (array3[12] & 1056153664u); + array3[12] = (array3[12] & 3238813631u); + array3[0] = num8; + array3[12] = (array3[12] | (array3[7] & 1056153664u)); + array3[7] = (array3[7] & 3238813631u); + num8 = array3[2] << 3; + num5 = array3[8] * 2590225985u; + num6 *= 770570833u; + array3[7] = (array3[7] | num6 * 2945289905u); + array3[8] = array3[4]; + num8 += array3[13] * 50u; + num7 += array3[3] * 181u; + array3[0] = array3[0] * 154310079u; + num6 = (array3[15] & 1073272377u); + array3[10] = array3[10] - 4001279812u; + array3[3] = num7; + array3[15] = (array3[15] & 3221694918u); + num8 += array3[9] * 67u; + num7 = array3[5] << 14; + array3[1] = (array3[1] ^ array2[1]); + array3[15] = (array3[15] | (array3[11] & 1073272377u)); + array3[5] = array3[5] >> 18; + array3[12] = (array3[12] ^ array2[12]); + array3[4] = num5 * 2830588353u; + array3[11] = (array3[11] & 3221694918u); + array3[5] = (array3[5] | num7); + array3[6] = (array3[6] ^ array3[14]); + num7 = array3[2] << 1; + num6 *= 918007135u; + num7 += array3[2]; + array3[11] = (array3[11] | num6 * 3949194911u); + num5 = array3[2] << 2; + num7 += array3[13] * 19u; + num8 += array3[11] << 2; + num6 = array3[2]; + num6 += array3[13] << 1; + num6 += array3[13] << 3; + array3[8] = (array3[8] ^ 4107405834u); + num7 += array3[9] * 25u; + num6 += array3[9] * 14u; + num8 += array3[11] << 7; + num5 += array3[13] * 38u; + num6 += array3[11] * 31u; + array3[3] = (array3[3] ^ array2[3]); + num5 += array3[9] * 56u; + array3[1] = array3[1] - 1508476838u; + array3[3] = (array3[3] ^ 938209744u); + array3[2] = num6; + num7 += array3[11] * 49u; + array3[9] = num8; + num8 = array3[6] << 1; + array3[7] = (array3[7] ^ ~array3[15]); + num8 += array3[6] << 3; + num5 += array3[11] * 125u; + array3[0] = (array3[0] ^ array2[0]); + array3[11] = num5; + num8 += array3[5] << 5; + num6 = array3[6] * 11u; + array3[13] = num7; + num5 = array3[6] * 55u; + num8 += array3[5]; + num7 = array3[6] * 54u; + num8 += array3[3] * 39u; + num8 += array3[4] << 1; + num7 += array3[5] * 175u; + num8 += array3[4] << 5; + num7 += array3[3] * 209u; + num5 += array3[5] * 179u; + num5 += array3[3] * 213u; + num6 += array3[5] * 35u; + num7 += array3[4] * 177u; + num6 += array3[3] * 42u; + array3[13] = (array3[13] ^ array3[9]); + num6 += array3[4] << 1; + num6 += array3[4] << 5; + array3[7] = (array3[7] ^ array2[7]); + array3[6] = num6; + num5 += array3[4] * 181u; + num6 = array3[14] >> 29; array3[3] = num5; - array3[10] = num6; - num6 = array3[1] * 15u; - array3[12] = array3[12] ^ 1080861703u; array3[5] = num8; - num5 = array3[4] & 3659960635u; - num6 += array3[12] << 1; - array3[4] = array3[4] & 635006660u; - array3[4] = array3[4] | (array3[9] & 3659960635u); - num5 *= 1676034815u; - array3[11] = num7; - num7 = array3[1] * 19u; - num6 += array3[12] << 4; - array3[9] = array3[9] & 635006660u; - num6 += array3[3] << 6; - num7 += array3[12] * 27u; - array3[5] = array3[5] - array3[8]; - array3[9] = array3[9] | (num5 * 1267776767u); - num5 = array3[1] << 2; - num5 += array3[1]; - array3[13] = array3[13] ^ array2[13]; - num8 = array3[1]; - num6 += array3[3]; - num5 += array3[12] << 3; - num8 += array3[12] << 1; + num5 = array3[8] << 2; + num8 = array3[8] << 2; + array3[4] = num7; + array3[14] = array3[14] << 3; + array3[12] = (array3[12] ^ 2411275161u); + num8 += array3[8]; + num5 += array3[7] * 7u; + num5 += array3[6] * 19u; + num8 += array3[7] * 21u; + num7 = array3[11] << 24; + array3[14] = (array3[14] | num6); + array3[11] = array3[11] >> 8; + array3[11] = (array3[11] | num7); + num6 = array3[8] * 11u; + num5 += array3[13] * 46u; + num8 += array3[6] * 47u; + num7 = array3[8]; + num7 += array3[7] << 1; + num6 += array3[7] * 25u; + num8 += array3[13] * 109u; + array3[4] = (array3[4] ^ array2[4]); + num6 += array3[6] * 63u; + array3[2] = (array3[2] ^ array3[12]); + num7 += array3[7] << 2; + num7 += array3[6] * 13u; + array3[6] = num5; + num5 = (array3[0] & 247307561u); + num5 *= 2926546863u; + array3[0] = (array3[0] & 4047659734u); + array3[0] = (array3[0] | (array3[11] & 247307561u)); + num7 += array3[13] * 30u; + num6 += array3[13] * 150u; + array3[11] = (array3[11] & 4047659734u); + array3[11] = (array3[11] | num5 * 1929455439u); + array3[7] = num8; + num5 = array3[14] << 20; + array3[14] = array3[14] >> 12; + array3[14] = (array3[14] | num5); + num8 = array3[15] * 19u; + num5 = array3[3] << 8; + array3[3] = array3[3] >> 24; + array3[14] = (array3[14] ^ array2[14]); + array3[3] = (array3[3] | num5); + num8 += array3[5] * 69u; + num5 = array3[15] * 23u; + array3[8] = num7; + array3[13] = num6; + num5 += array3[5] * 86u; + array3[6] = (array3[6] ^ 3317586132u); + array3[8] = (array3[8] ^ array2[8]); + array3[4] = array3[4] - 3314395924u; + num8 += array3[9] << 1; + num6 = array3[15] << 2; + array3[13] = (array3[13] ^ 574204725u); + num8 += array3[9] << 6; + num5 += array3[9] * 82u; + array3[2] = (array3[2] ^ 1681301553u); + num8 += array3[1] * 76u; + num7 = array3[15] * 49u; + num6 += array3[15] << 6; + array3[15] = num8; + num7 += array3[5] * 182u; + num6 += array3[5] * 249u; + num6 += array3[9] * 239u; + num7 += array3[9] * 174u; + num7 += array3[1] * 218u; + array3[9] = num7; + num7 = array3[12] << 1; + num5 += array3[1] * 105u; + array3[5] = num5; + num5 = array3[12] << 1; + num8 = array3[10] >> 2; + num5 += array3[7] * 7u; + array3[10] = array3[10] << 30; + num6 += array3[1] * 285u; + array3[1] = num6; + array3[10] = (array3[10] | num8); + num6 = array3[12] * 28u; + num7 += array3[12] << 4; + num7 += array3[7] * 57u; + num7 += array3[13] * 95u; + num5 += array3[13] << 2; + num8 = array3[4] >> 3; + array3[15] = (array3[15] ^ array2[15]); + array3[4] = array3[4] << 29; + array3[4] = (array3[4] | num8); + num6 += array3[7] * 75u; + num8 = array3[12] << 1; + num6 += array3[13] * 113u; + num5 += array3[13] << 3; num8 += array3[12]; - num6 += array3[15] * 22u; - num5 += array3[3] * 27u; - num5 += array3[15] << 3; - num7 += array3[3] * 92u; + num6 += array3[10] << 1; + num8 += array3[7] * 11u; + num7 += array3[10] << 1; + num7 += array3[10]; + array3[11] = (array3[11] ^ array3[1]); + num8 += array3[13] << 2; + array3[7] = num5; + num8 += array3[13] << 4; + num5 = array3[9]; + array3[13] = num7; + array3[12] = num8; + num8 = array3[9]; + num8 += array3[6] << 1; + num8 += array3[6]; + array3[0] = array3[0] - array3[15]; + num6 += array3[10] << 3; num8 += array3[3] << 3; + num5 += array3[6] << 4; + array3[5] = (array3[5] ^ array3[14]); + array3[10] = num6; + num6 = array3[9] << 1; + num5 += array3[3] * 58u; + num6 += array3[9]; num8 += array3[3]; - num5 += array3[15]; - num8 += array3[15] << 1; - num8 += array3[15]; - array3[3] = num6; - array3[0] = array3[0] ^ array3[13]; - array3[14] = array3[14] - array3[15]; - num7 += array3[15] << 5; - array3[13] = array3[13] ^ ~array3[1]; - num6 = array3[10] >> 31; - array3[14] = array3[14] ^ array2[14]; - array3[8] = array3[8] ^ array2[8]; - array3[12] = num5; - array3[1] = num8; - array3[5] = array3[5] ^ array2[5]; - array3[11] = array3[11] ^ array2[11]; - num5 = array3[11] & 2204625944u; - array3[1] = array3[1] ^ array2[1]; - array3[4] = array3[4] ^ array2[4]; - array3[11] = array3[11] & 2090341351u; - array3[11] = array3[11] | (array3[4] & 2204625944u); - array3[15] = num7; - num8 = array3[14] & 2496954112u; - array3[14] = array3[14] & 1798013183u; - array3[4] = array3[4] & 2090341351u; - array3[15] = array3[15] ^ array2[15]; - array3[10] = array3[10] << 1; - num5 *= 338764649u; - array3[14] = array3[14] | (array3[9] & 2496954112u); - array3[15] = array3[15] - array3[0]; - array3[10] = array3[10] | num6; - array3[10] = array3[10] ^ array2[10]; - array3[3] = array3[3] ^ array2[3]; - num8 *= 2292397853u; - array3[0] = array3[0] ^ array2[0]; - array3[0] = array3[0] ^ 2814140307u; - array3[2] = array3[2] ^ ~array3[13]; - array3[4] = array3[4] | (num5 * 587046105u); - array3[9] = array3[9] & 1798013183u; - array3[9] = array3[9] | (num8 * 1520255797u); - for (var k = 0; k < 16; k++) + num8 += array3[8] << 2; + num5 += array3[8] * 23u; + num7 = 0u + (array3[6] << 1); + num6 += array3[6] * 13u; + array3[9] = num8; + num8 = array3[11] * 3847227639u; + num6 += array3[3] * 42u; + num7 += array3[6] << 3; + num7 += array3[3] * 38u; + num6 += array3[8] << 1; + array3[11] = array3[5]; + num6 += array3[8] << 4; + array3[5] = num8 * 1879390407u; + num8 = (array3[10] & 3016193462u); + array3[3] = num5; + array3[10] = (array3[10] & 1278773833u); + num5 = array3[14] << 3; + array3[14] = array3[14] >> 29; + array3[14] = (array3[14] | num5); + array3[6] = num6; + array3[5] = (array3[5] ^ array2[5]); + num5 = (array3[2] & 1689524702u); + array3[2] = (array3[2] & 2605442593u); + array3[10] = (array3[10] | (array3[14] & 3016193462u)); + array3[6] = (array3[6] ^ 1826460809u); + array3[11] = (array3[11] ^ ~array3[5]); + num5 *= 1545381913u; + num7 += array3[8] * 15u; + array3[8] = num7; + array3[6] = (array3[6] ^ array2[6]); + array3[8] = (array3[8] ^ array3[7]); + num8 *= 3177808341u; + array3[15] = (array3[15] ^ array3[12]); + array3[9] = (array3[9] ^ array2[9]); + array3[11] = (array3[11] ^ array2[11]); + array3[2] = (array3[2] | (array3[0] & 1689524702u)); + array3[0] = (array3[0] & 2605442593u); + array3[1] = array3[1] - 504000940u; + array3[0] = (array3[0] | num5 * 1340355625u); + array3[2] = (array3[2] ^ array2[2]); + array3[3] = array3[3] - array3[13]; + num6 = array3[9] >> 30; + array3[14] = (array3[14] & 1278773833u); + array3[14] = (array3[14] | num8 * 2270146429u); + array3[10] = (array3[10] ^ array2[10]); + num7 = array3[0] << 24; + array3[9] = array3[9] << 2; + array3[6] = (array3[6] ^ array3[1]); + array3[4] = (array3[4] ^ 2605939339u); + array3[0] = array3[0] >> 8; + num8 = (array3[3] & 1626864053u); + array3[6] = array3[6] - array3[1]; + array3[12] = (array3[12] ^ 1401990150u); + num8 *= 1682859757u; + array3[8] = array3[8] - 393293234u; + array3[4] = (array3[4] ^ 2139869331u); + array3[3] = (array3[3] & 2668103242u); + array3[3] = (array3[3] | (array3[14] & 1626864053u)); + array3[14] = (array3[14] & 2668103242u); + array3[9] = (array3[9] | num6); + array3[14] = (array3[14] | num8 * 2707023589u); + num5 = array3[9] * 1442504087u; + num8 = array3[10] * 3851007073u; + array3[10] = array3[15]; + array3[13] = array3[13] * 2082553177u; + array3[13] = (array3[13] ^ array2[13]); + array3[9] = array3[2]; + array3[2] = num5 * 1511879207u; + array3[0] = (array3[0] | num7); + array3[15] = num8 * 1163915169u; + array3[0] = (array3[0] ^ array3[1]); + array3[13] = (array3[13] ^ array3[7]); + for (int k = 0; k < 16; k++) { - var num9 = array3[k]; - array4[num4++] = (byte) num9; - array4[num4++] = (byte) (num9 >> 8); - array4[num4++] = (byte) (num9 >> 16); - array4[num4++] = (byte) (num9 >> 24); + uint num9 = array3[k]; + array4[num4++] = (byte)num9; + array4[num4++] = (byte)(num9 >> 8); + array4[num4++] = (byte)(num9 >> 16); + array4[num4++] = (byte)(num9 >> 24); array2[k] ^= num9; } num3 += 16; diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index 23d06208c..56b046974 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -28,7 +28,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx public class DeobfuscatorInfo : DeobfuscatorInfoBase { internal const string THE_NAME = "ConfuserEx"; - public const string THE_TYPE = "cx"; + public const string THE_TYPE = "crx"; private const string DEFAULT_REGEX = DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX; public DeobfuscatorInfo() @@ -77,11 +77,16 @@ public override IEnumerable BlocksDeobfuscators list.Add(_controlFlowFixer); if (_deobfuscating && _int32ValueInliner != null) - list.Add(new ConstantsInliner(_sbyteValueInliner, _byteValueInliner, _int16ValueInliner, - _uint16ValueInliner, - _int32ValueInliner, _uint32ValueInliner, _int64ValueInliner, _uint64ValueInliner, - _singleValueInliner, _doubleValueInliner, _arrayValueInliner) - {ExecuteIfNotModified = true}); + { + var constantInliner = new ConstantsInliner(_sbyteValueInliner, _byteValueInliner, + _int16ValueInliner, + _uint16ValueInliner, _int32ValueInliner, _uint32ValueInliner, _int64ValueInliner, + _uint64ValueInliner, _singleValueInliner, _doubleValueInliner, _arrayValueInliner) + { + ExecuteIfNotModified = true + }; + list.Add(constantInliner); + } return list; } } @@ -244,8 +249,8 @@ public override void DeobfuscateEnd() var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); foreach (var instr in moduleCctor.Body.Instructions) - if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef && - toRemoveFromCctor.Contains((MethodDef) instr.Operand)) + if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef + && toRemoveFromCctor.Contains((MethodDef)instr.Operand)) instr.OpCode = OpCodes.Nop; //No more mixed! diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs index af9c74365..264a023bd 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Constants.cs @@ -1,7 +1,7 @@  namespace de4dot.Bea { - public class BeaConstants + public static class BeaConstants { public static int INSTRUCT_LENGTH = 80; diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs index 875cf8c26..afba212ba 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs @@ -1,8 +1,9 @@ -using System.Runtime.InteropServices; +using System.IO; +using System.Runtime.InteropServices; namespace de4dot.Bea { - public class BeaEngine + public static class BeaEngine { [DllImport("BeaEngine")] public static extern int Disasm([In, Out, MarshalAs(UnmanagedType.LPStruct)] Disasm disasm); diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs index a19cf81eb..fbed7c192 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/X86Method.cs @@ -39,7 +39,7 @@ private void ParseInstructions(MethodDef method) var rawInstructions = new List(); while (true) - { + { byte[] bytes = ReadChunk(method, _module); var disasm = new Disasm(); @@ -135,7 +135,6 @@ public int Execute(params int[] @params) return Registers["EAX"]; } - public static Disasm Clone(Disasm disasm) { return new Disasm @@ -152,6 +151,6 @@ public static Disasm Clone(Disasm disasm) SecurityBlock = disasm.SecurityBlock, VirtualAddr = disasm.VirtualAddr }; - } + } } } diff --git a/de4dot.cui/FilesDeobfuscator.cs b/de4dot.cui/FilesDeobfuscator.cs index ab79adde0..d724b055d 100644 --- a/de4dot.cui/FilesDeobfuscator.cs +++ b/de4dot.cui/FilesDeobfuscator.cs @@ -205,11 +205,11 @@ bool Add(IObfuscatedFile file, bool skipUnknownObfuscator, bool isFromPossibleFi allFiles[key] = true; int oldIndentLevel = Logger.Instance.IndentLevel; - try { + //try { file.DeobfuscatorContext = options.DeobfuscatorContext; file.Load(options.CreateDeobfuscators()); - } - catch (NotSupportedException) { + //} + /*catch (NotSupportedException) { return false; // Eg. unsupported architecture } catch (BadImageFormatException) { @@ -226,14 +226,15 @@ bool Add(IObfuscatedFile file, bool skipUnknownObfuscator, bool isFromPossibleFi return false; // Not a .NET file } catch (Exception ex) { + throw; Logger.Instance.Log(false, null, LoggerEvent.Warning, "Could not load file ({0}): {1}", ex.GetType(), file.Filename); return false; } finally { Logger.Instance.IndentLevel = oldIndentLevel; - } + }*/ - var deob = file.Deobfuscator; + var deob = file.Deobfuscator; if (skipUnknownObfuscator && deob.Type == "un") { Logger.v("Skipping unknown obfuscator: {0}", file.Filename); RemoveModule(file.ModuleDefMD); From 06d439a5f52ca5882f68a54b2e1a951d63e0e4ec Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Sat, 19 Aug 2017 17:44:30 +0300 Subject: [PATCH 09/14] Move ConfuserEx warning messages to display during the correct deobfuscation stage --- de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index 56b046974..4428fb361 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -105,8 +105,15 @@ protected override void ScanForObfuscator() { _lzmaFinder = new LzmaFinder(module, DeobfuscatedFile); _lzmaFinder.Find(); + _constantDecrypter = new ConstantsDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); + Logger.w( + "Constants decryption enabled! Please note that the decryption method has to be set manually!"); //TODO: Remove + _resourceDecrypter = new ResourceDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); + Logger.w( + "Resource decryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove + if (_lzmaFinder.FoundLzma) { _constantDecrypter.Find(); @@ -144,9 +151,6 @@ public override void DeobfuscateBegin() { if (_constantDecrypter.Detected) { - Logger.w( - "Constants encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove - _sbyteValueInliner = new SByteValueInliner(); _byteValueInliner = new ByteValueInliner(); _int16ValueInliner = new Int16ValueInliner(); @@ -189,8 +193,6 @@ public override void DeobfuscateBegin() } if (_resourceDecrypter.Detected) { - Logger.w( - "Resource encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove _resourceDecrypter.Fix(); } From f6d73852e1b519ce5101b061c550c458594d0d07 Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Sun, 20 Aug 2017 16:25:25 +0300 Subject: [PATCH 10/14] Implement ConfuserEx generic constants and resource decryption; misc improvements Move BeaEngine.dll to /bin/ Make sure BeaEngine.dll is loaded when the working directory is different Disable file deobfuscation exception handler Don't remove LZMA methods by default Trim version read from ConfuserAttribute Minor refactoring --- de4dot.code/ObfuscatedFile.cs | 9 +- .../ConfuserEx/ConstantDecrypter.cs | 488 ++++-------------- .../deobfuscators/ConfuserEx/Deobfuscator.cs | 12 +- .../ConfuserEx/ResourceDecrypter.cs | 170 +++--- de4dot.code/deobfuscators/ConfuserEx/Utils.cs | 24 +- .../ConfuserEx/x86/Bea/Engine.cs | 12 + 6 files changed, 237 insertions(+), 478 deletions(-) diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 611b6a2ec..484d630a3 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -264,12 +264,13 @@ IDeobfuscator DetectObfuscator2(IEnumerable deobfuscators) { foreach (var deob in deobfuscators) { this.deob = deob; // So we can call deob.CanInlineMethods in deobfuscate() int val; - try { - val = deob.Detect(); - } + //TODO: Re-enable exception handler + //try { + val = deob.Detect(); + /*} catch { val = deob.Type == "un" ? 1 : 0; - } + }*/ Logger.v("{0,3}: {1}", val, deob.TypeLong); if (val > 0 && deob.Type != "un") allDetected.Add(deob); diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs index b13bd363f..be5dc26be 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs @@ -1,12 +1,19 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; using System.Runtime.InteropServices; using System.Text; using de4dot.blocks; using de4dot.blocks.cflow; using de4dot.code.deobfuscators.ConfuserEx.x86; using dnlib.DotNet; -using dnlib.DotNet.Emit; +using dnlib.DotNet.Writer; +using FieldAttributes = dnlib.DotNet.FieldAttributes; +using MethodAttributes = dnlib.DotNet.MethodAttributes; +using OpCodes = dnlib.DotNet.Emit.OpCodes; +using TypeAttributes = dnlib.DotNet.TypeAttributes; namespace de4dot.code.deobfuscators.ConfuserEx { @@ -101,7 +108,7 @@ public ConstantsDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfus _deobfuscator = deobfsucator; } - public bool CanRemoveLzma { get; private set; } = true; + public bool CanRemoveLzma { get; private set; } public TypeDef Type => ArrayType; @@ -130,18 +137,35 @@ public void Find() continue; if (!DotNetUtils.IsMethod(method, "System.Void", "()")) continue; + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); - if (!IsStringDecrypterInit(method)) + + if (!IsStringDecrypterInit(method, out FieldDef aField, out FieldDef dField)) continue; + try + { + _decryptedBytes = DecryptArray(method, aField.InitialValue); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + return; + } + + _arrayField = aField; + _decryptedField = dField; + ArrayType = DotNetUtils.GetType(_module, _arrayField.FieldSig.Type); Method = method; - FindStringDecrypters(moduleCctor.DeclaringType); + Decrypters.AddRange(FindStringDecrypters(moduleCctor.DeclaringType)); + CanRemoveLzma = true; } } - private bool IsStringDecrypterInit(MethodDef method) + private bool IsStringDecrypterInit(MethodDef method, out FieldDef aField, out FieldDef dField) { + aField = null; + dField = null; var instructions = method.Body.Instructions; - if (instructions.Count < 15) return false; @@ -162,7 +186,7 @@ private bool IsStringDecrypterInit(MethodDef method) return false; if (instructions[5].OpCode != OpCodes.Ldtoken) return false; - var aField = instructions[5].Operand as FieldDef; + aField = instructions[5].Operand as FieldDef; if (aField?.InitialValue == null) return false; if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA)) @@ -185,360 +209,56 @@ private bool IsStringDecrypterInit(MethodDef method) return false; if (instructions[l - 2].OpCode != OpCodes.Stsfld) //.byte_0 = .smethod_0(array4); return false; - var dField = instructions[l - 2].Operand as FieldDef; + dField = instructions[l - 2].Operand as FieldDef; if (dField == null) return false; - try - { - DecryptArray(ConvertArray(aField.InitialValue)); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - CanRemoveLzma = false; - return false; - } - _arrayField = aField; - ArrayType = DotNetUtils.GetType(_module, aField.FieldSig.Type); - _decryptedField = dField; - return true; - } - private static T[] ConvertArray(T1[] array) - { - var l = Marshal.SizeOf(typeof(T)); - var l1 = Marshal.SizeOf(typeof(T1)); - var buffer = new T[array.Length * l1 / l]; - Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); - return buffer; + return true; } - private void DecryptArray(uint[] array) //TODO: Automatic detection + private byte[] DecryptArray(MethodDef method, byte[] encryptedArray) { - var num = 1888u; // array size? - uint[] array2 = new uint[16]; - uint num2 = 3153506350u; - for (int i = 0; i < 16; i++) - { - num2 ^= num2 >> 12; - num2 ^= num2 << 25; - num2 ^= num2 >> 27; - array2[i] = num2; - } - int num3 = 0; - int num4 = 0; - uint[] array3 = new uint[16]; - byte[] array4 = new byte[num * 4u]; - while ((long)num3 < (long)((ulong)num)) + ModuleDefUser tempModule = new ModuleDefUser("TempModule"); + + AssemblyDef tempAssembly = new AssemblyDefUser("TempAssembly"); + tempAssembly.Modules.Add(tempModule); + + var tempType = new TypeDefUser("", "TempType", tempModule.CorLibTypes.Object.TypeDefOrRef); + tempType.Attributes = TypeAttributes.Public | TypeAttributes.Class; + MethodDef tempMethod = Utils.Clone(method); + + tempMethod.ReturnType = new SZArraySig(tempModule.CorLibTypes.Byte); + tempMethod.MethodSig.Params.Add(new SZArraySig(tempModule.CorLibTypes.Byte)); + tempMethod.Attributes = MethodAttributes.Public | MethodAttributes.Static; + + for (int i = 0; i < 5; i++) + tempMethod.Body.Instructions.RemoveAt(2); // read encrypted array from argument + tempMethod.Body.Instructions.Insert(2, OpCodes.Ldarg_0.ToInstruction()); + + for (int i = 0; i < 2; i++) + tempMethod.Body.Instructions.RemoveAt(tempMethod.Body.Instructions.Count - + 2); // make return decrypted array + + tempType.Methods.Add(tempMethod); + tempModule.Types.Add(tempType); + + using (MemoryStream memoryStream = new MemoryStream()) { - for (int j = 0; j < 16; j++) - { - array3[j] = array[num3 + j]; - } - uint num5 = array3[1] << 1; - uint num6 = array3[1] * 21u; - array3[2] = (array3[2] ^ array3[10]); - num5 += array3[1] << 2; - uint num7 = array3[1] * 21u; - num6 += array3[0] * 67u; - uint num8 = array3[1] * 13u; - num5 += array3[0] * 14u; - num6 += array3[9] * 157u; - num8 += array3[0] << 2; - num5 += array3[9] * 27u; - array3[13] = array3[13] * 748798011u; - num8 += array3[0] << 5; - num7 += array3[0] * 57u; - num6 += array3[3] * 206u; - num5 += array3[3] * 43u; - array3[1] = num5; - num7 += array3[9] * 133u; - num8 += array3[9] * 77u; - num8 += array3[3] * 110u; - array3[9] = num6; - num6 = (array3[12] & 1056153664u); - array3[12] = (array3[12] & 3238813631u); - array3[0] = num8; - array3[12] = (array3[12] | (array3[7] & 1056153664u)); - array3[7] = (array3[7] & 3238813631u); - num8 = array3[2] << 3; - num5 = array3[8] * 2590225985u; - num6 *= 770570833u; - array3[7] = (array3[7] | num6 * 2945289905u); - array3[8] = array3[4]; - num8 += array3[13] * 50u; - num7 += array3[3] * 181u; - array3[0] = array3[0] * 154310079u; - num6 = (array3[15] & 1073272377u); - array3[10] = array3[10] - 4001279812u; - array3[3] = num7; - array3[15] = (array3[15] & 3221694918u); - num8 += array3[9] * 67u; - num7 = array3[5] << 14; - array3[1] = (array3[1] ^ array2[1]); - array3[15] = (array3[15] | (array3[11] & 1073272377u)); - array3[5] = array3[5] >> 18; - array3[12] = (array3[12] ^ array2[12]); - array3[4] = num5 * 2830588353u; - array3[11] = (array3[11] & 3221694918u); - array3[5] = (array3[5] | num7); - array3[6] = (array3[6] ^ array3[14]); - num7 = array3[2] << 1; - num6 *= 918007135u; - num7 += array3[2]; - array3[11] = (array3[11] | num6 * 3949194911u); - num5 = array3[2] << 2; - num7 += array3[13] * 19u; - num8 += array3[11] << 2; - num6 = array3[2]; - num6 += array3[13] << 1; - num6 += array3[13] << 3; - array3[8] = (array3[8] ^ 4107405834u); - num7 += array3[9] * 25u; - num6 += array3[9] * 14u; - num8 += array3[11] << 7; - num5 += array3[13] * 38u; - num6 += array3[11] * 31u; - array3[3] = (array3[3] ^ array2[3]); - num5 += array3[9] * 56u; - array3[1] = array3[1] - 1508476838u; - array3[3] = (array3[3] ^ 938209744u); - array3[2] = num6; - num7 += array3[11] * 49u; - array3[9] = num8; - num8 = array3[6] << 1; - array3[7] = (array3[7] ^ ~array3[15]); - num8 += array3[6] << 3; - num5 += array3[11] * 125u; - array3[0] = (array3[0] ^ array2[0]); - array3[11] = num5; - num8 += array3[5] << 5; - num6 = array3[6] * 11u; - array3[13] = num7; - num5 = array3[6] * 55u; - num8 += array3[5]; - num7 = array3[6] * 54u; - num8 += array3[3] * 39u; - num8 += array3[4] << 1; - num7 += array3[5] * 175u; - num8 += array3[4] << 5; - num7 += array3[3] * 209u; - num5 += array3[5] * 179u; - num5 += array3[3] * 213u; - num6 += array3[5] * 35u; - num7 += array3[4] * 177u; - num6 += array3[3] * 42u; - array3[13] = (array3[13] ^ array3[9]); - num6 += array3[4] << 1; - num6 += array3[4] << 5; - array3[7] = (array3[7] ^ array2[7]); - array3[6] = num6; - num5 += array3[4] * 181u; - num6 = array3[14] >> 29; - array3[3] = num5; - array3[5] = num8; - num5 = array3[8] << 2; - num8 = array3[8] << 2; - array3[4] = num7; - array3[14] = array3[14] << 3; - array3[12] = (array3[12] ^ 2411275161u); - num8 += array3[8]; - num5 += array3[7] * 7u; - num5 += array3[6] * 19u; - num8 += array3[7] * 21u; - num7 = array3[11] << 24; - array3[14] = (array3[14] | num6); - array3[11] = array3[11] >> 8; - array3[11] = (array3[11] | num7); - num6 = array3[8] * 11u; - num5 += array3[13] * 46u; - num8 += array3[6] * 47u; - num7 = array3[8]; - num7 += array3[7] << 1; - num6 += array3[7] * 25u; - num8 += array3[13] * 109u; - array3[4] = (array3[4] ^ array2[4]); - num6 += array3[6] * 63u; - array3[2] = (array3[2] ^ array3[12]); - num7 += array3[7] << 2; - num7 += array3[6] * 13u; - array3[6] = num5; - num5 = (array3[0] & 247307561u); - num5 *= 2926546863u; - array3[0] = (array3[0] & 4047659734u); - array3[0] = (array3[0] | (array3[11] & 247307561u)); - num7 += array3[13] * 30u; - num6 += array3[13] * 150u; - array3[11] = (array3[11] & 4047659734u); - array3[11] = (array3[11] | num5 * 1929455439u); - array3[7] = num8; - num5 = array3[14] << 20; - array3[14] = array3[14] >> 12; - array3[14] = (array3[14] | num5); - num8 = array3[15] * 19u; - num5 = array3[3] << 8; - array3[3] = array3[3] >> 24; - array3[14] = (array3[14] ^ array2[14]); - array3[3] = (array3[3] | num5); - num8 += array3[5] * 69u; - num5 = array3[15] * 23u; - array3[8] = num7; - array3[13] = num6; - num5 += array3[5] * 86u; - array3[6] = (array3[6] ^ 3317586132u); - array3[8] = (array3[8] ^ array2[8]); - array3[4] = array3[4] - 3314395924u; - num8 += array3[9] << 1; - num6 = array3[15] << 2; - array3[13] = (array3[13] ^ 574204725u); - num8 += array3[9] << 6; - num5 += array3[9] * 82u; - array3[2] = (array3[2] ^ 1681301553u); - num8 += array3[1] * 76u; - num7 = array3[15] * 49u; - num6 += array3[15] << 6; - array3[15] = num8; - num7 += array3[5] * 182u; - num6 += array3[5] * 249u; - num6 += array3[9] * 239u; - num7 += array3[9] * 174u; - num7 += array3[1] * 218u; - array3[9] = num7; - num7 = array3[12] << 1; - num5 += array3[1] * 105u; - array3[5] = num5; - num5 = array3[12] << 1; - num8 = array3[10] >> 2; - num5 += array3[7] * 7u; - array3[10] = array3[10] << 30; - num6 += array3[1] * 285u; - array3[1] = num6; - array3[10] = (array3[10] | num8); - num6 = array3[12] * 28u; - num7 += array3[12] << 4; - num7 += array3[7] * 57u; - num7 += array3[13] * 95u; - num5 += array3[13] << 2; - num8 = array3[4] >> 3; - array3[15] = (array3[15] ^ array2[15]); - array3[4] = array3[4] << 29; - array3[4] = (array3[4] | num8); - num6 += array3[7] * 75u; - num8 = array3[12] << 1; - num6 += array3[13] * 113u; - num5 += array3[13] << 3; - num8 += array3[12]; - num6 += array3[10] << 1; - num8 += array3[7] * 11u; - num7 += array3[10] << 1; - num7 += array3[10]; - array3[11] = (array3[11] ^ array3[1]); - num8 += array3[13] << 2; - array3[7] = num5; - num8 += array3[13] << 4; - num5 = array3[9]; - array3[13] = num7; - array3[12] = num8; - num8 = array3[9]; - num8 += array3[6] << 1; - num8 += array3[6]; - array3[0] = array3[0] - array3[15]; - num6 += array3[10] << 3; - num8 += array3[3] << 3; - num5 += array3[6] << 4; - array3[5] = (array3[5] ^ array3[14]); - array3[10] = num6; - num6 = array3[9] << 1; - num5 += array3[3] * 58u; - num6 += array3[9]; - num8 += array3[3]; - num8 += array3[8] << 2; - num5 += array3[8] * 23u; - num7 = 0u + (array3[6] << 1); - num6 += array3[6] * 13u; - array3[9] = num8; - num8 = array3[11] * 3847227639u; - num6 += array3[3] * 42u; - num7 += array3[6] << 3; - num7 += array3[3] * 38u; - num6 += array3[8] << 1; - array3[11] = array3[5]; - num6 += array3[8] << 4; - array3[5] = num8 * 1879390407u; - num8 = (array3[10] & 3016193462u); - array3[3] = num5; - array3[10] = (array3[10] & 1278773833u); - num5 = array3[14] << 3; - array3[14] = array3[14] >> 29; - array3[14] = (array3[14] | num5); - array3[6] = num6; - array3[5] = (array3[5] ^ array2[5]); - num5 = (array3[2] & 1689524702u); - array3[2] = (array3[2] & 2605442593u); - array3[10] = (array3[10] | (array3[14] & 3016193462u)); - array3[6] = (array3[6] ^ 1826460809u); - array3[11] = (array3[11] ^ ~array3[5]); - num5 *= 1545381913u; - num7 += array3[8] * 15u; - array3[8] = num7; - array3[6] = (array3[6] ^ array2[6]); - array3[8] = (array3[8] ^ array3[7]); - num8 *= 3177808341u; - array3[15] = (array3[15] ^ array3[12]); - array3[9] = (array3[9] ^ array2[9]); - array3[11] = (array3[11] ^ array2[11]); - array3[2] = (array3[2] | (array3[0] & 1689524702u)); - array3[0] = (array3[0] & 2605442593u); - array3[1] = array3[1] - 504000940u; - array3[0] = (array3[0] | num5 * 1340355625u); - array3[2] = (array3[2] ^ array2[2]); - array3[3] = array3[3] - array3[13]; - num6 = array3[9] >> 30; - array3[14] = (array3[14] & 1278773833u); - array3[14] = (array3[14] | num8 * 2270146429u); - array3[10] = (array3[10] ^ array2[10]); - num7 = array3[0] << 24; - array3[9] = array3[9] << 2; - array3[6] = (array3[6] ^ array3[1]); - array3[4] = (array3[4] ^ 2605939339u); - array3[0] = array3[0] >> 8; - num8 = (array3[3] & 1626864053u); - array3[6] = array3[6] - array3[1]; - array3[12] = (array3[12] ^ 1401990150u); - num8 *= 1682859757u; - array3[8] = array3[8] - 393293234u; - array3[4] = (array3[4] ^ 2139869331u); - array3[3] = (array3[3] & 2668103242u); - array3[3] = (array3[3] | (array3[14] & 1626864053u)); - array3[14] = (array3[14] & 2668103242u); - array3[9] = (array3[9] | num6); - array3[14] = (array3[14] | num8 * 2707023589u); - num5 = array3[9] * 1442504087u; - num8 = array3[10] * 3851007073u; - array3[10] = array3[15]; - array3[13] = array3[13] * 2082553177u; - array3[13] = (array3[13] ^ array2[13]); - array3[9] = array3[2]; - array3[2] = num5 * 1511879207u; - array3[0] = (array3[0] | num7); - array3[15] = num8 * 1163915169u; - array3[0] = (array3[0] ^ array3[1]); - array3[13] = (array3[13] ^ array3[7]); - for (int k = 0; k < 16; k++) - { - uint num9 = array3[k]; - array4[num4++] = (byte)num9; - array4[num4++] = (byte)(num9 >> 8); - array4[num4++] = (byte)(num9 >> 16); - array4[num4++] = (byte)(num9 >> 24); - array2[k] ^= num9; - } - num3 += 16; + ModuleWriterOptions moduleWriterOptions = new ModuleWriterOptions(tempModule); + moduleWriterOptions.MetadataOptions = new MetadataOptions(); + + tempModule.Write(memoryStream, moduleWriterOptions); + + Assembly patchedAssembly = Assembly.Load(memoryStream.ToArray()); + var type = patchedAssembly.ManifestModule.GetType("TempType"); + var methods = type.GetMethods(); + MethodInfo patchedMethod = methods.First(m => m.IsPublic && m.IsStatic); + byte[] decryptedBytes = (byte[]) patchedMethod.Invoke(null, new object[]{encryptedArray}); + return Lzma.Decompress(decryptedBytes); } - _decryptedBytes = Lzma.Decompress(array4); } - private void FindStringDecrypters(TypeDef type) + private IEnumerable FindStringDecrypters(TypeDef type) { foreach (var method in type.Methods) { @@ -555,79 +275,85 @@ private void FindStringDecrypters(TypeDef type) continue; if (sig.GenParamCount != 1) continue; + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); - IsStringDecrypter(method); + + if (IsStringDecrypter(method, out MethodDef nativeMethod)) + { + yield return new ConstantDecrypterBase + { + Decrypted = _decryptedBytes, + Method = method, + NativeMethod = nativeMethod + }; + } } } - private void IsStringDecrypter(MethodDef method) + private bool IsStringDecrypter(MethodDef method, out MethodDef nativeMethod) { + nativeMethod = null; var instr = method.Body.Instructions; if (instr.Count < 25) - return; + return false; var i = 0; if (!instr[i++].IsLdarg()) - return; + return false; if (instr[i].OpCode != OpCodes.Call) - return; + return false; - var nativeMethod = instr[i++].Operand as MethodDef; + nativeMethod = instr[i++].Operand as MethodDef; if (nativeMethod == null || !nativeMethod.IsStatic || !nativeMethod.IsNative) - return; + return false; if (!DotNetUtils.IsMethod(nativeMethod, "System.Int32", "(System.Int32)")) - return; + return false; if (!instr[i++].IsStarg()) //uint_0 = (uint_0 * 2857448701u ^ 1196001109u); - return; + return false; if (!instr[i++].IsLdarg()) - return; + return false; if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x1E) - return; + return false; if (instr[i++].OpCode != OpCodes.Shr_Un) - return; + return false; if (!instr[i++].IsStloc()) //uint num = uint_0 >> 30; - return; + return false; i++; //TODO: Implement //if (!instr[10].IsLdloca()) // return; if (instr[i++].OpCode != OpCodes.Initobj) - return; + return false; if (!instr[i++].IsLdarg()) - return; + return false; if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x3FFFFFFF) - return; + return false; if (instr[i++].OpCode != OpCodes.And) - return; + return false; if (!instr[i++].IsStarg()) //uint_0 &= 1073741823u; - return; + return false; if (!instr[i++].IsLdarg()) - return; + return false; if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 2) - return; + return false; if (instr[i++].OpCode != OpCodes.Shl) - return; + return false; if (!instr[i++].IsStarg()) //uint_0 <<= 2; - return; + return false; foreach (var mtd in _strDecryptCalledMethods) if (!DotNetUtils.CallsMethod(method, mtd)) - return; + return false; //TODO: Implement //if (!DotNetUtils.LoadsField(method, decryptedField)) // return; - Decrypters.Add(new ConstantDecrypterBase - { - Decrypted = _decryptedBytes, - Method = method, - NativeMethod = nativeMethod - }); + return true; } private static bool VerifyGenericArg(MethodSpec gim, ElementType etype) @@ -722,4 +448,4 @@ public object DecryptArray(ConstantDecrypterBase info, MethodSpec gim, uint magi return info.DecryptArray(magic1); } } -} \ No newline at end of file +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index 4428fb361..cfaf377a4 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -107,12 +107,7 @@ protected override void ScanForObfuscator() _lzmaFinder.Find(); _constantDecrypter = new ConstantsDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); - Logger.w( - "Constants decryption enabled! Please note that the decryption method has to be set manually!"); //TODO: Remove - _resourceDecrypter = new ResourceDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); - Logger.w( - "Resource decryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove if (_lzmaFinder.FoundLzma) { @@ -141,7 +136,7 @@ private void DetectConfuserExAttribute() if (!value.Contains("ConfuserEx")) continue; _detectedConfuserExAttribute = true; - _version = value.Replace("ConfuserEx", ""); + _version = value.Replace("ConfuserEx", "").Trim(); return; } } @@ -231,7 +226,9 @@ public override void DeobfuscateEnd() } if (!_constantDecrypter.CanRemoveLzma || !_resourceDecrypter.CanRemoveLzma) + { _canRemoveLzma = false; + } if (_lzmaFinder.FoundLzma && _canRemoveLzma) { @@ -252,9 +249,10 @@ public override void DeobfuscateEnd() var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); foreach (var instr in moduleCctor.Body.Instructions) if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef - && toRemoveFromCctor.Contains((MethodDef)instr.Operand)) + && toRemoveFromCctor.Contains((MethodDef) instr.Operand)) instr.OpCode = OpCodes.Nop; + //TODO: Might not always be correct //No more mixed! module.IsILOnly = true; diff --git a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs index f66f57c94..4f7637012 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs @@ -1,9 +1,16 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; using System.Runtime.InteropServices; using de4dot.blocks; using dnlib.DotNet; using dnlib.DotNet.Emit; +using dnlib.DotNet.Writer; +using FieldAttributes = dnlib.DotNet.FieldAttributes; +using MethodAttributes = dnlib.DotNet.MethodAttributes; +using TypeAttributes = dnlib.DotNet.TypeAttributes; namespace de4dot.code.deobfuscators.ConfuserEx { @@ -24,7 +31,7 @@ public ResourceDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfusc _deobfuscator = deobfsucator; } - public bool CanRemoveLzma { get; private set; } = true; + public bool CanRemoveLzma { get; private set; } public TypeDef Type { get; private set; } public MethodDef Method { get; private set; } @@ -49,17 +56,37 @@ public void Find() continue; if (!DotNetUtils.IsMethod(method, "System.Void", "()")) continue; + _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); - if (!IsResDecryptInit(method)) + + if (!IsResDecryptInit(method, out FieldDef aField, out FieldDef asmField, out MethodDef mtd)) continue; - Method = method; + + try + { + _decryptedBytes = DecryptArray(method, aField.InitialValue); + } + catch(Exception e) + { + Console.WriteLine(e.Message); + return; + } + + _arrayField = aField; + Type = DotNetUtils.GetType(_module, aField.FieldSig.Type); + _asmField = asmField; + AssembyResolveMethod = mtd; + Method = method; + CanRemoveLzma = true; } } - private bool IsResDecryptInit(MethodDef method) + private bool IsResDecryptInit(MethodDef method, out FieldDef aField, out FieldDef asField, out MethodDef mtd) { + aField = null; + asField = null; + mtd = null; var instr = method.Body.Instructions; - if (instr.Count < 15) return false; @@ -80,10 +107,8 @@ private bool IsResDecryptInit(MethodDef method) return false; if (instr[5].OpCode != OpCodes.Ldtoken) return false; - var aField = instr[5].Operand as FieldDef; - if (aField == null) - return false; - if (aField.InitialValue == null) + aField = instr[5].Operand as FieldDef; + if (aField?.InitialValue == null) return false; if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA)) return false; @@ -110,7 +135,7 @@ private bool IsResDecryptInit(MethodDef method) return false; if (instr[l - 7].OpCode != OpCodes.Stsfld) //.assembly_0 = Assembly.Load(array4); return false; - var asField = instr[l - 7].Operand as FieldDef; + asField = instr[l - 7].Operand as FieldDef; if (asField == null) return false; @@ -122,97 +147,71 @@ private bool IsResDecryptInit(MethodDef method) return false; if (instr[l - 4].OpCode != OpCodes.Ldftn) return false; - var mtd = instr[l - 4].Operand as MethodDef; + mtd = instr[l - 4].Operand as MethodDef; if (mtd == null) return false; + + if (DotNetUtils.IsMethod(mtd, "", "()")) + return false; + + _deobfuscator.Deobfuscate(mtd, SimpleDeobfuscatorFlags.Force); + if (!IsAssembyResolveMethod(mtd, asField)) return false; + if (instr[l - 3].OpCode != OpCodes.Newobj) return false; - if (instr[l - 2].OpCode != OpCodes.Callvirt - ) //AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(.smethod_1); + if (instr[l - 2].OpCode != OpCodes.Callvirt) + //AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(.smethod_1); return false; - try - { - DecryptArray(ConvertArray(aField.InitialValue)); - } - catch - { - CanRemoveLzma = false; - return false; - } - _arrayField = aField; - Type = DotNetUtils.GetType(_module, aField.FieldSig.Type); - _asmField = asField; - AssembyResolveMethod = mtd; + return true; } - private T[] ConvertArray(T1[] array) + private byte[] DecryptArray(MethodDef method, byte[] encryptedArray) { - var l = Marshal.SizeOf(typeof(T)); - var l1 = Marshal.SizeOf(typeof(T1)); - var buffer = new T[array.Length * l1 / l]; - Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); - return buffer; - } + ModuleDefUser tempModule = new ModuleDefUser("TempModule"); + + AssemblyDef tempAssembly = new AssemblyDefUser("TempAssembly"); + tempAssembly.Modules.Add(tempModule); + + var tempType = new TypeDefUser("", "TempType", tempModule.CorLibTypes.Object.TypeDefOrRef); + tempType.Attributes = TypeAttributes.Public | TypeAttributes.Class; + MethodDef tempMethod = Utils.Clone(method); - private void DecryptArray(uint[] array) //TODO: Automatic detection - { - var num = array.Length; - var array2 = new uint[16]; - var num2 = 825993394u; - for (var i = 0; i < 16; i++) - { - num2 ^= num2 >> 13; - num2 ^= num2 << 25; - num2 ^= num2 >> 27; - array2[i] = num2; - } - var num3 = 0; - var num4 = 0; - var array3 = new uint[16]; - var array4 = new byte[num * 4u]; - while (num3 < (long) (ulong) num) + tempMethod.ReturnType = new SZArraySig(tempModule.CorLibTypes.Byte); + tempMethod.MethodSig.Params.Add(new SZArraySig(tempModule.CorLibTypes.Byte)); + tempMethod.Attributes = MethodAttributes.Public | MethodAttributes.Static; + + for (int i = 0; i < 5; i++) + tempMethod.Body.Instructions.RemoveAt(2); // read encrypted array from argument + tempMethod.Body.Instructions.Insert(2, OpCodes.Ldarg_0.ToInstruction()); + + for (int i = 0; i < 8; i++) + tempMethod.Body.Instructions.RemoveAt(tempMethod.Body.Instructions.Count - + 2); // make return decrypted array + + tempType.Methods.Add(tempMethod); + tempModule.Types.Add(tempType); + + using (MemoryStream memoryStream = new MemoryStream()) { - for (var j = 0; j < 16; j++) - array3[j] = array[num3 + j]; - array3[0] = array3[0] ^ array2[0]; - array3[1] = array3[1] ^ array2[1]; - array3[2] = array3[2] ^ array2[2]; - array3[3] = array3[3] ^ array2[3]; - array3[4] = array3[4] ^ array2[4]; - array3[5] = array3[5] ^ array2[5]; - array3[6] = array3[6] ^ array2[6]; - array3[7] = array3[7] ^ array2[7]; - array3[8] = array3[8] ^ array2[8]; - array3[9] = array3[9] ^ array2[9]; - array3[10] = array3[10] ^ array2[10]; - array3[11] = array3[11] ^ array2[11]; - array3[12] = array3[12] ^ array2[12]; - array3[13] = array3[13] ^ array2[13]; - array3[14] = array3[14] ^ array2[14]; - array3[15] = array3[15] ^ array2[15]; - for (var k = 0; k < 16; k++) - { - var num5 = array3[k]; - array4[num4++] = (byte) num5; - array4[num4++] = (byte) (num5 >> 8); - array4[num4++] = (byte) (num5 >> 16); - array4[num4++] = (byte) (num5 >> 24); - array2[k] ^= num5; - } - num3 += 16; + ModuleWriterOptions moduleWriterOptions = new ModuleWriterOptions(tempModule); + moduleWriterOptions.MetadataOptions = new MetadataOptions(); + + tempModule.Write(memoryStream, moduleWriterOptions); + + Assembly patchedAssembly = Assembly.Load(memoryStream.ToArray()); + var type = patchedAssembly.ManifestModule.GetType("TempType"); + var methods = type.GetMethods(); + MethodInfo patchedMethod = methods.First(m => m.IsPublic && m.IsStatic); + byte[] decryptedBytes = (byte[]) patchedMethod.Invoke(null, new object[]{encryptedArray}); + return Lzma.Decompress(decryptedBytes); } - _decryptedBytes = Lzma.Decompress(array4); } - private bool IsAssembyResolveMethod(MethodDef method, FieldDef field) + private bool IsAssembyResolveMethod(MethodDef method, FieldDef field) { - if (DotNetUtils.IsMethod(method, "", "()")) - return false; - _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); - var instr = method.Body.Instructions; if (instr.Count != 10) return false; @@ -247,6 +246,7 @@ private bool IsAssembyResolveMethod(MethodDef method, FieldDef field) return false; if (instr[9].OpCode != OpCodes.Ret) return false; + return true; } @@ -278,4 +278,4 @@ public void Fix() _module.Resources.Add(resToAdd); } } -} \ No newline at end of file +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/Utils.cs b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs index 543554092..27c3899d1 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Utils.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; using de4dot.blocks; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -143,6 +145,26 @@ public static bool IsDup(this Block block) return false; return true; } + + public static MethodDefUser Clone(MethodDef origin) + { + var ret = new MethodDefUser(origin.Name, origin.MethodSig, origin.ImplAttributes, origin.Attributes); + + foreach (GenericParam genericParam in origin.GenericParameters) + ret.GenericParameters.Add(new GenericParamUser(genericParam.Number, genericParam.Flags, "-")); + + ret.Body = origin.Body; + return ret; + } + + public static T[] ConvertArray(T1[] array) + { + var l = Marshal.SizeOf(typeof(T)); + var l1 = Marshal.SizeOf(typeof(T1)); + var buffer = new T[array.Length * l1 / l]; + Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1); + return buffer; + } } public static class Extensions diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs index afba212ba..8e518df93 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/x86/Bea/Engine.cs @@ -5,6 +5,18 @@ namespace de4dot.Bea { public static class BeaEngine { + // 'de4dot\bin\de4dot.blocks.dll' -> 'de4dot\bin\' + private static string _executingPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + + static BeaEngine() + { + //TODO: Better handle native DLL discovery + SetDllDirectory(_executingPath); + } + + [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)] + private static extern bool SetDllDirectory(string lpPathName); + [DllImport("BeaEngine")] public static extern int Disasm([In, Out, MarshalAs(UnmanagedType.LPStruct)] Disasm disasm); From da26c5c32bc604ed9292c959e7323c5c559baaa4 Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Tue, 26 Sep 2017 01:30:57 +0100 Subject: [PATCH 11/14] Add support for normal predicate control flow; Add detection weight to ConfusedBy attribute --- de4dot.blocks/Block.cs | 21 +++- de4dot.blocks/SwitchData.cs | 20 +++ .../ConfuserEx/ControlFlowFixer.cs | 114 ++++++++++++------ .../deobfuscators/ConfuserEx/Deobfuscator.cs | 2 +- .../ConfuserEx/NativeSwitchData.cs | 45 +++++++ .../ConfuserEx/NormalSwitchData.cs | 40 ++++++ de4dot.code/deobfuscators/ConfuserEx/Utils.cs | 55 --------- 7 files changed, 202 insertions(+), 95 deletions(-) create mode 100644 de4dot.blocks/SwitchData.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/NativeSwitchData.cs create mode 100644 de4dot.code/deobfuscators/ConfuserEx/NormalSwitchData.cs diff --git a/de4dot.blocks/Block.cs b/de4dot.blocks/Block.cs index a2f58e82e..2384dbdb8 100644 --- a/de4dot.blocks/Block.cs +++ b/de4dot.blocks/Block.cs @@ -22,8 +22,25 @@ You should have received a copy of the GNU General Public License using dnlib.DotNet.Emit; namespace de4dot.blocks { - public class Block : BaseBlock { - List instructions = new List(); + public enum BlockType + { + Normal, + Switch, + SwitchCase + } + + public class Block : BaseBlock + { + public Block() + { + SwitchData = new SwitchData(this); + } + + public BlockType BlockType = BlockType.Normal; + public SwitchData SwitchData; + public bool Processed = false; + + List instructions = new List(); // List of all explicit (non-fall-through) targets. It's just one if it's a normal // branch, but if it's a switch, it could be many targets. diff --git a/de4dot.blocks/SwitchData.cs b/de4dot.blocks/SwitchData.cs new file mode 100644 index 000000000..1e70f61f2 --- /dev/null +++ b/de4dot.blocks/SwitchData.cs @@ -0,0 +1,20 @@ +namespace de4dot.blocks +{ + public class SwitchData + { + protected readonly Block _block; + + public int? Key; + public bool IsKeyHardCoded; + + public SwitchData(Block block) + { + _block = block; + } + + public virtual bool Initialize() + { + return false; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs index 62b7b456c..8dcc62656 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs @@ -16,30 +16,49 @@ internal class ControlFlowFixer : IBlocksDeobfuscator private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); - private Blocks _blocks; - private X86Method _nativeMethod; + private Blocks _blocks; private Local _switchKey; - private int? CalculateKey() + private int? CalculateKey(SwitchData switchData) { var popValue = _instructionEmulator.Peek(); - if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid()) return null; _instructionEmulator.Pop(); - int result = _nativeMethod.Execute(((Int32Value)popValue).Value); - return result; + int num = ((Int32Value)popValue).Value; + + if (switchData is NativeSwitchData) + { + var nativeSwitchData = (NativeSwitchData)switchData; + var nativeMethod = new X86Method(nativeSwitchData.NativeMethodDef, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null + return nativeMethod.Execute(num); + } + if (switchData is NormalSwitchData) + { + var normalSwitchData = (NormalSwitchData)switchData; + return num ^ normalSwitchData.Key.Value; + } + return null; } - private int CalculateSwitchCaseIndex(Block block, int nativeKey) + private int? CalculateSwitchCaseIndex(Block block, SwitchData switchData, int key) { - _instructionEmulator.Push(new Int32Value(nativeKey)); - _instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1); + if (switchData is NativeSwitchData) + { + _instructionEmulator.Push(new Int32Value(key)); + _instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1); - var popValue = _instructionEmulator.Peek(); - _instructionEmulator.Pop(); - return ((Int32Value)popValue).Value; + var popValue = _instructionEmulator.Peek(); + _instructionEmulator.Pop(); + return ((Int32Value)popValue).Value; + } + if (switchData is NormalSwitchData) + { + var normalSwitchData = (NormalSwitchData)switchData; + return key % normalSwitchData.DivisionKey; + } + return null; } private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch @@ -47,15 +66,17 @@ private int CalculateSwitchCaseIndex(Block block, int nativeKey) var targets = switchBlock.Targets; _instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value)); - int? key = CalculateKey(); + int? key = CalculateKey(switchBlock.SwitchData); if (!key.HasValue) throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); - int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + int? switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, switchBlock.SwitchData, key.Value); + if (!switchCaseIndex.HasValue) + throw new Exception("CRITICAL ERROR: SWITCH CASE HAS NO VALUE"); if (targets.Count < switchCaseIndex) throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); - var targetBlock = targets[switchCaseIndex]; + var targetBlock = targets[switchCaseIndex.Value]; targetBlock.SwitchData.Key = key; switchBlock.Instructions.Clear(); @@ -70,15 +91,17 @@ private void ProcessBlock(List switchCaseBlocks, Block block, Block switc if (_instructionEmulator.Peek().IsUnknown()) throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); - int? key = CalculateKey(); + int? key = CalculateKey(switchBlock.SwitchData); if (!key.HasValue) throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); - int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + int? switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, switchBlock.SwitchData, key.Value); + if (!switchCaseIndex.HasValue) + throw new Exception("CRITICAL ERROR: SWITCH CASE HAS NO VALUE"); if (targets.Count < switchCaseIndex) throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); - var targetBlock = targets[switchCaseIndex]; + var targetBlock = targets[switchCaseIndex.Value]; targetBlock.SwitchData.Key = key; block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them @@ -105,21 +128,23 @@ private void ProcessTernaryBlock(List switchCaseBlocks, Block ternaryBloc if (_instructionEmulator.Peek().IsUnknown()) throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN"); - int? key = CalculateKey(); + int? key = CalculateKey(switchBlock.SwitchData); if (!key.HasValue) throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE"); - int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value); + int? switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, switchBlock.SwitchData, key.Value); + if (!switchCaseIndex.HasValue) + throw new Exception("CRITICAL ERROR: SWITCH CASE HAS NO VALUE"); if (targets.Count < switchCaseIndex) throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE"); - var targetBlock = targets[switchCaseIndex]; + var targetBlock = targets[switchCaseIndex.Value]; targetBlock.SwitchData.Key = key; sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction()); - sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]); + sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex.Value]); - ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value); + ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex.Value], key.Value); // the second source block now becomes the first one } @@ -142,12 +167,6 @@ public bool Deobfuscate(List methodBlocks) foreach (Block switchBlock in switchBlocks) { - if (!switchBlock.SwitchData.IsConfuserExSwitch()) - { - Console.WriteLine("Unsupported switch block obfuscation!"); - continue; - } - if (switchBlock.SwitchData.IsKeyHardCoded) { ProcessHardcodedSwitch(switchBlock); @@ -221,19 +240,40 @@ private bool DeobfuscateSwitchBlock(List methodBlocks, Block switchBlock) } - public bool IsSwitchBlock(Block block) + public bool IsConfuserExSwitchBlock(Block block) { if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0) return false; - if (!block.SwitchData.IsNative()) + + var instructions = block.Instructions; + var lastIndex = instructions.Count - 1; + + if (instructions.Count < 4) + return false; + if (!instructions[lastIndex - 3].IsStloc()) + return false; + if (!instructions[lastIndex - 2].IsLdcI4()) + return false; + if (instructions[lastIndex - 1].OpCode != OpCodes.Rem_Un) return false; - MethodDef nativeMethod = block.SwitchData.GetNativeMethod(); - _nativeMethod = new X86Method(nativeMethod, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null - if (!NativeMethods.Contains(nativeMethod)) - NativeMethods.Add(nativeMethod); + var nativeSwitchData = new NativeSwitchData(block); + if (nativeSwitchData.Initialize()) + { + block.SwitchData = nativeSwitchData; + if (!NativeMethods.Contains(nativeSwitchData.NativeMethodDef)) // add for remove + NativeMethods.Add(nativeSwitchData.NativeMethodDef); + return true; + } - return true; + var normalSwitchData = new NormalSwitchData(block); + if (normalSwitchData.Initialize()) + { + block.SwitchData = normalSwitchData; + return true; + } + + return false; } public List GetSwitchBlocks(List blocks) // get the blocks which contain the switch statement @@ -241,7 +281,7 @@ public bool IsSwitchBlock(Block block) List switchBlocks = new List(); foreach (Block block in blocks) - if (IsSwitchBlock(block)) + if (IsConfuserExSwitchBlock(block)) switchBlocks.Add(block); return switchBlocks; diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index cfaf377a4..a0e11c5b4 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -94,7 +94,7 @@ public override IEnumerable BlocksDeobfuscators protected override int DetectInternal() { var val = 0; - if (_detectedConfuserExAttribute) val += 0; + if (_detectedConfuserExAttribute) val += 2; if (_lzmaFinder.FoundLzma) val += 10; if (_constantDecrypter.Detected) val += 10; if (_resourceDecrypter.Detected) val += 10; diff --git a/de4dot.code/deobfuscators/ConfuserEx/NativeSwitchData.cs b/de4dot.code/deobfuscators/ConfuserEx/NativeSwitchData.cs new file mode 100644 index 000000000..f4568b0ee --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/NativeSwitchData.cs @@ -0,0 +1,45 @@ +using de4dot.blocks; +using de4dot.code.deobfuscators.ConfuserEx.x86; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + public class NativeSwitchData : SwitchData + { + public NativeSwitchData(Block switchBlock) : base(switchBlock) + { + } + + public MethodDef NativeMethodDef; + + public override bool Initialize() + { + var instr = _block.Instructions; + if (instr.Count <= 4) + return false; + + if (instr[0].IsLdcI4() && instr[1].OpCode == OpCodes.Call) + { + IsKeyHardCoded = true; + Key = instr[0].GetLdcI4Value(); + } + + if (!IsKeyHardCoded && instr[0].OpCode != OpCodes.Call) + return false; + + var nativeMethodDef = _block.Instructions[IsKeyHardCoded ? 1 : 0].Operand as MethodDef; + + if (nativeMethodDef == null || !nativeMethodDef.IsStatic || !nativeMethodDef.IsNative) + return false; + if (!DotNetUtils.IsMethod(nativeMethodDef, "System.Int32", "(System.Int32)")) + return false; + for (var i = IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++) + if (!instr[i].IsValidInstr()) + return false; + + NativeMethodDef = nativeMethodDef; + return true; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/NormalSwitchData.cs b/de4dot.code/deobfuscators/ConfuserEx/NormalSwitchData.cs new file mode 100644 index 000000000..f59d8821f --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/NormalSwitchData.cs @@ -0,0 +1,40 @@ +using de4dot.blocks; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + public class NormalSwitchData : SwitchData + { + public readonly Block Block; + public NormalSwitchData(Block switchBlock) : base(switchBlock) + { + Block = switchBlock; + } + + public int DivisionKey; + + public override bool Initialize() + { + var instr = _block.Instructions; + if (instr.Count != 7) + return false; + + if (!instr[0].IsLdcI4()) + return false; + if (instr[1].OpCode != OpCodes.Xor) + return false; + if (instr[2].OpCode != OpCodes.Dup) + return false; + if (!instr[3].IsStloc()) + return false; + if (!instr[4].IsLdcI4()) + return false; + if (instr[5].OpCode != OpCodes.Rem_Un) + return false; + + Key = instr[0].GetLdcI4Value(); + DivisionKey = instr[4].GetLdcI4Value(); + return true; + } + } +} diff --git a/de4dot.code/deobfuscators/ConfuserEx/Utils.cs b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs index 27c3899d1..9d6c21ca8 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Utils.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Utils.cs @@ -169,61 +169,6 @@ public static T[] ConvertArray(T1[] array) public static class Extensions { - public static bool IsConfuserExSwitch(this SwitchData switchData) - { - var instructions = switchData.Block.Instructions; - var lastIndex = instructions.Count - 1; - - if (instructions.Count < 4) - return false; - if (!instructions[lastIndex - 3].IsStloc()) - return false; - if (!instructions[lastIndex - 2].IsLdcI4()) - return false; - if (instructions[lastIndex - 1].OpCode != OpCodes.Rem_Un) - return false; - - return true; - } - - public static bool IsNative(this SwitchData switchData) - { - var block = switchData.Block; - - var instr = block.Instructions; - if (instr.Count <= 4) - return false; - - if (instr[0].IsLdcI4() && instr[1].OpCode == OpCodes.Call) - { - switchData.IsKeyHardCoded = true; - block.SwitchData.Key = block.FirstInstr.GetLdcI4Value(); - } - - if (!switchData.IsKeyHardCoded && instr[0].OpCode != OpCodes.Call) - return false; - - var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef; - - if (method == null || !method.IsStatic || !method.IsNative) - return false; - if (!DotNetUtils.IsMethod(method, "System.Int32", "(System.Int32)")) - return false; - for (var i = switchData.IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++) - if (!instr[i].IsValidInstr()) - return false; - - return true; - } - - public static MethodDef GetNativeMethod(this SwitchData switchData) - { - var block = switchData.Block; - - var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef; - return method; - } - public static bool IsTernaryPredicate(this Block ternaryPredicateBlock) { if (!ternaryPredicateBlock.LastInstr.IsConditionalBranch()) From b3f206daf0e54f70820f64b273c0a7e3b526cdd3 Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Tue, 26 Sep 2017 01:51:53 +0100 Subject: [PATCH 12/14] Add support for normal mode string decryption --- .../ConfuserEx/ConstantDecrypter.cs | 106 ++++++++++++++++-- 1 file changed, 99 insertions(+), 7 deletions(-) diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs index be5dc26be..a47c1972f 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs @@ -23,12 +23,18 @@ public class ConstantDecrypterBase private X86Method _nativeMethod; public MethodDef Method { get; set; } - public MethodDef NativeMethod { get; set; } public byte[] Decrypted { get; set; } public uint Magic1 { get; set; } public uint Magic2 { get; set; } public bool CanRemove { get; set; } = true; + // native mode + public MethodDef NativeMethod { get; internal set; } + + // normal mode + public uint Num1 { get; internal set; } + public uint Num2 { get; internal set; } + private int? CalculateKey() { var popValue = _instructionEmulator.Peek(); @@ -43,11 +49,20 @@ public class ConstantDecrypterBase private uint CalculateMagic(uint index) { - _instructionEmulator.Push(new Int32Value((int) index)); - _nativeMethod = new X86Method(NativeMethod, Method.Module as ModuleDefMD); //TODO: Possible null - var key = CalculateKey(); + uint uint_0; + if (NativeMethod != null) + { + _instructionEmulator.Push(new Int32Value((int)index)); + _nativeMethod = new X86Method(NativeMethod, Method.Module as ModuleDefMD); //TODO: Possible null + var key = CalculateKey(); + + uint_0 = (uint)key.Value; + } + else + { + uint_0 = index * Num1 ^ Num2; + } - var uint_0 = (uint) key.Value; uint_0 &= 0x3fffffff; uint_0 <<= 2; return uint_0; @@ -278,7 +293,7 @@ private IEnumerable FindStringDecrypters(TypeDef type) _deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force); - if (IsStringDecrypter(method, out MethodDef nativeMethod)) + if (IsNativeStringDecrypter(method, out MethodDef nativeMethod)) { yield return new ConstantDecrypterBase { @@ -287,10 +302,87 @@ private IEnumerable FindStringDecrypters(TypeDef type) NativeMethod = nativeMethod }; } + if (IsNormalStringDecrypter(method, out int num1, out int num2)) + { + yield return new ConstantDecrypterBase + { + Decrypted = _decryptedBytes, + Method = method, + Num1 = (uint)num1, + Num2 = (uint)num2 + }; + } } } - private bool IsStringDecrypter(MethodDef method, out MethodDef nativeMethod) + private bool IsNormalStringDecrypter(MethodDef method, out int num1, out int num2) + { + num1 = 0; + num2 = 0; + var instr = method.Body.Instructions; + if (instr.Count < 25) + return false; + + var i = 0; + + if (!instr[i++].IsLdarg()) + return false; + if (!instr[i].IsLdcI4()) + return false; + num1 = (int)instr[i++].Operand; + if (instr[i++].OpCode != OpCodes.Mul) + return false; + if (!instr[i].IsLdcI4()) + return false; + num2 = (int)instr[i++].Operand; + if (instr[i++].OpCode != OpCodes.Xor) + return false; + + if (!instr[i++].IsStarg()) //uint_0 = (uint_0 * 2857448701u ^ 1196001109u); + return false; + + if (!instr[i++].IsLdarg()) + return false; + if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x1E) + return false; + if (instr[i++].OpCode != OpCodes.Shr_Un) + return false; + if (!instr[i++].IsStloc()) //uint num = uint_0 >> 30; + return false; + i++; + //TODO: Implement + //if (!instr[10].IsLdloca()) + // return; + if (instr[i++].OpCode != OpCodes.Initobj) + return false; + if (!instr[i++].IsLdarg()) + return false; + if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 0x3FFFFFFF) + return false; + if (instr[i++].OpCode != OpCodes.And) + return false; + if (!instr[i++].IsStarg()) //uint_0 &= 1073741823u; + return false; + + if (!instr[i++].IsLdarg()) + return false; + if (!instr[i].IsLdcI4() || instr[i++].GetLdcI4Value() != 2) + return false; + if (instr[i++].OpCode != OpCodes.Shl) + return false; + if (!instr[i++].IsStarg()) //uint_0 <<= 2; + return false; + + foreach (var mtd in _strDecryptCalledMethods) + if (!DotNetUtils.CallsMethod(method, mtd)) + return false; + //TODO: Implement + //if (!DotNetUtils.LoadsField(method, decryptedField)) + // return; + return true; + } + + private bool IsNativeStringDecrypter(MethodDef method, out MethodDef nativeMethod) { nativeMethod = null; var instr = method.Body.Instructions; From bf2f597f687b63d5662a42aff67ad3b0d6b00c38 Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Sat, 24 Mar 2018 16:21:12 +0000 Subject: [PATCH 13/14] Update README.md --- README-CEx.md | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 README-CEx.md diff --git a/README-CEx.md b/README-CEx.md new file mode 100644 index 000000000..9c2b017a4 --- /dev/null +++ b/README-CEx.md @@ -0,0 +1,137 @@ +## de4dot CEx +A de4dot fork with full support for vanilla ConfuserEx + +## Features +* Supports x86 (native) mode +* Supports normal mode +* Decrypts and inlines constants +* Decrypts resources +* Fixes control flow +* Fixes proxy calls +* Deobfuscated assemblies are runnable + +## Notes +* You have to unpack the obfuscated assembly **before** running this deobfuscator. The easiest way is to dump the module/s just after the methods have been decrypted. +* This deobfuscator uses method invocation for constant decryption, therefore you always **risk** running malware if it's present in the obfuscated assembly. Be cautious and use a VM/Sandboxie! + +### [Original README](./README.md) +--- + +## Samples + +### Before (obfuscated symbols shortened): +```csharp +ublic byte[] ShiftAddress(uint address) +{ + byte[] array = new byte[4]; + for (;;) + { + IL_07: + int num = -2174478396; + for (;;) + { + uint num2; + switch ((num2 = (uint).a(num)) % 7u) + { + case 0u: + goto IL_07; + case 1u: + { + int num3 = 0; + num = (int)(num2 * 81144519u ^ 2359132411u); + continue; + } + case 2u: + num = (int)(num2 * 2975731004u ^ 34171348176); + continue; + case 3u: + { + int num3; + num3++; + num = (int)(num2 * 2174567110u ^ 244457623u); + continue; + } + case 5u: + { + int num3; + num = ((num3 >= 4) ? 631278122 : 1299552879); + continue; + } + case 6u: + { + int num3; + array[num3] = (byte)(address >> num3 * 8 & 255u); + num = 556578930; + continue; + } + } + return array; + } + } + return array; +} +``` + +### After: +```csharp +public byte[] ShiftAddress(uint address) +{ + byte[] array = new byte[4]; + for (int i = 0; i < 4; i++) + { + array[i] = (byte)(address >> i * 8 & 255u); + } + return array; +} +``` + +### Before (obfuscated symbols shortened): +```csharp +public bool WriteBytes(uint address, List buffer) +{ + byte[] array = buffer.ToArray(); + IntPtr intPtr; + uint num = Memory.a(this.Handle, b((long)((ulong)address)), array, (uint)array.Length, out intPtr); + for (;;) + { + IL_25: + int num2 = 482469350; + for (;;) + { + uint num3; + switch ((num3 = (uint).c(num2)) % 5u) + { + case 0u: + this.d.Account.Log.WriteLine(.e(3167610260u)); + num2 = (int)(num3 * 3588940066u ^ 1074051690u); + continue; + case 2u: + return false; + case 3u: + goto IL_25; + case 4u: + num2 = (int)(((num != 0u) ? 4496537787u : 434512514u) ^ num3 * 589449693u); + continue; + } + goto Block_1; + } + } + Block_1: + return true; +} +``` + +### After: +```csharp +public bool WriteBytes(uint address, List buffer) +{ + byte[] array = buffer.ToArray(); + IntPtr intPtr; + if (Memory.WriteProcessMemory(this.Handle, (IntPtr)((long)((ulong)address)), array, (uint)array.Length, out intPtr) == 0u) + { + this.Owner.Console.Log.WriteLine("WriteBytes failed: WriteProcessMemory failed"); + return false; + } + return true; +} +``` From 21e4b494558cd9be80f506d007f7e463cff3227d Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Fri, 3 May 2019 01:17:30 +0100 Subject: [PATCH 14/14] Check if cctor exists before using it --- de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index a0e11c5b4..0c34ddbd7 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -247,10 +247,11 @@ public override void DeobfuscateEnd() AddMethodsToBeRemoved(_controlFlowFixer.NativeMethods, "Control flow native methods"); var moduleCctor = DotNetUtils.GetModuleTypeCctor(module); - foreach (var instr in moduleCctor.Body.Instructions) - if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef - && toRemoveFromCctor.Contains((MethodDef) instr.Operand)) - instr.OpCode = OpCodes.Nop; + if (moduleCctor != null) + foreach (var instr in moduleCctor.Body.Instructions) + if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef + && toRemoveFromCctor.Contains((MethodDef) instr.Operand)) + instr.OpCode = OpCodes.Nop; //TODO: Might not always be correct //No more mixed!