From 245d875d5ff1449b7e72859c68d8d934a41e9fac Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 11 Dec 2012 00:23:16 +0100 Subject: [PATCH] Support Eazfuscator.NET 3.5 string encrypter --- de4dot.code/de4dot.code.csproj | 1 + .../Eazfuscator_NET/Deobfuscator.cs | 1 + .../deobfuscators/Eazfuscator_NET/Dynocode.cs | 286 ++++++++++++++++++ .../Eazfuscator_NET/StringDecrypter.cs | 77 +++++ 4 files changed, 365 insertions(+) create mode 100644 de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 72a87098..a44e993b 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -153,6 +153,7 @@ + diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs index e95669fc..9de3dd16 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs @@ -156,6 +156,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { if (CanRemoveStringDecrypterType) { addTypesToBeRemoved(stringDecrypter.Types, "String decrypter type"); addTypeToBeRemoved(decrypterType.Type, "Decrypter type"); + addTypesToBeRemoved(stringDecrypter.DynocodeTypes, "Dynocode type"); addResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings"); } addTypeToBeRemoved(assemblyResolver.Type, "Assembly resolver type"); diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs new file mode 100644 index 00000000..2454bfa5 --- /dev/null +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs @@ -0,0 +1,286 @@ +/* + Copyright (C) 2011-2012 de4dot@gmail.com + + This file is part of 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 System.Collections.Generic; +using dot10.DotNet; +using dot10.DotNet.Emit; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Eazfuscator_NET { + interface IDynocodeGenerator { + IEnumerable getValues(int input); + } + + // Something added in EF 3.5 which they call Dynocode. The string decrypter can now + // call some iterator classes that will return some integers that it will use to + // XOR some key. + class Dynocode { + ISimpleDeobfuscator simpleDeobfuscator; + Dictionary typeToDCGen = new Dictionary(); + + class DCGen1 : IDynocodeGenerator { + public int magic1; + public int magic2; + public int magic3; + public int magic4; + public int magic5; + public int magic6; + public int magic7; + + public IEnumerable getValues(int input) { + yield return magic1; + yield return magic2; + yield return input ^ magic3; + yield return magic4; + yield return magic5; + yield return magic6; + yield return input ^ magic7; + } + } + + class DCGen2 : IDynocodeGenerator { + public int magic1; + + public IEnumerable getValues(int input) { + int x = 0; + int y = 1; + while (true) { + yield return y; + if (--input == 0) + break; + int tmp = y; + y = (x + y + input) ^ magic1; + x = tmp; + } + } + } + + class DCGen3 : IDynocodeGenerator { + public int magic1; + public int magic2; + public DCGen2 dc2; + + public IEnumerable getValues(int input) { + int i = 7; + foreach (var val in dc2.getValues(input)) { + int x = val ^ input; + if ((x % 4) == 0) + x ^= magic1; + if ((x % 16) == 0) + x ^= magic2; + yield return x; + if (--i == 0) + break; + } + } + } + + public IEnumerable Types { + get { return typeToDCGen.Keys; } + } + + public Dynocode(ISimpleDeobfuscator simpleDeobfuscator) { + this.simpleDeobfuscator = simpleDeobfuscator; + } + + public IDynocodeGenerator getDynocodeGenerator(TypeDef type) { + if (type == null) + return null; + var dt = type.DeclaringType; + if (dt == null) + return null; + IDynocodeGenerator dcGen; + if (typeToDCGen.TryGetValue(type, out dcGen)) + return dcGen; + + if (dt.NestedTypes.Count == 1) + dcGen = getDCGen1(type); + else if (dt.NestedTypes.Count == 2) + dcGen = getDCGen3(type); + + typeToDCGen[type] = dcGen; + + return dcGen; + } + + DCGen1 getDCGen1(TypeDef type) { + var method = getMoveNext(type); + if (method == null) + return null; + simpleDeobfuscator.deobfuscate(method); + var swLabels = getSwitchLabels(method); + if (swLabels == null || swLabels.Count < 7) + return null; + + var dcGen = new DCGen1(); + if (!getMagicDC1(method, swLabels[0], out dcGen.magic1)) + return null; + if (!getMagicDC1(method, swLabels[1], out dcGen.magic2)) + return null; + if (!getMagicXorDC1(method, swLabels[2], out dcGen.magic3)) + return null; + if (!getMagicDC1(method, swLabels[3], out dcGen.magic4)) + return null; + if (!getMagicDC1(method, swLabels[4], out dcGen.magic5)) + return null; + if (!getMagicDC1(method, swLabels[5], out dcGen.magic6)) + return null; + if (!getMagicXorDC1(method, swLabels[6], out dcGen.magic7)) + return null; + + return dcGen; + } + + static IList getSwitchLabels(MethodDef method) { + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Switch) + continue; + return instr.Operand as IList; + } + return null; + } + + static bool getMagicDC1(MethodDef method, Instruction target, out int magic) { + magic = 0; + var instrs = method.Body.Instructions; + int index = instrs.IndexOf(target); + if (index < 0) + return false; + + for (int i = index; i < instrs.Count - 3; i++) { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + return false; + if (instr.OpCode.Code != Code.Stfld) + continue; + if (instrs[i + 1].OpCode.Code != Code.Ldarg_0) + continue; + var ldci4 = instrs[i + 2]; + if (!ldci4.IsLdcI4()) + continue; + if (instrs[i + 3].OpCode.Code != Code.Stfld) + continue; + + magic = ldci4.GetLdcI4Value(); + return true; + } + + return false; + } + + static bool getMagicXorDC1(MethodDef method, Instruction target, out int magic) { + magic = 0; + var instrs = method.Body.Instructions; + int index = instrs.IndexOf(target); + if (index < 0) + return false; + + for (int i = index; i < instrs.Count - 2; i++) { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + return false; + if (!instr.IsLdcI4()) + continue; + if (instrs[i + 1].OpCode.Code != Code.Xor) + continue; + if (instrs[i + 2].OpCode.Code != Code.Stfld) + continue; + + magic = instr.GetLdcI4Value(); + return true; + } + + return false; + } + + DCGen3 getDCGen3(TypeDef type) { + var method = getMoveNext(type); + if (method == null) + return null; + simpleDeobfuscator.deobfuscate(method); + + var dcGen = new DCGen3(); + int index = 0; + if (!getMagicDC3(method, ref index, out dcGen.magic1)) + return null; + if (!getMagicDC3(method, ref index, out dcGen.magic2)) + return null; + + var dt = type.DeclaringType; + dcGen.dc2 = getDCGen2(dt.NestedTypes[0] == type ? dt.NestedTypes[1] : dt.NestedTypes[0]); + + return dcGen; + } + + static bool getMagicDC3(MethodDef method, ref int index, out int magic) { + var instrs = method.Body.Instructions; + for (int i = index; i < instrs.Count - 2; i++) { + var ldci4 = instrs[i]; + if (!ldci4.IsLdcI4()) + continue; + if (instrs[i + 1].OpCode.Code != Code.Xor) + continue; + if (instrs[i + 2].OpCode.Code != Code.Stfld) + continue; + + index = i + 3; + magic = ldci4.GetLdcI4Value(); + return true; + } + + magic = 0; + return false; + } + + DCGen2 getDCGen2(TypeDef type) { + var method = getMoveNext(type); + if (method == null) + return null; + simpleDeobfuscator.deobfuscate(method); + + var dcGen = new DCGen2(); + int index = 0; + if (!getMagicDC3(method, ref index, out dcGen.magic1)) + return null; + + return dcGen; + } + + static MethodDef getMoveNext(TypeDef type) { + foreach (var m in type.Methods) { + if (!m.IsVirtual) + continue; + foreach (var mo in m.Overrides) { + if (mo.MethodDeclaration.FullName == "System.Boolean System.Collections.IEnumerator::MoveNext()") + return m; + } + } + foreach (var m in type.Methods) { + if (!m.IsVirtual) + continue; + if (m.Name != "MoveNext") + continue; + if (!DotNetUtils.isMethod(m, "System.Boolean", "()")) + continue; + return m; + } + return null; + } + } +} diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs index c1c948e1..fe07d7ea 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs @@ -47,6 +47,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { EfConstantsReader stringMethodConsts; bool isV32OrLater; int? validStringDecrypterValue; + Dynocode dynocode; class StreamHelperType { public TypeDef type; @@ -99,6 +100,10 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } } + public IEnumerable DynocodeTypes { + get { return dynocode.Types; } + } + public MethodDef Method { get { return stringMethod; } } @@ -223,6 +228,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } bool findConstants(ISimpleDeobfuscator simpleDeobfuscator) { + dynocode = new Dynocode(simpleDeobfuscator); simpleDeobfuscator.deobfuscate(stringMethod); stringMethodConsts = new EfConstantsReader(stringMethod); @@ -630,6 +636,11 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { emu.push(new Int64Value((long)decrypterType.getMagic())); break; + case Code.Newobj: + if (!emulateDynocode(emu, ref i)) + goto default; + break; + default: if (instr.OpCode.FlowControl != FlowControl.Next) goto done; @@ -649,6 +660,72 @@ done: ; return false; } + bool emulateDynocode(InstructionEmulator emu, ref int index) { + var instrs = stringMethod.Body.Instructions; + var instr = instrs[index]; + + var ctor = instr.Operand as MethodDef; + if (ctor == null || ctor.MethodSig.GetParamCount() != 1 || ctor.MethodSig.Params[0].ElementType != ElementType.I4) + return false; + + if (index + 4 >= instrs.Count) + return false; + var ldloc = instrs[index + 3]; + if (!ldloc.IsLdloc() || instrs[index + 4].OpCode.Code != Code.Stfld) + return false; + + var initValue = emu.getLocal(ldloc.GetLocal(stringMethod.Body.Variables)) as Int32Value; + if (initValue == null || !initValue.allBitsValid()) + return false; + + int leaveIndex = findLeave(instrs, index); + if (leaveIndex < 0) + return false; + var afterLoop = instrs[leaveIndex].Operand as Instruction; + if (afterLoop == null) + return false; + int newIndex = instrs.IndexOf(afterLoop); + var loopLocal = getDCLoopLocal(index, newIndex); + if (loopLocal == null) + return false; + var initValue2 = emu.getLocal(loopLocal) as Int32Value; + if (initValue2 == null || !initValue2.allBitsValid()) + return false; + + var dcGen = dynocode.getDynocodeGenerator(ctor.DeclaringType); + if (dcGen == null) + return false; + int loopLocalValue = initValue2.value; + foreach (var val in dcGen.getValues(initValue.value)) + loopLocalValue ^= val; + + emu.setLocal(loopLocal, new Int32Value(loopLocalValue)); + emu.emulate(instr); + index = newIndex - 1; + return true; + } + + Local getDCLoopLocal(int start, int end) { + var instrs = stringMethod.Body.Instructions; + for (int i = start; i < end - 1; i++) { + if (instrs[i].OpCode.Code != Code.Xor) + continue; + var stloc = instrs[i + 1]; + if (!stloc.IsStloc()) + continue; + return stloc.GetLocal(stringMethod.Body.Variables); + } + return null; + } + + static int findLeave(IList instrs, int index) { + for (int i = index; i < instrs.Count; i++) { + if (instrs[i].OpCode.Code == Code.Leave_S || instrs[i].OpCode.Code == Code.Leave) + return i; + } + return -1; + } + static int findInitIntsIndex(MethodDef method, out bool initializedAll) { initializedAll = false;