diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index 7c946ab2..dc6c3bce 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -159,7 +159,9 @@
-
+
+
+
diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs
index 284d2dbe..74a3db9b 100644
--- a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs
@@ -159,6 +159,8 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
AddTypesToBeRemoved(stringDecrypter.DynocodeTypes, "Dynocode type");
AddResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings");
}
+ stringDecrypter.CloseServer();
+
AddTypeToBeRemoved(assemblyResolver.Type, "Assembly resolver type");
AddTypeToBeRemoved(assemblyResolver.OtherType, "Assembly resolver other type");
AddTypeToBeRemoved(resourceResolver.Type, "Resource resolver type");
@@ -214,6 +216,14 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
DotNetUtils.RestoreBody(cctor, allInstructions, allExceptionHandlers);
}
+ protected override void Dispose(bool disposing) {
+ if (disposing) {
+ if (stringDecrypter != null)
+ stringDecrypter.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
public override IEnumerable GetStringDecrypterMethods() {
var list = new List();
if (stringDecrypter.Method != null)
diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/DynamicDynocodeIterator.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/DynamicDynocodeIterator.cs
new file mode 100644
index 00000000..1d60eed1
--- /dev/null
+++ b/de4dot.code/deobfuscators/Eazfuscator_NET/DynamicDynocodeIterator.cs
@@ -0,0 +1,109 @@
+/*
+ Copyright (C) 2011-2013 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;
+using System.Collections.Generic;
+using AssemblyData;
+using de4dot.code.AssemblyClient;
+using dnlib.DotNet;
+
+namespace de4dot.code.deobfuscators.Eazfuscator_NET {
+ class DynamicDynocodeIterator : IDisposable, IEnumerable {
+ IAssemblyClient assemblyClient;
+ List dynocodeTypes = new List();
+
+ public List Types {
+ get { return dynocodeTypes; }
+ }
+
+ class MyEnumerator : IEnumerator {
+ DynamicDynocodeIterator ddi;
+
+ public MyEnumerator(DynamicDynocodeIterator ddi) {
+ this.ddi = ddi;
+ }
+
+ public int Current {
+ get {
+ return (int)ddi.assemblyClient.GenericService.SendMessage(DynocodeService.MSG_CALL_GET_CURRENT, null);
+ }
+ }
+
+ public void Dispose() {
+ }
+
+ object System.Collections.IEnumerator.Current {
+ get { return Current; }
+ }
+
+ public bool MoveNext() {
+ return (bool)ddi.assemblyClient.GenericService.SendMessage(DynocodeService.MSG_CALL_MOVE_NEXT, null);
+ }
+
+ public void Reset() {
+ throw new NotImplementedException();
+ }
+ }
+
+ public void Dispose() {
+ if (assemblyClient != null)
+ assemblyClient.Dispose();
+ assemblyClient = null;
+ }
+
+ public void Initialize(ModuleDef module) {
+ if (assemblyClient != null)
+ return;
+
+ var serverVersion = NewProcessAssemblyClientFactory.GetServerClrVersion(module);
+ assemblyClient = new NewProcessAssemblyClientFactory(serverVersion).Create(AssemblyServiceType.Generic);
+ assemblyClient.Connect();
+ assemblyClient.WaitConnected();
+
+ assemblyClient.GenericService.LoadUserService(typeof(DynocodeService), null);
+ assemblyClient.GenericService.LoadAssembly(module.Location);
+ }
+
+ public void CreateEnumerable(MethodDef ctor, object[] args) {
+ var type = ctor.DeclaringType;
+ while (type.DeclaringType != null)
+ type = type.DeclaringType;
+ dynocodeTypes.Add(type);
+ assemblyClient.GenericService.SendMessage(DynocodeService.MSG_CREATE_ENUMERABLE,
+ new object[] { ctor.MDToken.ToUInt32(), args });
+ }
+
+ public void WriteEnumerableField(uint fieldToken, object value) {
+ assemblyClient.GenericService.SendMessage(DynocodeService.MSG_WRITE_ENUMERABLE_FIELD,
+ new object[] { fieldToken, value });
+ }
+
+ public void CreateEnumerator() {
+ assemblyClient.GenericService.SendMessage(DynocodeService.MSG_CREATE_ENUMERATOR, null);
+ }
+
+ public IEnumerator GetEnumerator() {
+ return new MyEnumerator(this);
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return new MyEnumerator(this);
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs
deleted file mode 100644
index fcd689ba..00000000
--- a/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- Copyright (C) 2011-2013 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 dnlib.DotNet;
-using dnlib.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 {
- foreach (var type in typeToDCGen.Keys)
- yield return type.DeclaringType;
- }
- }
-
- 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/DynocodeService.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/DynocodeService.cs
new file mode 100644
index 00000000..4c98d3d5
--- /dev/null
+++ b/de4dot.code/deobfuscators/Eazfuscator_NET/DynocodeService.cs
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 2011-2013 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;
+using System.Collections.Generic;
+using System.Reflection;
+using AssemblyData;
+
+namespace de4dot.code.deobfuscators.Eazfuscator_NET {
+ class DynocodeService : IUserGenericService {
+ public const int MSG_CREATE_ENUMERABLE = 0;
+ public const int MSG_WRITE_ENUMERABLE_FIELD = 1;
+ public const int MSG_CREATE_ENUMERATOR = 2;
+ public const int MSG_CALL_GET_CURRENT = 3;
+ public const int MSG_CALL_MOVE_NEXT = 4;
+
+ Module reflObfModule;
+ IEnumerable ienumerable = null;
+ IEnumerator ienumerator = null;
+
+ [CreateUserGenericService]
+ public static IUserGenericService Create() {
+ return new DynocodeService();
+ }
+
+ public void AssemblyLoaded(Assembly assembly) {
+ this.reflObfModule = assembly.ManifestModule;
+ }
+
+ public object HandleMessage(int msg, object[] args) {
+ switch (msg) {
+ case MSG_CREATE_ENUMERABLE:
+ CreateEnumerable((uint)args[0], args[1] as object[]);
+ return true;
+
+ case MSG_WRITE_ENUMERABLE_FIELD:
+ WriteEnumerableField((uint)args[0], args[1] as object);
+ return true;
+
+ case MSG_CREATE_ENUMERATOR:
+ CreateEnumerator();
+ return true;
+
+ case MSG_CALL_GET_CURRENT:
+ return CallGetCurrent();
+
+ case MSG_CALL_MOVE_NEXT:
+ return CallMoveNext();
+
+ default:
+ throw new ApplicationException(string.Format("Invalid msg: {0:X8}", msg));
+ }
+ }
+
+ void CreateEnumerable(uint ctorToken, object[] args) {
+ var ctor = reflObfModule.ResolveMethod((int)ctorToken) as ConstructorInfo;
+ if (ctor == null)
+ throw new ApplicationException(string.Format("Invalid ctor with token: {0:X8}", ctorToken));
+ ienumerable = (IEnumerable)ctor.Invoke(args);
+ }
+
+ void WriteEnumerableField(uint fieldToken, object value) {
+ var field = reflObfModule.ResolveField((int)fieldToken);
+ if (field == null)
+ throw new ApplicationException(string.Format("Invalid field: {0:X8}", fieldToken));
+ field.SetValue(ienumerable, value);
+ }
+
+ void CreateEnumerator() {
+ ienumerator = ienumerable.GetEnumerator();
+ }
+
+ int CallGetCurrent() {
+ return ienumerator.Current;
+ }
+
+ bool CallMoveNext() {
+ return ienumerator.MoveNext();
+ }
+
+ public void Dispose() {
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/ResourceMethodsRestorer.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/ResourceMethodsRestorer.cs
index ec9a2216..96db8fe4 100644
--- a/de4dot.code/deobfuscators/Eazfuscator_NET/ResourceMethodsRestorer.cs
+++ b/de4dot.code/deobfuscators/Eazfuscator_NET/ResourceMethodsRestorer.cs
@@ -50,9 +50,11 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
var getStream2 = GetTheOnlyMethod(type, "System.IO.Stream", "(System.Reflection.Assembly,System.Type,System.String)");
var getNames = GetTheOnlyMethod(type, "System.String[]", "(System.Reflection.Assembly)");
+ var getRefAsms = GetTheOnlyMethod(type, "System.Reflection.AssemblyName[]", "(System.Reflection.Assembly)");
var bitmapCtor = GetTheOnlyMethod(type, "System.Drawing.Bitmap", "(System.Type,System.String)");
var iconCtor = GetTheOnlyMethod(type, "System.Drawing.Icon", "(System.Type,System.String)");
- if (getStream2 == null && getNames == null && bitmapCtor == null && iconCtor == null)
+ if (getStream2 == null && getNames == null && getRefAsms == null &&
+ bitmapCtor == null && iconCtor == null)
continue;
var resource = FindGetManifestResourceStreamTypeResource(type, simpleDeobfuscator, deob);
@@ -62,6 +64,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
getManifestResourceStreamType = type;
CreateGetManifestResourceStream2(getStream2);
CreateGetManifestResourceNames(getNames);
+ CreateGetReferencedAssemblies(getRefAsms);
CreateBitmapCtor(bitmapCtor);
CreateIconCtor(iconCtor);
getManifestResourceStreamTypeResource = resource;
diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs
index dd40699d..26b0d1cc 100644
--- a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs
+++ b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs
@@ -27,7 +27,7 @@ using de4dot.blocks;
using de4dot.blocks.cflow;
namespace de4dot.code.deobfuscators.Eazfuscator_NET {
- class StringDecrypter {
+ class StringDecrypter : IDisposable {
ModuleDefMD module;
TypeDef stringType;
MethodDef stringMethod;
@@ -47,7 +47,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
EfConstantsReader stringMethodConsts;
bool isV32OrLater;
int? validStringDecrypterValue;
- Dynocode dynocode;
+ DynamicDynocodeIterator dynocode;
class StreamHelperType {
public TypeDef type;
@@ -228,7 +228,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
}
bool FindConstants(ISimpleDeobfuscator simpleDeobfuscator) {
- dynocode = new Dynocode(simpleDeobfuscator);
+ dynocode = new DynamicDynocodeIterator();
simpleDeobfuscator.Deobfuscate(stringMethod);
stringMethodConsts = new EfConstantsReader(stringMethod);
@@ -671,7 +671,11 @@ done: ;
if (index + 4 >= instrs.Count)
return false;
var ldloc = instrs[index + 3];
- if (!ldloc.IsLdloc() || instrs[index + 4].OpCode.Code != Code.Stfld)
+ var stfld = instrs[index + 4];
+ if (!ldloc.IsLdloc() || stfld.OpCode.Code != Code.Stfld)
+ return false;
+ var enumerableField = stfld.Operand as FieldDef;
+ if (enumerableField == null)
return false;
var initValue = emu.GetLocal(ldloc.GetLocal(stringMethod.Body.Variables)) as Int32Value;
@@ -692,19 +696,46 @@ done: ;
if (initValue2 == null || !initValue2.AllBitsValid())
return false;
- var dcGen = dynocode.GetDynocodeGenerator(ctor.DeclaringType);
- if (dcGen == null)
+ int loopStart = GetIndexOfCall(instrs, index, leaveIndex, "System.Int32 System.Collections.Generic.IEnumerator`1::get_Current()");
+ int loopEnd = GetIndexOfCall(instrs, loopStart, leaveIndex, "System.Boolean System.Collections.IEnumerator::MoveNext()");
+ if (loopStart < 0 || loopEnd < 0)
return false;
- int loopLocalValue = initValue2.value;
- foreach (var val in dcGen.GetValues(initValue.value))
- loopLocalValue ^= val;
+ loopStart++;
+ loopEnd--;
+
+ dynocode.Initialize(module);
+ var ctorArg = emu.Pop() as Int32Value;
+ if (ctorArg == null || !ctorArg.AllBitsValid())
+ return false;
+ dynocode.CreateEnumerable(ctor, new object[] { ctorArg.value });
+ dynocode.WriteEnumerableField(enumerableField.MDToken.ToUInt32(), initValue.value);
+ dynocode.CreateEnumerator();
+ foreach (var val in dynocode) {
+ emu.Push(new Int32Value(val));
+ for (int i = loopStart; i < loopEnd; i++)
+ emu.Emulate(instrs[i]);
+ }
- emu.SetLocal(loopLocal, new Int32Value(loopLocalValue));
- emu.Emulate(instr);
index = newIndex - 1;
return true;
}
+ static int GetIndexOfCall(IList instrs, int startIndex, int endIndex, string fullMethodName) {
+ if (startIndex < 0 || endIndex < 0)
+ return -1;
+ for (int i = startIndex; i < endIndex; i++) {
+ var instr = instrs[i];
+ if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt)
+ continue;
+ var method = instr.Operand as IMethod;
+ if (method == null || method.FullName != fullMethodName)
+ continue;
+
+ return i;
+ }
+ return -1;
+ }
+
Local GetDCLoopLocal(int start, int end) {
var instrs = stringMethod.Body.Instructions;
for (int i = start; i < end - 1; i++) {
@@ -980,5 +1011,15 @@ done: ;
return false;
}
+
+ public void Dispose() {
+ CloseServer();
+ }
+
+ public void CloseServer() {
+ if (dynocode != null)
+ dynocode.Dispose();
+ dynocode = null;
+ }
}
}
diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs
index f88d7f10..e38a6f98 100644
--- a/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs
+++ b/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs
@@ -722,7 +722,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET {
decryptStringMethod.Body.ExceptionHandlers.Count >= 2 &&
new LocalTypes(decryptStringMethod).All(locals35) &&
CheckTypeFields2(fields35)) {
- return "3.5 - 3.6";
+ return "3.5 - 4.0";
}
}
diff --git a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs
index 47bff0f6..62b57472 100644
--- a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs
@@ -27,7 +27,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
public class DeobfuscatorInfo : DeobfuscatorInfoBase {
public const string THE_NAME = "MaxtoCode";
public const string THE_TYPE = "mc";
- const string DEFAULT_REGEX = @"!^[oO01l]+$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
+ const string DEFAULT_REGEX = @"!^[oO01l]+$&!^[A-F0-9]{20,}$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
IntOption stringCodePage;
public DeobfuscatorInfo()
@@ -106,7 +106,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
var fileData = DeobUtils.ReadModule(module);
decrypterInfo = new DecrypterInfo(mainType, fileData);
- var methodsDecrypter = new MethodsDecrypter(decrypterInfo);
+ var methodsDecrypter = new MethodsDecrypter(module, decrypterInfo);
if (!methodsDecrypter.Decrypt(ref dumpedMethods))
return false;
diff --git a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs
index 1a24dbad..f5efc42e 100644
--- a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs
+++ b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs
@@ -95,6 +95,28 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
MagicHi = 0x828ECDA3,
Version = EncryptionVersion.V7,
},
+ // 513D4492
+ // 51413BD8
+ // 51413D68
+ // 5166DB4F
+ new EncryptionInfo {
+ MagicLo = 0x1A683B87,
+ MagicHi = 0x128ECDA3,
+ Version = EncryptionVersion.V8,
+ },
+ // 51927495
+ new EncryptionInfo {
+ MagicLo = 0x7A643B87,
+ MagicHi = 0x624ECDA3,
+ Version = EncryptionVersion.V8,
+ },
+ // 526BC020
+ // 526BDD12
+ new EncryptionInfo {
+ MagicLo = 0x9A683B87,
+ MagicHi = 0x928ECDA3,
+ Version = EncryptionVersion.V8,
+ },
};
public static readonly EncryptionInfo[] McKey8C0h = new EncryptionInfo[] {
@@ -149,6 +171,23 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
MagicHi = 0x8723891F,
Version = EncryptionVersion.V7,
},
+ // 513D4492
+ // 51413BD8
+ // 51413D68
+ // 5166DB4F
+ // 526BC020
+ // 526BDD12
+ new EncryptionInfo {
+ MagicLo = 0x1A731B13,
+ MagicHi = 0x1723891F,
+ Version = EncryptionVersion.V8,
+ },
+ // 51927495
+ new EncryptionInfo {
+ MagicLo = 0x7A731B13,
+ MagicHi = 0x1723891F,
+ Version = EncryptionVersion.V8,
+ },
};
}
}
diff --git a/de4dot.code/deobfuscators/MaxtoCode/MainType.cs b/de4dot.code/deobfuscators/MaxtoCode/MainType.cs
index 76d0913c..93d0f420 100644
--- a/de4dot.code/deobfuscators/MaxtoCode/MainType.cs
+++ b/de4dot.code/deobfuscators/MaxtoCode/MainType.cs
@@ -27,6 +27,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
ModuleDefMD module;
TypeDef mcType;
bool isOld;
+ ModuleRef runtimeModule1, runtimeModule2;
public bool IsOld {
get { return isOld; }
@@ -49,6 +50,15 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
}
}
+ public IEnumerable RuntimeModuleRefs {
+ get {
+ if (runtimeModule1 != null)
+ yield return runtimeModule1;
+ if (runtimeModule2 != null)
+ yield return runtimeModule2;
+ }
+ }
+
public bool Detected {
get { return mcType != null; }
}
@@ -80,9 +90,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
if (!DotNetUtils.IsMethod(method, "System.Void", "()"))
continue;
- ModuleRef module1, module2;
bool isOldTmp;
- if (!CheckType(method.DeclaringType, out module1, out module2, out isOldTmp))
+ if (!CheckType(method.DeclaringType, out runtimeModule1, out runtimeModule2, out isOldTmp))
continue;
mcType = method.DeclaringType;
diff --git a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs
index b3151eeb..af2489a7 100644
--- a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs
+++ b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs
@@ -21,15 +21,19 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
+using dnlib.DotNet;
using dnlib.IO;
+using dnlib.PE;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.MaxtoCode {
// Decrypts methods, resources and strings (#US heap)
class MethodsDecrypter {
+ ModuleDef module;
DecrypterInfo decrypterInfo;
class MethodInfos {
+ ModuleDef module;
MainType mainType;
MyPEImage peImage;
PeHeader peHeader;
@@ -43,16 +47,6 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
const int ENCRYPTED_DATA_INFO_SIZE = 0x13;
delegate byte[] DecryptFunc(byte[] encrypted);
- readonly DecryptFunc[] decryptHandlersV1a;
- readonly DecryptFunc[] decryptHandlersV1b;
- readonly DecryptFunc[] decryptHandlersV2;
- readonly DecryptFunc[] decryptHandlersV3;
- readonly DecryptFunc[] decryptHandlersV4;
- readonly DecryptFunc[] decryptHandlersV5a;
- readonly DecryptFunc[] decryptHandlersV5b;
- readonly DecryptFunc[] decryptHandlersV5c;
- readonly DecryptFunc[] decryptHandlersV6;
- readonly DecryptFunc[] decryptHandlersV7;
public class DecryptedMethodInfo {
public uint bodyRva;
@@ -64,23 +58,13 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
}
}
- public MethodInfos(MainType mainType, MyPEImage peImage, PeHeader peHeader, McKey mcKey) {
+ public MethodInfos(ModuleDef module, MainType mainType, MyPEImage peImage, PeHeader peHeader, McKey mcKey) {
+ this.module = module;
this.mainType = mainType;
this.peImage = peImage;
this.peHeader = peHeader;
this.mcKey = mcKey;
- decryptHandlersV1a = new DecryptFunc[] { Decrypt1_v1, Decrypt4_v1, Decrypt2_v1, Decrypt3_v1, Decrypt5, Decrypt6, Decrypt7 };
- decryptHandlersV1b = new DecryptFunc[] { Decrypt4_v1, Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt5, Decrypt6, Decrypt7 };
- decryptHandlersV2 = new DecryptFunc[] { Decrypt3_v1, Decrypt2_v1, Decrypt1_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 };
- decryptHandlersV3 = new DecryptFunc[] { Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 };
- decryptHandlersV4 = new DecryptFunc[] { Decrypt2_v1, Decrypt1_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 };
- decryptHandlersV5a = new DecryptFunc[] { Decrypt4_v1, Decrypt2_v1, Decrypt3_v1, Decrypt1_v1, Decrypt5, Decrypt6, Decrypt7 };
- decryptHandlersV5b = new DecryptFunc[] { Decrypt4_v2, Decrypt2_v2, Decrypt3_v2, Decrypt1_v2, Decrypt6, Decrypt7, Decrypt5 };
- decryptHandlersV5c = new DecryptFunc[] { Decrypt4_v3, Decrypt2_v3, Decrypt3_v3, Decrypt1_v3, Decrypt6, Decrypt7, Decrypt5 };
- decryptHandlersV6 = new DecryptFunc[] { Decrypt4_v4, Decrypt2_v4, Decrypt3_v4, Decrypt1_v4, Decrypt6, Decrypt7, Decrypt5 };
- decryptHandlersV7 = new DecryptFunc[] { Decrypt4_v5, Decrypt2_v5, Decrypt3_v5, Decrypt1_v5, Decrypt6, Decrypt8_v5, Decrypt9_v5, Decrypt7, Decrypt5 };
-
structSize = GetStructSize(mcKey);
uint methodInfosRva = peHeader.GetRva(0x0FF8, mcKey.ReadUInt32(0x005A));
@@ -152,13 +136,20 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
interface IDecrypter {
byte[] Decrypt(int type, byte[] encrypted);
+ bool HasTimeStamp(uint timeStamp);
}
class Decrypter : IDecrypter {
DecryptFunc[] decrypterHandlers;
+ uint[] timeStamps;
- public Decrypter(DecryptFunc[] decrypterHandlers) {
+ public Decrypter(DecryptFunc[] decrypterHandlers)
+ : this(decrypterHandlers, null) {
+ }
+
+ public Decrypter(DecryptFunc[] decrypterHandlers, uint[] timeStamps) {
this.decrypterHandlers = decrypterHandlers;
+ this.timeStamps = timeStamps ?? new uint[0];
}
public byte[] Decrypt(int type, byte[] encrypted) {
@@ -166,27 +157,44 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
return decrypterHandlers[type - 1](encrypted);
throw new ApplicationException(string.Format("Invalid encryption type: {0:X2}", type));
}
+
+ public bool HasTimeStamp(uint timeStamp) {
+ foreach (var ts in timeStamps) {
+ if (timeStamp == ts)
+ return true;
+ }
+ return false;
+ }
}
void InitializeDecrypter() {
switch (GetVersion()) {
case EncryptionVersion.V1:
- decrypters.Add(new Decrypter(decryptHandlersV1a));
- decrypters.Add(new Decrypter(decryptHandlersV1b));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v1, Decrypt4_v1, Decrypt2_v1, Decrypt3_v1, Decrypt5, Decrypt6, Decrypt7 }, new uint[] { 0x462FA2D2 }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v1, Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt5, Decrypt6, Decrypt7 }, new uint[] { 0x471299D3 }));
break;
- case EncryptionVersion.V2: decrypters.Add(new Decrypter(decryptHandlersV2)); break;
- case EncryptionVersion.V3: decrypters.Add(new Decrypter(decryptHandlersV3)); break;
- case EncryptionVersion.V4: decrypters.Add(new Decrypter(decryptHandlersV4)); break;
+ case EncryptionVersion.V2: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt3_v1, Decrypt2_v1, Decrypt1_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 }, new uint[] { 0x482384FB, 0x4A5EEC64, 0x4BD6F703, 0x4C6220EC, 0x4C622357, 0x4C6E4605, 0x4D0E220D, 0x4DC2FC75, 0x4DC2FE0C, 0x4DFA3D5D })); break;
+ case EncryptionVersion.V3: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 }, new uint[] { 0x4ECF2195, 0x4ED76740, 0x4EE1FAD1 })); break;
+ case EncryptionVersion.V4: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt2_v1, Decrypt1_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 }, new uint[] { 0x4F832868, 0x4F8C86BE, 0x4F9447DB, 0x4FDEF2FF })); break;
case EncryptionVersion.V5:
- decrypters.Add(new Decrypter(decryptHandlersV5a));
- decrypters.Add(new Decrypter(decryptHandlersV5b));
- decrypters.Add(new Decrypter(decryptHandlersV5c));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v1, Decrypt2_v1, Decrypt3_v1, Decrypt1_v1, Decrypt5, Decrypt6, Decrypt7 }, new uint[] { 0x4F8E262C, 0x4F966B0B, 0x4FAB3CCF }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v2, Decrypt2_v2, Decrypt3_v2, Decrypt1_v2, Decrypt6, Decrypt7, Decrypt5 }, new uint[] { 0x4FC7459E, 0x4FCEBD7B }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v3, Decrypt2_v3, Decrypt3_v3, Decrypt1_v3, Decrypt6, Decrypt7, Decrypt5 }, new uint[] { 0x4FBE81DE }));
break;
- case EncryptionVersion.V6: decrypters.Add(new Decrypter(decryptHandlersV6)); break;
- case EncryptionVersion.V7: decrypters.Add(new Decrypter(decryptHandlersV7)); break;
+ case EncryptionVersion.V6: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v4, Decrypt2_v4, Decrypt3_v4, Decrypt1_v4, Decrypt6, Decrypt7, Decrypt5 }, new uint[] { 0x50A0963C })); break;
+ case EncryptionVersion.V7: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v5, Decrypt2_v5, Decrypt3_v5, Decrypt1_v5, Decrypt6, Decrypt8_v5, Decrypt9_v5, Decrypt7, Decrypt5 }, new uint[] { 0x50D367A5 })); break;
+
+ case EncryptionVersion.V8:
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v6, Decrypt2_v2, Decrypt3_v6, Decrypt1_v6, Decrypt6, Decrypt8_v6, Decrypt9_v6, Decrypt7, Decrypt10, Decrypt5 }, new uint[] { 0x5166DB4F, 0x51927495 }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v7, Decrypt2_v2, Decrypt3_v6, Decrypt1_v7, Decrypt6, Decrypt8_v7, Decrypt9_v7, Decrypt7, Decrypt5 }, new uint[] { 0x51413D68 }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v7, Decrypt2_v2, Decrypt3_v6, Decrypt1_v7, Decrypt6, Decrypt8_v8, Decrypt9_v8, Decrypt7, Decrypt5 }, new uint[] { 0x513D7124, 0x51413BD8 }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v5, Decrypt2_v2, Decrypt3_v6, Decrypt1_v9, Decrypt6, Decrypt8_v8, Decrypt9_v9, Decrypt7, Decrypt5 }, new uint[] { 0x513D4492, 0x5113E277 }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt3_v6, Decrypt2_v2, Decrypt4_v8, Decrypt1_v10, Decrypt8_v9, Decrypt9_v10, Decrypt6, Decrypt7, Decrypt5 }, new uint[] { 0x526BDD12 }));
+ decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v10, Decrypt4_v8, Decrypt2_v2, Decrypt3_v6, Decrypt6, Decrypt8_v9, Decrypt9_v10, Decrypt7, Decrypt5 }, new uint[] { 0x526BC020 }));
+ break;
case EncryptionVersion.Unknown:
default:
@@ -200,16 +208,82 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
throw new ApplicationException("Could not decrypt methods");
}
+ uint GetRuntimeTimeStamp() {
+ string path = module.Location;
+ if (string.IsNullOrEmpty(path))
+ return 0;
+
+ try {
+ var rtNames = new List();
+ foreach (var rtModRef in mainType.RuntimeModuleRefs) {
+ string dllName = rtModRef.Name;
+ if (!dllName.ToUpperInvariant().EndsWith(".DLL"))
+ dllName += ".dll";
+ rtNames.Add(dllName);
+ }
+ if (rtNames.Count == 0)
+ return 0;
+
+ for (var di = new DirectoryInfo(Path.GetDirectoryName(path)); di != null; di = di.Parent) {
+ foreach (var dllName in rtNames) {
+ try {
+ using (var peImage = new PEImage(Path.Combine(di.FullName, dllName))) {
+ if (peImage.ImageNTHeaders.FileHeader.Machine == Machine.I386)
+ return peImage.ImageNTHeaders.FileHeader.TimeDateStamp;
+ }
+ }
+ catch {
+ }
+ }
+ }
+ }
+ catch {
+ }
+
+ return 0;
+ }
+
bool InitializeInfos2() {
+ uint rtTimeStamp = GetRuntimeTimeStamp();
+ if (rtTimeStamp != 0) {
+ var decrypter = GetCorrectDecrypter(rtTimeStamp);
+ if (decrypter != null) {
+ try {
+ if (InitializeInfos2(decrypter))
+ return true;
+ }
+ catch {
+ }
+ }
+ }
+
+ Dictionary savedInfos = null;
foreach (var decrypter in decrypters) {
try {
- if (InitializeInfos2(decrypter))
- return true;
+ if (InitializeInfos2(decrypter)) {
+ if (savedInfos != null) {
+ Logger.w("Decryption probably failed. Make sure the correct MaxtoCode runtime file is present.");
+ break;
+ }
+ savedInfos = infos;
+ infos = new Dictionary();
+ }
}
catch {
}
}
- return false;
+ if (savedInfos == null)
+ return false;
+ infos = savedInfos;
+ return true;
+ }
+
+ IDecrypter GetCorrectDecrypter(uint timeStamp) {
+ foreach (var decrypter in decrypters) {
+ if (decrypter.HasTimeStamp(timeStamp))
+ return decrypter;
+ }
+ return null;
}
bool InitializeInfos2(IDecrypter decrypter) {
@@ -222,6 +296,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
var encryptedDataInfos = new byte[numEncryptedDataInfos][];
uint offset = 8;
+ infos.Clear();
for (int i = 0; i < numMethods; i++, offset += structSize) {
uint methodBodyRva = ReadEncryptedUInt32(offset);
uint totalSize = ReadEncryptedUInt32(offset + 4);
@@ -313,6 +388,22 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
return Decrypt1(encrypted, 9, 9, 0x500);
}
+ byte[] Decrypt1_v6(byte[] encrypted) {
+ return Decrypt1(encrypted, 0x27, 0x27, 0x100);
+ }
+
+ byte[] Decrypt1_v7(byte[] encrypted) {
+ return Decrypt1(encrypted, 0x1D, 0x1D, 0x400);
+ }
+
+ byte[] Decrypt1_v9(byte[] encrypted) {
+ return Decrypt1(encrypted, 9, 0x13, 0x400);
+ }
+
+ byte[] Decrypt1_v10(byte[] encrypted) {
+ return Decrypt1(encrypted, 0x11, 0x11, 0x400);
+ }
+
byte[] Decrypt1(byte[] encrypted, int keyStart, int keyReset, int keyEnd) {
var decrypted = new byte[encrypted.Length];
for (int i = 0, ki = keyStart; i < decrypted.Length; i++) {
@@ -386,6 +477,10 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
return Decrypt3(encrypted, 0x015E + 7);
}
+ byte[] Decrypt3_v6(byte[] encrypted) {
+ return Decrypt3(encrypted, 0x015E + 0x7F);
+ }
+
static readonly byte[] decrypt3Shifts = new byte[16] { 5, 11, 14, 21, 6, 20, 17, 29, 4, 10, 3, 2, 7, 1, 26, 18 };
byte[] Decrypt3(byte[] encrypted, int offset) {
if ((encrypted.Length & 7) != 0)
@@ -435,6 +530,18 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
return Decrypt4(encrypted, 0x15, 0x15, 0x100);
}
+ byte[] Decrypt4_v6(byte[] encrypted) {
+ return Decrypt4(encrypted, 0x63, 0x63, 0x150);
+ }
+
+ byte[] Decrypt4_v7(byte[] encrypted) {
+ return Decrypt4(encrypted, 0x0B, 0x0B, 0x100);
+ }
+
+ byte[] Decrypt4_v8(byte[] encrypted) {
+ return Decrypt4(encrypted, 9, 9, 0x100);
+ }
+
byte[] Decrypt4(byte[] encrypted, int keyStart, int keyReset, int keyEnd) {
var decrypted = new byte[encrypted.Length / 3 * 2 + 1];
@@ -476,6 +583,22 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
return Decrypt8(encrypted, 7, 7, 0x600);
}
+ byte[] Decrypt8_v6(byte[] encrypted) {
+ return Decrypt8(encrypted, 0x1B, 0x1B, 0x100);
+ }
+
+ byte[] Decrypt8_v7(byte[] encrypted) {
+ return Decrypt8(encrypted, 0x0D, 0x0D, 0x600);
+ }
+
+ byte[] Decrypt8_v8(byte[] encrypted) {
+ return Decrypt8(encrypted, 0x11, 0x11, 0x600);
+ }
+
+ byte[] Decrypt8_v9(byte[] encrypted) {
+ return Decrypt8(encrypted, 0xA, 0xA, 0x600);
+ }
+
byte[] Decrypt8(byte[] encrypted, int keyStart, int keyReset, int keyEnd) {
var decrypted = new byte[encrypted.Length];
int ki = keyStart;
@@ -493,6 +616,26 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
return Decrypt9(encrypted, 0x11, 0x11, 0x610);
}
+ byte[] Decrypt9_v6(byte[] encrypted) {
+ return Decrypt9(encrypted, 0x2E, 0x2E, 0x310);
+ }
+
+ byte[] Decrypt9_v7(byte[] encrypted) {
+ return Decrypt9(encrypted, 0x28, 0x28, 0x510);
+ }
+
+ byte[] Decrypt9_v8(byte[] encrypted) {
+ return Decrypt9(encrypted, 0x2C, 0x2C, 0x510);
+ }
+
+ byte[] Decrypt9_v9(byte[] encrypted) {
+ return Decrypt9(encrypted, 0x10, 0x10, 0x510);
+ }
+
+ byte[] Decrypt9_v10(byte[] encrypted) {
+ return Decrypt9(encrypted, 5, 5, 0x510);
+ }
+
byte[] Decrypt9(byte[] encrypted, int keyStart, int keyReset, int keyEnd) {
var decrypted = new byte[encrypted.Length];
int ki = keyStart;
@@ -527,6 +670,31 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
return decrypted;
}
+ byte[] Decrypt10(byte[] encrypted) {
+ byte[] enc = (byte[])encrypted.Clone();
+ byte[] dest = new byte[enc.Length];
+ int halfSize = enc.Length / 2;
+
+ byte b = enc[0];
+ b ^= 0xA1;
+ dest[0] = b;
+
+ b = enc[enc.Length - 1];
+ b ^= 0x1A;
+ dest[enc.Length - 1] = b;
+
+ enc[0] = dest[0];
+ enc[enc.Length - 1] = b;
+
+ for (int i = 1; i < halfSize; i++)
+ dest[i] = (byte)(enc[i] ^ dest[i - 1]);
+
+ for (int i = enc.Length - 2; i >= halfSize; i--)
+ dest[i] = (byte)(enc[i] ^ dest[i + 1]);
+
+ return dest;
+ }
+
byte[] blowfishKey;
byte[] GetBlowfishKey() {
if (blowfishKey != null)
@@ -546,7 +714,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
}
}
- public MethodsDecrypter(DecrypterInfo decrypterInfo) {
+ public MethodsDecrypter(ModuleDef module, DecrypterInfo decrypterInfo) {
+ this.module = module;
this.decrypterInfo = decrypterInfo;
}
@@ -565,7 +734,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
var dumpedMethods = new DumpedMethods();
var peImage = decrypterInfo.peImage;
- var methodInfos = new MethodInfos(decrypterInfo.mainType, peImage, decrypterInfo.peHeader, decrypterInfo.mcKey);
+ var methodInfos = new MethodInfos(module, decrypterInfo.mainType, peImage, decrypterInfo.peHeader, decrypterInfo.mcKey);
methodInfos.InitializeInfos();
var methodDef = peImage.DotNetFile.MetaData.TablesStream.MethodTable;
@@ -596,9 +765,14 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
var peImage = decrypterInfo.peImage;
var fileData = decrypterInfo.fileData;
+ uint decryptedResources = peHeader.ReadUInt32(0xFE8) ^ mcKey.ReadUInt32(0);
uint resourceRva = peHeader.GetRva(0x0E10, mcKey.ReadUInt32(0x00A0));
- uint resourceSize = peHeader.ReadUInt32(0x0E14) ^ mcKey.ReadUInt32(0x00AA);
- if (resourceRva == 0 || resourceSize == 0)
+ int resourceSize = (int)(peHeader.ReadUInt32(0x0E14) ^ mcKey.ReadUInt32(0x00AA));
+ if (decryptedResources == 1) {
+ Logger.v("Resources have already been decrypted");
+ return;
+ }
+ if (resourceRva == 0 || resourceSize <= 0)
return;
if (resourceRva != (uint)peImage.Cor20Header.Resources.VirtualAddress ||
resourceSize != peImage.Cor20Header.Resources.Size) {
diff --git a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs
index b5109c75..5eeeb246 100644
--- a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs
+++ b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs
@@ -30,6 +30,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
V5,
V6,
V7,
+ V8,
}
class PeHeader {
@@ -44,6 +45,7 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
public PeHeader(MainType mainType, MyPEImage peImage) {
uint headerOffset;
version = GetHeaderOffsetAndVersion(peImage, out headerOffset);
+ headerData = peImage.OffsetReadBytes(headerOffset, 0x1000);
switch (version) {
case EncryptionVersion.V1:
@@ -62,9 +64,22 @@ namespace de4dot.code.deobfuscators.MaxtoCode {
case EncryptionVersion.V7:
xorKey = 0x8ABA931;
break;
- }
- headerData = peImage.OffsetReadBytes(headerOffset, 0x1000);
+ case EncryptionVersion.V8:
+ if (CheckMcKeyRva(peImage, 0x99BA9A13))
+ break;
+ if (CheckMcKeyRva(peImage, 0x18ABA931))
+ break;
+ if (CheckMcKeyRva(peImage, 0x18ABA933))
+ break;
+ break;
+ }
+ }
+
+ bool CheckMcKeyRva(MyPEImage peImage, uint newXorKey) {
+ xorKey = newXorKey;
+ uint rva = GetMcKeyRva();
+ return (rva & 0xFFF) == 0 && peImage.FindSection((RVA)rva) != null;
}
public uint GetMcKeyRva() {
diff --git a/de4dot.code/deobfuscators/MethodCallRestorerBase.cs b/de4dot.code/deobfuscators/MethodCallRestorerBase.cs
index 220067af..189549df 100644
--- a/de4dot.code/deobfuscators/MethodCallRestorerBase.cs
+++ b/de4dot.code/deobfuscators/MethodCallRestorerBase.cs
@@ -71,6 +71,15 @@ namespace de4dot.code.deobfuscators {
Add(oldMethod, newMethod, OpCodes.Callvirt);
}
+ public void CreateGetReferencedAssemblies(MethodDef oldMethod) {
+ if (oldMethod == null)
+ return;
+ var assemblyType = builder.Type("System.Reflection", "Assembly", builder.CorLib);
+ var asmNameArray = builder.Array(builder.Type("System.Reflection", "AssemblyName", builder.CorLib));
+ var newMethod = builder.InstanceMethod("GetReferencedAssemblies", assemblyType.TypeDefOrRef, asmNameArray);
+ Add(oldMethod, newMethod, OpCodes.Callvirt);
+ }
+
public void CreateBitmapCtor(MethodDef oldMethod) {
if (oldMethod == null)
return;
diff --git a/de4dot.code/deobfuscators/MyPEImage.cs b/de4dot.code/deobfuscators/MyPEImage.cs
index f933887e..a3661cea 100644
--- a/de4dot.code/deobfuscators/MyPEImage.cs
+++ b/de4dot.code/deobfuscators/MyPEImage.cs
@@ -73,7 +73,7 @@ namespace de4dot.code.deobfuscators {
this.peStream = peImage.CreateFullStream();
}
- ImageSectionHeader FindSection(RVA rva) {
+ public ImageSectionHeader FindSection(RVA rva) {
foreach (var section in peImage.ImageSectionHeaders) {
if (section.VirtualAddress <= rva && rva < section.VirtualAddress + Math.Max(section.VirtualSize, section.SizeOfRawData))
return section;
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/AssemblyResolver.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/AssemblyResolver.cs
index 08f3ba8b..8a4ce825 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/AssemblyResolver.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/AssemblyResolver.cs
@@ -87,7 +87,6 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
var resolverLocals = new string[] {
"System.Byte[]",
"System.Reflection.Assembly",
- "System.Security.Cryptography.MD5",
"System.String",
"System.IO.BinaryReader",
"System.IO.Stream",
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs
index 85526e2a..70e24bca 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs
@@ -26,12 +26,13 @@ using dnlib.DotNet;
using dnlib.DotNet.Emit;
using dnlib.DotNet.Writer;
using de4dot.blocks;
+using de4dot.blocks.cflow;
namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
public class DeobfuscatorInfo : DeobfuscatorInfoBase {
public const string THE_NAME = ".NET Reactor";
public const string THE_TYPE = "dr4";
- const string DEFAULT_REGEX = DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
+ const string DEFAULT_REGEX = @"!^[A-Za-z0-9]{2,3}$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
BoolOption decryptMethods;
BoolOption decryptBools;
BoolOption restoreTypes;
@@ -142,6 +143,15 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
get { return startedDeobfuscating ? options.InlineMethods : true; }
}
+ public override IEnumerable BlocksDeobfuscators {
+ get {
+ var list = new List();
+ if (CanInlineMethods)
+ list.Add(new DnrMethodCallInliner());
+ return list;
+ }
+ }
+
public Deobfuscator(Options options)
: base(options) {
this.options = options;
@@ -168,7 +178,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
static Regex isRandomName = new Regex(@"^[A-Z]{30,40}$");
static Regex isRandomNameMembers = new Regex(@"^[a-zA-Z0-9]{9,11}$"); // methods, fields, props, events
- static Regex isRandomNameTypes = new Regex(@"^[a-zA-Z0-9]{18,19}(?:`\d+)?$"); // types, namespaces
+ static Regex isRandomNameTypes = new Regex(@"^[a-zA-Z0-9]{18,20}(?:`\d+)?$"); // types, namespaces
bool CheckValidName(string name, Regex regex) {
if (isRandomName.IsMatch(name))
@@ -333,8 +343,12 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
}
var compileMethod = MethodsDecrypter.FindDnrCompileMethod(methodsDecrypter.Method.DeclaringType);
- if (compileMethod == null)
- return DeobfuscatorInfo.THE_NAME + " < 4.0";
+ if (compileMethod == null) {
+ DeobfuscatedFile.Deobfuscate(methodsDecrypter.Method);
+ if (!MethodsDecrypter.IsNewer45Decryption(methodsDecrypter.Method))
+ return DeobfuscatorInfo.THE_NAME + " < 4.0";
+ return DeobfuscatorInfo.THE_NAME + " 4.5+";
+ }
DeobfuscatedFile.Deobfuscate(compileMethod);
bool compileMethodHasConstant_0x70000000 = DeobUtils.HasInteger(compileMethod, 0x70000000); // 4.0-4.1
DeobfuscatedFile.Deobfuscate(methodsDecrypter.Method);
@@ -346,11 +360,36 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
return DeobfuscatorInfo.THE_NAME + " 4.0";
}
if (!hasCorEnableProfilingString) {
- // 4.x or 4.5
+ // 4.x or 4.5 - 4.6
bool callsReverse = DotNetUtils.CallsMethod(methodsDecrypter.Method, "System.Void System.Array::Reverse(System.Array)");
if (!callsReverse)
- return DeobfuscatorInfo.THE_NAME + " 4.x";
- return DeobfuscatorInfo.THE_NAME + " 4.5";
+ return DeobfuscatorInfo.THE_NAME + " 4.0 - 4.4";
+
+ int numIntPtrSizeCompares = CountCompareSystemIntPtrSize(methodsDecrypter.Method);
+ bool hasSymmetricAlgorithm = new LocalTypes(methodsDecrypter.Method).Exists("System.Security.Cryptography.SymmetricAlgorithm");
+ if (module.IsClr40) {
+ switch (numIntPtrSizeCompares) {
+ case 7:
+ case 9: return DeobfuscatorInfo.THE_NAME + " 4.5";
+ case 10:
+ if (!hasSymmetricAlgorithm)
+ return DeobfuscatorInfo.THE_NAME + " 4.6";
+ return DeobfuscatorInfo.THE_NAME + " 4.7";
+ }
+ }
+ else {
+ switch (numIntPtrSizeCompares) {
+ case 6:
+ case 8: return DeobfuscatorInfo.THE_NAME + " 4.5";
+ case 9:
+ if (!hasSymmetricAlgorithm)
+ return DeobfuscatorInfo.THE_NAME + " 4.6";
+ return DeobfuscatorInfo.THE_NAME + " 4.7";
+ }
+ }
+
+ // Should never be reached unless it's a new version
+ return DeobfuscatorInfo.THE_NAME + " 4.5+";
}
// 4.2-4.4
@@ -364,6 +403,29 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
return DeobfuscatorInfo.THE_NAME + " 4.3";
}
+ static int CountCompareSystemIntPtrSize(MethodDef method) {
+ if (method == null || method.Body == null)
+ return 0;
+ int count = 0;
+ var instrs = method.Body.Instructions;
+ for (int i = 1; i < instrs.Count - 1; i++) {
+ var ldci4 = instrs[i];
+ if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 4)
+ continue;
+ if (!instrs[i + 1].IsConditionalBranch())
+ continue;
+ var call = instrs[i - 1];
+ if (call.OpCode.Code != Code.Call)
+ continue;
+ var calledMethod = call.Operand as MemberRef;
+ if (calledMethod == null || calledMethod.FullName != "System.Int32 System.IntPtr::get_Size()")
+ continue;
+
+ count++;
+ }
+ return count;
+ }
+
static bool FindString(MethodDef method, string s) {
foreach (var cs in DotNetUtils.GetCodeStrings(method)) {
if (cs == s)
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/DnrMethodCallInliner.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/DnrMethodCallInliner.cs
new file mode 100644
index 00000000..2a3324b6
--- /dev/null
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/DnrMethodCallInliner.cs
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2011-2013 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;
+using System.Collections.Generic;
+using dnlib.DotNet.Emit;
+using de4dot.blocks;
+using de4dot.blocks.cflow;
+
+namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
+ class DnrMethodCallInliner : MethodCallInliner {
+ public DnrMethodCallInliner()
+ : base(false) {
+ }
+
+ protected override Instruction GetFirstInstruction(IList instrs, ref int index) {
+ var instr = GetFirstInstruction(instrs, index);
+ if (instr != null)
+ index = instrs.IndexOf(instr);
+ return DotNetUtils.GetInstruction(instrs, ref index);
+ }
+
+ Instruction GetFirstInstruction(IList instrs, int index) {
+ try {
+ var instr = instrs[index];
+ if (!instr.IsBr())
+ return null;
+ instr = instr.Operand as Instruction;
+ if (instr == null)
+ return null;
+ if (!instr.IsLdcI4() || instr.GetLdcI4Value() != 0)
+ return null;
+ instr = instrs[instrs.IndexOf(instr) + 1];
+ if (!instr.IsBrtrue())
+ return null;
+ return instrs[instrs.IndexOf(instr) + 1];
+ }
+ catch {
+ return null;
+ }
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs
index c9f59963..9660c66e 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs
@@ -91,7 +91,8 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
if (!localTypes.All(requiredTypes))
return false;
if (!localTypes.Exists("System.Security.Cryptography.RijndaelManaged") &&
- !localTypes.Exists("System.Security.Cryptography.AesManaged"))
+ !localTypes.Exists("System.Security.Cryptography.AesManaged") &&
+ !localTypes.Exists("System.Security.Cryptography.SymmetricAlgorithm"))
return false;
if (checkResource && FindMethodsDecrypterResource(method) == null)
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs
index 8d538834..cc9a7dc3 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs
@@ -121,8 +121,8 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
}
}
- static short[] nativeLdci4 = new short[] { 0x55, 0x8B, 0xEC, 0xB8, -1, -1, -1, -1, 0x5D, 0xC3 };
- static short[] nativeLdci4_0 = new short[] { 0x55, 0x8B, 0xEC, 0x33, 0xC0, 0x5D, 0xC3 };
+ readonly static short[] nativeLdci4 = new short[] { 0x55, 0x8B, 0xEC, 0xB8, -1, -1, -1, -1, 0x5D, 0xC3 };
+ readonly static short[] nativeLdci4_0 = new short[] { 0x55, 0x8B, 0xEC, 0x33, 0xC0, 0x5D, 0xC3 };
public bool Decrypt(MyPEImage peImage, ISimpleDeobfuscator simpleDeobfuscator, ref DumpedMethods dumpedMethods, Dictionary tokenToNativeCode, bool unpackedNativeFile) {
if (encryptedResource.Method == null)
return false;
@@ -157,27 +157,29 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
}
}
else if (!hooksJitter || mode == 1) {
- // DNR 3.9.8.0, 4.0, 4.1, 4.2, 4.3, 4.4
-
- // If it's .NET 1.x, then offsets are used, not RVAs.
- bool useOffsets = unpackedNativeFile && module.IsClr1x;
+ // DNR 3.9.8.0, 4.0+
PatchDwords(peImage, methodsDataReader, patchCount);
+ bool oldCode = !IsNewer45Decryption(encryptedResource.Method);
while (methodsDataReader.Position < methodsData.Length - 1) {
uint rva = methodsDataReader.ReadUInt32();
- uint token = methodsDataReader.ReadUInt32(); // token, unknown, or index
- int size = methodsDataReader.ReadInt32();
- if (size > 0) {
- var newData = methodsDataReader.ReadBytes(size);
- if (useOffsets)
- peImage.DotNetSafeWriteOffset(rva, newData);
- else
- peImage.DotNetSafeWrite(rva, newData);
+ int size;
+ if (oldCode) {
+ methodsDataReader.ReadUInt32(); // token, unknown, or index
+ size = methodsDataReader.ReadInt32();
}
+ else
+ size = methodsDataReader.ReadInt32() * 4;
+
+ var newData = methodsDataReader.ReadBytes(size);
+ if (unpackedNativeFile)
+ peImage.DotNetSafeWriteOffset(rva, newData);
+ else
+ peImage.DotNetSafeWrite(rva, newData);
}
}
else {
- // DNR 4.0 - 4.5 (jitter is hooked)
+ // DNR 4.0+ (jitter is hooked)
var methodDef = peImage.DotNetFile.MetaData.TablesStream.MethodTable;
var rvaToIndex = new Dictionary((int)methodDef.Rows);
@@ -258,6 +260,33 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
return true;
}
+ public static bool IsNewer45Decryption(MethodDef method) {
+ if (method == null || method.Body == null)
+ return false;
+
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count - 4; i++) {
+ var ldci4 = instrs[i];
+ if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 4)
+ continue;
+ if (instrs[i + 1].OpCode.Code != Code.Mul)
+ continue;
+ ldci4 = instrs[i + 2];
+ if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 4)
+ continue;
+ if (instrs[i + 3].OpCode.Code != Code.Ldloca_S && instrs[i + 3].OpCode.Code != Code.Ldloca)
+ continue;
+ var call = instrs[i + 4];
+ if (call.OpCode.Code != Code.Call)
+ continue;
+ if (!DotNetUtils.IsPinvokeMethod(call.Operand as MethodDef, "kernel32", "VirtualProtect"))
+ continue;
+
+ return true;
+ }
+ return false;
+ }
+
static void PatchDwords(MyPEImage peImage, IBinaryReader reader, int count) {
for (int i = 0; i < count; i++) {
uint rva = reader.ReadUInt32();
@@ -363,6 +392,12 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
Array.Copy(encrypted, resourceData, resourceData.Length);
}
+ enum CompileMethodType {
+ Unknown,
+ V1, // <= DNR 4.5.0.0 (2012-11-06 <= endDate < 2013-01-31)
+ V2, // >= DNR 4.5.0.0 (2012-11-06 < startDate <= 2013-01-31)
+ }
+
public static MethodDef FindDnrCompileMethod(TypeDef type) {
foreach (var method in type.Methods) {
if (!method.IsStatic || method.Body == null)
@@ -370,11 +405,19 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 {
var sig = method.MethodSig;
if (sig == null || sig.Params.Count != 6)
continue;
- if (!DotNetUtils.IsMethod(method, "System.UInt32", "(System.UInt64&,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr&,System.UInt32&)"))
+ if (GetCompileMethodType(method) == CompileMethodType.Unknown)
continue;
return method;
}
return null;
}
+
+ static CompileMethodType GetCompileMethodType(MethodDef method) {
+ if (DotNetUtils.IsMethod(method, "System.UInt32", "(System.UInt64&,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr&,System.UInt32&)"))
+ return CompileMethodType.V1;
+ if (DotNetUtils.IsMethod(method, "System.UInt32", "(System.IntPtr,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr,System.UInt32&)"))
+ return CompileMethodType.V2;
+ return CompileMethodType.Unknown;
+ }
}
}