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() {