From 689635cf6918120ebf6a34d66b53e38ab8fbb5e8 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 13 Oct 2013 08:49:34 +0200 Subject: [PATCH 01/13] Support .NET Reactor 4.5 (an updated version) and 4.6 --- de4dot.code/de4dot.code.csproj | 1 + .../dotNET_Reactor/v4/Deobfuscator.cs | 63 ++++++++++++++-- .../dotNET_Reactor/v4/DnrMethodCallInliner.cs | 59 +++++++++++++++ .../dotNET_Reactor/v4/MethodsDecrypter.cs | 75 +++++++++++++++---- 4 files changed, 177 insertions(+), 21 deletions(-) create mode 100644 de4dot.code/deobfuscators/dotNET_Reactor/v4/DnrMethodCallInliner.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 55c6a658..7a89a627 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -157,6 +157,7 @@ + diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index 85526e2a..360c0e9d 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -26,6 +26,7 @@ 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 { @@ -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; @@ -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,27 @@ 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); + if (module.IsClr40) { + switch (numIntPtrSizeCompares) { + case 9: return DeobfuscatorInfo.THE_NAME + " 4.5"; + case 10:return DeobfuscatorInfo.THE_NAME + " 4.6"; + } + } + else { + switch (numIntPtrSizeCompares) { + case 8: return DeobfuscatorInfo.THE_NAME + " 4.5"; + case 9: return DeobfuscatorInfo.THE_NAME + " 4.6"; + } + } + + // Should never be reached unless it's a new version + return DeobfuscatorInfo.THE_NAME + " 4.5+"; } // 4.2-4.4 @@ -364,6 +394,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/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; + } } } From 6f925abab7b3f3e67f9cfbbf0db8c3b72aaa10d2 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 20 Oct 2013 10:25:51 +0200 Subject: [PATCH 02/13] Make FindSection() public --- de4dot.code/deobfuscators/MyPEImage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 7c6005cde5966e16f55f0bef97617ea0395d5a7e Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 20 Oct 2013 18:09:38 +0200 Subject: [PATCH 03/13] Support more MaxtoCode runtimes --- .../deobfuscators/MaxtoCode/Deobfuscator.cs | 2 +- .../MaxtoCode/EncryptionInfos.cs | 30 +++ .../deobfuscators/MaxtoCode/MainType.cs | 13 +- .../MaxtoCode/MethodsDecrypter.cs | 210 +++++++++++++++--- .../deobfuscators/MaxtoCode/PeHeader.cs | 17 +- 5 files changed, 232 insertions(+), 40 deletions(-) diff --git a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs index 47bff0f6..63e5ffbe 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs @@ -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..4baf7b93 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs @@ -95,6 +95,21 @@ 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, + }, }; public static readonly EncryptionInfo[] McKey8C0h = new EncryptionInfo[] { @@ -149,6 +164,21 @@ namespace de4dot.code.deobfuscators.MaxtoCode { MagicHi = 0x8723891F, Version = EncryptionVersion.V7, }, + // 513D4492 + // 51413BD8 + // 51413D68 + // 5166DB4F + 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..179aca66 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,42 @@ 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 })); + decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v1, Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt5, Decrypt6, Decrypt7 })); 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 })); break; + case EncryptionVersion.V3: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 })); break; + case EncryptionVersion.V4: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt2_v1, Decrypt1_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 })); 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 })); + decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v2, Decrypt2_v2, Decrypt3_v2, Decrypt1_v2, Decrypt6, Decrypt7, Decrypt5 })); + decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v3, Decrypt2_v3, Decrypt3_v3, Decrypt1_v3, Decrypt6, Decrypt7, Decrypt5 })); 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 })); 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 })); 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 })); + break; case EncryptionVersion.Unknown: default: @@ -200,7 +206,55 @@ 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() { + if (decrypters.Count > 1) { + uint rtTimeStamp = GetRuntimeTimeStamp(); + var decrypter = GetCorrectDecrypter(rtTimeStamp); + if (decrypter != null) { + try { + if (InitializeInfos2(decrypter)) + return true; + } + catch { + } + } + } + foreach (var decrypter in decrypters) { try { if (InitializeInfos2(decrypter)) @@ -212,6 +266,14 @@ namespace de4dot.code.deobfuscators.MaxtoCode { return false; } + IDecrypter GetCorrectDecrypter(uint timeStamp) { + foreach (var decrypter in decrypters) { + if (decrypter.HasTimeStamp(timeStamp)) + return decrypter; + } + return null; + } + bool InitializeInfos2(IDecrypter decrypter) { int numMethods = ReadInt32(0) ^ ReadInt32(4); if (numMethods < 0) @@ -313,6 +375,18 @@ 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(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 +460,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 +513,14 @@ 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(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { var decrypted = new byte[encrypted.Length / 3 * 2 + 1]; @@ -476,6 +562,18 @@ 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(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { var decrypted = new byte[encrypted.Length]; int ki = keyStart; @@ -493,6 +591,22 @@ 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(byte[] encrypted, int keyStart, int keyReset, int keyEnd) { var decrypted = new byte[encrypted.Length]; int ki = keyStart; @@ -527,6 +641,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 +685,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } } - public MethodsDecrypter(DecrypterInfo decrypterInfo) { + public MethodsDecrypter(ModuleDef module, DecrypterInfo decrypterInfo) { + this.module = module; this.decrypterInfo = decrypterInfo; } @@ -565,7 +705,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; diff --git a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs index b5109c75..2922361f 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,20 @@ 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; + break; + } + } + + bool CheckMcKeyRva(MyPEImage peImage, uint newXorKey) { + xorKey = newXorKey; + uint rva = GetMcKeyRva(); + return (rva & 0xFFF) == 0 && peImage.FindSection((RVA)rva) != null; } public uint GetMcKeyRva() { From 8808c049f1aced1699ec1326b96d91d76358beab Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 21 Oct 2013 07:19:39 +0200 Subject: [PATCH 04/13] Clear old data if we re-try decryption --- de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs index 179aca66..ea296afc 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs @@ -284,6 +284,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); From 730505fd4f573ef12fa018f567fe3884afea1645 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 21 Oct 2013 07:29:20 +0200 Subject: [PATCH 05/13] Write a message if decryption probably failed --- .../deobfuscators/MaxtoCode/MethodsDecrypter.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs index ea296afc..27cfb8b8 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs @@ -255,15 +255,25 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } } + 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) { From 511a061f64bf4441557c07d8494aee876703ea7d Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 27 Oct 2013 19:40:51 +0100 Subject: [PATCH 06/13] Support latest Eazfuscator.NET --- de4dot.code/de4dot.code.csproj | 3 +- .../Eazfuscator_NET/Deobfuscator.cs | 10 + .../DynamicDynocodeIterator.cs | 109 +++++++ .../deobfuscators/Eazfuscator_NET/Dynocode.cs | 289 ------------------ .../Eazfuscator_NET/DynocodeService.cs | 100 ++++++ .../ResourceMethodsRestorer.cs | 5 +- .../Eazfuscator_NET/StringDecrypter.cs | 63 +++- .../Eazfuscator_NET/VersionDetector.cs | 2 +- .../deobfuscators/MethodCallRestorerBase.cs | 9 + 9 files changed, 287 insertions(+), 303 deletions(-) create mode 100644 de4dot.code/deobfuscators/Eazfuscator_NET/DynamicDynocodeIterator.cs delete mode 100644 de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs create mode 100644 de4dot.code/deobfuscators/Eazfuscator_NET/DynocodeService.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 7c946ab2..be5b26b7 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -159,7 +159,8 @@ - + + 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/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; From eb284bf4bf2d9f0a1f9aa26f616c32324a52c3e0 Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 5 Nov 2013 13:52:58 +0100 Subject: [PATCH 07/13] Support .NET Reactor 4.7 --- .../dotNET_Reactor/v4/AssemblyResolver.cs | 1 - .../deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs | 13 ++++++++++--- .../dotNET_Reactor/v4/EncryptedResource.cs | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) 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 360c0e9d..b0f2fd22 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -32,7 +32,7 @@ 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; @@ -366,16 +366,23 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { 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 9: return DeobfuscatorInfo.THE_NAME + " 4.5"; - case 10:return DeobfuscatorInfo.THE_NAME + " 4.6"; + case 10: + if (!hasSymmetricAlgorithm) + return DeobfuscatorInfo.THE_NAME + " 4.6"; + return DeobfuscatorInfo.THE_NAME + " 4.7"; } } else { switch (numIntPtrSizeCompares) { case 8: return DeobfuscatorInfo.THE_NAME + " 4.5"; - case 9: return DeobfuscatorInfo.THE_NAME + " 4.6"; + case 9: + if (!hasSymmetricAlgorithm) + return DeobfuscatorInfo.THE_NAME + " 4.6"; + return DeobfuscatorInfo.THE_NAME + " 4.7"; } } 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) From 85c565fc20565d7cc80eca4ea7b460f5a1ac35ec Mon Sep 17 00:00:00 2001 From: de4dot Date: Wed, 6 Nov 2013 03:20:44 +0100 Subject: [PATCH 08/13] Support more MaxtoCode runtimes --- .../deobfuscators/MaxtoCode/EncryptionInfos.cs | 9 +++++++++ .../MaxtoCode/MethodsDecrypter.cs | 18 ++++++++++++++++++ .../deobfuscators/MaxtoCode/PeHeader.cs | 2 ++ 3 files changed, 29 insertions(+) diff --git a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs index 4baf7b93..f5efc42e 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/EncryptionInfos.cs @@ -110,6 +110,13 @@ namespace de4dot.code.deobfuscators.MaxtoCode { MagicHi = 0x624ECDA3, Version = EncryptionVersion.V8, }, + // 526BC020 + // 526BDD12 + new EncryptionInfo { + MagicLo = 0x9A683B87, + MagicHi = 0x928ECDA3, + Version = EncryptionVersion.V8, + }, }; public static readonly EncryptionInfo[] McKey8C0h = new EncryptionInfo[] { @@ -168,6 +175,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode { // 51413BD8 // 51413D68 // 5166DB4F + // 526BC020 + // 526BDD12 new EncryptionInfo { MagicLo = 0x1A731B13, MagicHi = 0x1723891F, diff --git a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs index 27cfb8b8..b312d2a5 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs @@ -192,6 +192,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode { 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 })); + 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: @@ -398,6 +400,10 @@ namespace de4dot.code.deobfuscators.MaxtoCode { 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++) { @@ -532,6 +538,10 @@ namespace de4dot.code.deobfuscators.MaxtoCode { 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]; @@ -585,6 +595,10 @@ namespace de4dot.code.deobfuscators.MaxtoCode { 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; @@ -618,6 +632,10 @@ namespace de4dot.code.deobfuscators.MaxtoCode { 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; diff --git a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs index 2922361f..5eeeb246 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/PeHeader.cs @@ -70,6 +70,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode { break; if (CheckMcKeyRva(peImage, 0x18ABA931)) break; + if (CheckMcKeyRva(peImage, 0x18ABA933)) + break; break; } } From d4ff713b95a48664c7c224768983605c9183eb68 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 7 Nov 2013 03:25:52 +0100 Subject: [PATCH 09/13] Update DNR name regex --- de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index b0f2fd22..3a24139d 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -178,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)) From b88ecc8a6300c8da5a928ff5decca38ec99d7014 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 14 Nov 2013 10:48:07 +0100 Subject: [PATCH 10/13] Update name regex --- de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs index 63e5ffbe..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() From 239bfbfc2b78f7cf895344046dcad017beadc7ba Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 14 Nov 2013 13:38:57 +0100 Subject: [PATCH 11/13] Add known timestamps --- .../MaxtoCode/MethodsDecrypter.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs index b312d2a5..ccda3adb 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs @@ -170,28 +170,28 @@ namespace de4dot.code.deobfuscators.MaxtoCode { void InitializeDecrypter() { switch (GetVersion()) { case EncryptionVersion.V1: - decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v1, Decrypt4_v1, Decrypt2_v1, Decrypt3_v1, Decrypt5, Decrypt6, Decrypt7 })); - decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v1, Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt5, Decrypt6, Decrypt7 })); + 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(new DecryptFunc[] { Decrypt3_v1, Decrypt2_v1, Decrypt1_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 })); break; - case EncryptionVersion.V3: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt1_v1, Decrypt2_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 })); break; - case EncryptionVersion.V4: decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt2_v1, Decrypt1_v1, Decrypt3_v1, Decrypt4_v1, Decrypt5, Decrypt6, Decrypt7 })); 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(new DecryptFunc[] { Decrypt4_v1, Decrypt2_v1, Decrypt3_v1, Decrypt1_v1, Decrypt5, Decrypt6, Decrypt7 })); - decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v2, Decrypt2_v2, Decrypt3_v2, Decrypt1_v2, Decrypt6, Decrypt7, Decrypt5 })); - decrypters.Add(new Decrypter(new DecryptFunc[] { Decrypt4_v3, Decrypt2_v3, Decrypt3_v3, Decrypt1_v3, Decrypt6, Decrypt7, Decrypt5 })); + 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(new DecryptFunc[] { Decrypt4_v4, Decrypt2_v4, Decrypt3_v4, Decrypt1_v4, Decrypt6, Decrypt7, Decrypt5 })); 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 })); 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 })); + 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; @@ -244,8 +244,8 @@ namespace de4dot.code.deobfuscators.MaxtoCode { } bool InitializeInfos2() { - if (decrypters.Count > 1) { - uint rtTimeStamp = GetRuntimeTimeStamp(); + uint rtTimeStamp = GetRuntimeTimeStamp(); + if (rtTimeStamp != 0) { var decrypter = GetCorrectDecrypter(rtTimeStamp); if (decrypter != null) { try { From 5d6db10ba47b5601cc4041ff2a894787b9d7a1cb Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 14 Nov 2013 22:48:44 +0100 Subject: [PATCH 12/13] Don't decrypt already decrypted resources --- de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs index ccda3adb..af2489a7 100644 --- a/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MaxtoCode/MethodsDecrypter.cs @@ -765,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) { From 48f31acb7eb0c75ee3d9f47b54be1f6f0f6129ca Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 15 Nov 2013 14:44:20 +0100 Subject: [PATCH 13/13] Detect some other DNR 4.5 version --- de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index 3a24139d..70e24bca 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -369,6 +369,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { 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) @@ -378,6 +379,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { } else { switch (numIntPtrSizeCompares) { + case 6: case 8: return DeobfuscatorInfo.THE_NAME + " 4.5"; case 9: if (!hasSymmetricAlgorithm)