diff --git a/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs b/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs index 35e380f4..7466a113 100644 --- a/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs +++ b/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs @@ -19,6 +19,8 @@ using System; using System.Collections.Generic; +using System.IO; +using SevenZip.Compression.LZMA; using dnlib.IO; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -171,5 +173,84 @@ namespace de4dot.code.deobfuscators.Confuser { } return count; } + + public static byte[] SevenZipDecompress(byte[] data) { + var reader = new BinaryReader(new MemoryStream(data)); + var props = reader.ReadBytes(5); + var decoder = new Decoder(); + decoder.SetDecoderProperties(props); + long totalSize = reader.ReadInt64(); + long compressedSize = data.Length - props.Length - 8; + var decompressed = new byte[totalSize]; + decoder.Code(reader.BaseStream, new MemoryStream(decompressed, true), compressedSize, totalSize, null); + return decompressed; + } + + // Finds the Lzma type by finding an instruction that allocates a new Lzma.Decoder + public static TypeDef FindLzmaType(MethodDef method) { + if (method == null || method.Body == null) + return null; + + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Newobj) + continue; + var ctor = instr.Operand as MethodDef; + if (ctor == null) + continue; + var ctorType = ctor.DeclaringType; + if (ctorType == null) + continue; + if (!IsLzmaType(ctorType.DeclaringType)) + continue; + + return ctorType.DeclaringType; + } + + return null; + } + + static bool IsLzmaType(TypeDef type) { + if (type == null) + return false; + + if (type.NestedTypes.Count != 6) + return false; + if (!CheckLzmaMethods(type)) + return false; + if (FindLzmaOutWindowType(type.NestedTypes) == null) + return false; + + return true; + } + + static bool CheckLzmaMethods(TypeDef type) { + int methods = 0; + foreach (var m in type.Methods) { + if (m.IsStaticConstructor) + continue; + if (m.IsInstanceConstructor) { + if (m.MethodSig.GetParamCount() != 0) + return false; + continue; + } + if (!DotNetUtils.IsMethod(m, "System.UInt32", "(System.UInt32)")) + return false; + methods++; + } + return methods == 1; + } + + static readonly string[] outWindowFields = new string[] { + "System.Byte[]", + "System.UInt32", + "System.IO.Stream", + }; + static TypeDef FindLzmaOutWindowType(IEnumerable types) { + foreach (var type in types) { + if (new FieldTypes(type).Exactly(outWindowFields)) + return type; + } + return null; + } } } diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs index 97129915..3cf6d912 100644 --- a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs +++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs @@ -46,7 +46,7 @@ namespace de4dot.code.deobfuscators.Confuser { return null; } - public static FieldDef FindDataField(MethodDef method, TypeDef declaringType) { + public static FieldDef FindDataField_v18_r75367(MethodDef method, TypeDef declaringType) { var instrs = method.Body.Instructions; for (int i = 0; i < instrs.Count - 1; i++) { var callvirt = instrs[i]; @@ -70,6 +70,31 @@ namespace de4dot.code.deobfuscators.Confuser { return null; } + // Normal ("safe") mode only (not dynamic or native) + public static FieldDef FindDataField_v19_r77172(MethodDef method, TypeDef declaringType) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 1; i++) { + var ldloc = instrs[i]; + if (!ldloc.IsLdloc()) + continue; + var local = ldloc.GetLocal(method.Body.Variables); + if (local == null || local.Type.GetFullName() != "System.Byte[]") + continue; + + var stsfld = instrs[i + 1]; + if (stsfld.OpCode.Code != Code.Stsfld) + continue; + var field = stsfld.Operand as FieldDef; + if (field == null || field.DeclaringType != declaringType) + continue; + if (field.FieldType.FullName != "System.Byte[]") + continue; + + return field; + } + return null; + } + public static FieldDef FindStreamField(MethodDef method, TypeDef declaringType) { return FindStreamField(method, declaringType, "System.IO.Stream"); } diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV18.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV18.cs index ccd6a61d..622e26d8 100644 --- a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV18.cs +++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV18.cs @@ -38,6 +38,7 @@ namespace de4dot.code.deobfuscators.Confuser { MethodDefAndDeclaringTypeDict decrypters = new MethodDefAndDeclaringTypeDict(); uint key0, key0d; MethodDef nativeMethod; + TypeDef lzmaType; EmbeddedResource resource; byte[] constants; ConfuserVersion version = ConfuserVersion.Unknown; @@ -50,6 +51,9 @@ namespace de4dot.code.deobfuscators.Confuser { v18_r75369_normal, v18_r75369_dynamic, v18_r75369_native, + v19_r77172_normal, + v19_r77172_dynamic, + v19_r77172_native, } public class DecrypterInfo { @@ -105,6 +109,9 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v18_r75369_normal: case ConfuserVersion.v18_r75369_dynamic: case ConfuserVersion.v18_r75369_native: + case ConfuserVersion.v19_r77172_normal: + case ConfuserVersion.v19_r77172_dynamic: + case ConfuserVersion.v19_r77172_native: return Hash1(key0l * magic); default: throw new ApplicationException("Invalid version"); @@ -151,6 +158,10 @@ namespace de4dot.code.deobfuscators.Confuser { get { return nativeMethod; } } + public TypeDef LzmaType { + get { return lzmaType; } + } + public EmbeddedResource Resource { get { return resource; } } @@ -177,7 +188,9 @@ namespace de4dot.code.deobfuscators.Confuser { if ((dictField = ConstantsDecrypterUtils.FindDictField(cctor, cctor.DeclaringType)) == null) return; - if ((dataField = ConstantsDecrypterUtils.FindDataField(cctor, cctor.DeclaringType)) == null) + + if ((dataField = ConstantsDecrypterUtils.FindDataField_v18_r75367(cctor, cctor.DeclaringType)) == null && + (dataField = ConstantsDecrypterUtils.FindDataField_v19_r77172(cctor, cctor.DeclaringType)) == null) return; nativeMethod = FindNativeMethod(cctor, cctor.DeclaringType); @@ -189,8 +202,13 @@ namespace de4dot.code.deobfuscators.Confuser { var info = new DecrypterInfo(this, method, ConfuserVersion.Unknown); if (FindKeys_v18_r75367(info)) InitVersion(cctor, ConfuserVersion.v18_r75367_normal, ConfuserVersion.v18_r75367_dynamic, ConfuserVersion.v18_r75367_native); - else if (FindKeys_v18_r75369(info)) - InitVersion(cctor, ConfuserVersion.v18_r75369_normal, ConfuserVersion.v18_r75369_dynamic, ConfuserVersion.v18_r75369_native); + else if (FindKeys_v18_r75369(info)) { + lzmaType = ConfuserUtils.FindLzmaType(cctor); + if (lzmaType == null) + InitVersion(cctor, ConfuserVersion.v18_r75369_normal, ConfuserVersion.v18_r75369_dynamic, ConfuserVersion.v18_r75369_native); + else + InitVersion(cctor, ConfuserVersion.v19_r77172_normal, ConfuserVersion.v19_r77172_dynamic, ConfuserVersion.v19_r77172_native); + } else return; @@ -380,6 +398,9 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v18_r75369_normal: case ConfuserVersion.v18_r75369_dynamic: case ConfuserVersion.v18_r75369_native: + case ConfuserVersion.v19_r77172_normal: + case ConfuserVersion.v19_r77172_dynamic: + case ConfuserVersion.v19_r77172_native: return FindKeys_v18_r75369(info); default: throw new ApplicationException("Invalid version"); @@ -541,18 +562,27 @@ namespace de4dot.code.deobfuscators.Confuser { return false; } + byte[] Decompress(byte[] compressed) { + if (lzmaType != null) + return ConfuserUtils.SevenZipDecompress(compressed); + return DeobUtils.Inflate(compressed, true); + } + byte[] DecryptResource(byte[] encrypted) { switch (version) { case ConfuserVersion.v18_r75367_normal: case ConfuserVersion.v18_r75369_normal: + case ConfuserVersion.v19_r77172_normal: return DecryptResource_v18_r75367_normal(encrypted); case ConfuserVersion.v18_r75367_dynamic: case ConfuserVersion.v18_r75369_dynamic: + case ConfuserVersion.v19_r77172_dynamic: return DecryptResource_v18_r75367_dynamic(encrypted); case ConfuserVersion.v18_r75367_native: case ConfuserVersion.v18_r75369_native: + case ConfuserVersion.v19_r77172_native: return DecryptResource_v18_r75367_native(encrypted); default: @@ -567,7 +597,7 @@ namespace de4dot.code.deobfuscators.Confuser { byte[] DecryptResource_v18_r75367_normal(byte[] encrypted) { var key = GetSigKey(); var decrypted = ConfuserUtils.Decrypt(BitConverter.ToUInt32(key, 12) * (uint)key0, encrypted); - return DeobUtils.Inflate(DeobUtils.AesDecrypt(decrypted, key, DeobUtils.Md5Sum(key)), true); + return Decompress(DeobUtils.AesDecrypt(decrypted, key, DeobUtils.Md5Sum(key))); } static int GetDynamicStartIndex(IList instrs, int ldlocIndex) { @@ -645,9 +675,7 @@ namespace de4dot.code.deobfuscators.Confuser { byte[] DecryptResource(byte[] encrypted, Func decryptFunc) { var key = GetSigKey(); - var decrypted = DeobUtils.AesDecrypt(encrypted, key, DeobUtils.Md5Sum(key)); - decrypted = DeobUtils.Inflate(decrypted, true); - + byte[] decrypted = DecryptAndDecompress(encrypted, key); var reader = MemoryImageStream.Create(decrypted); var result = new MemoryStream(); var writer = new BinaryWriter(result); @@ -659,6 +687,16 @@ namespace de4dot.code.deobfuscators.Confuser { return result.ToArray(); } + byte[] DecryptAndDecompress(byte[] encrypted, byte[] key) { + byte[] iv = DeobUtils.Md5Sum(key); + try { + return Decompress(DeobUtils.AesDecrypt(encrypted, key, iv)); + } + catch { + return DeobUtils.AesDecrypt(Decompress(encrypted), key, iv); + } + } + static bool VerifyGenericArg(MethodSpec gim, ElementType etype) { if (gim == null) return false; @@ -729,6 +767,13 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v18_r75369_dynamic: case ConfuserVersion.v18_r75369_native: minRev = 75369; + maxRev = 77124; + return true; + + case ConfuserVersion.v19_r77172_normal: + case ConfuserVersion.v19_r77172_dynamic: + case ConfuserVersion.v19_r77172_native: + minRev = 77172; maxRev = int.MaxValue; return true; diff --git a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs index f1809dad..1c22ac28 100644 --- a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs @@ -461,6 +461,7 @@ namespace de4dot.code.deobfuscators.Confuser { AddTypesToBeRemoved(constantsDecrypterV18.Types, "Constants decrypter type"); AddFieldsToBeRemoved(constantsDecrypterV18.Fields, "Constants decrypter field"); AddMethodToBeRemoved(constantsDecrypterV18.NativeMethod, "Constants decrypter native method"); + AddTypeToBeRemoved(constantsDecrypterV18.LzmaType, "LZMA type"); AddResourceToBeRemoved(constantsDecrypterV18.Resource, "Encrypted constants"); } @@ -516,6 +517,7 @@ namespace de4dot.code.deobfuscators.Confuser { AddResourceToBeRemoved(rsrc, "Encrypted resources"); AddMethodToBeRemoved(resourceDecrypter.Handler, "Resource decrypter handler"); AddFieldsToBeRemoved(resourceDecrypter.Fields, "Resource decrypter field"); + AddTypeToBeRemoved(resourceDecrypter.LzmaType, "LZMA type"); } void RemoveObfuscatorAttribute() { diff --git a/de4dot.code/deobfuscators/Confuser/ResourceDecrypter.cs b/de4dot.code/deobfuscators/Confuser/ResourceDecrypter.cs index fe288b45..24e11862 100644 --- a/de4dot.code/deobfuscators/Confuser/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/Confuser/ResourceDecrypter.cs @@ -30,6 +30,7 @@ namespace de4dot.code.deobfuscators.Confuser { ISimpleDeobfuscator simpleDeobfuscator; MethodDef handler; MethodDef installMethod; + TypeDef lzmaType; EmbeddedResource resource; Dictionary fields = new Dictionary(); byte key0, key1; @@ -42,6 +43,7 @@ namespace de4dot.code.deobfuscators.Confuser { v17_r73822, v18_r75367, v18_r75369, + v19_r77172, } public IEnumerable Fields { @@ -52,6 +54,10 @@ namespace de4dot.code.deobfuscators.Confuser { get { return handler; } } + public TypeDef LzmaType { + get { return lzmaType; } + } + public bool Detected { get { return handler != null; } } @@ -103,8 +109,13 @@ namespace de4dot.code.deobfuscators.Confuser { tmpVersion = ConfuserVersion.v17_r73822; else if (FindKey0_v18_r75367(tmpHandler, out key0) && FindKey1_v17_r73404(tmpHandler, out key1)) tmpVersion = ConfuserVersion.v18_r75367; - else if (FindKey0_v18_r75369(tmpHandler, out key0) && FindKey1_v18_r75369(tmpHandler, out key1)) - tmpVersion = ConfuserVersion.v18_r75369; + else if (FindKey0_v18_r75369(tmpHandler, out key0) && FindKey1_v18_r75369(tmpHandler, out key1)) { + lzmaType = ConfuserUtils.FindLzmaType(tmpHandler); + if (lzmaType == null) + tmpVersion = ConfuserVersion.v18_r75369; + else + tmpVersion = ConfuserVersion.v19_r77172; + } else return false; } @@ -343,6 +354,7 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v17_r73822: return Decrypt_v17_r73404(); case ConfuserVersion.v18_r75367: return Decrypt_v18_r75367(); case ConfuserVersion.v18_r75369: return Decrypt_v18_r75367(); + case ConfuserVersion.v19_r77172: return Decrypt_v19_r77172(); default: throw new ApplicationException("Unknown version"); } } @@ -381,6 +393,17 @@ namespace de4dot.code.deobfuscators.Confuser { return reader.ReadBytes(reader.ReadInt32()); } + byte[] Decrypt_v19_r77172() { + var encrypted = resource.GetResourceData(); + byte k = key0; + for (int i = 0; i < encrypted.Length; i++) { + encrypted[i] ^= k; + k *= key1; + } + var reader = new BinaryReader(new MemoryStream(ConfuserUtils.SevenZipDecompress(encrypted))); + return reader.ReadBytes(reader.ReadInt32()); + } + public void Deobfuscate(Blocks blocks) { if (blocks.Method != installMethod) return; @@ -415,6 +438,11 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v18_r75369: minRev = 75369; + maxRev = 77124; + return true; + + case ConfuserVersion.v19_r77172: + minRev = 77172; maxRev = int.MaxValue; return true; diff --git a/de4dot.code/deobfuscators/Confuser/Unpacker.cs b/de4dot.code/deobfuscators/Confuser/Unpacker.cs index 937954ca..1fb539c1 100644 --- a/de4dot.code/deobfuscators/Confuser/Unpacker.cs +++ b/de4dot.code/deobfuscators/Confuser/Unpacker.cs @@ -86,6 +86,7 @@ namespace de4dot.code.deobfuscators.Confuser { v17_r75076, v18_r75184, v18_r75367, + v19_r77172, } public bool Detected { @@ -170,8 +171,10 @@ namespace de4dot.code.deobfuscators.Confuser { theVersion = ConfuserVersion.v17_r75076; else if (module.Name == "Stub.exe") theVersion = ConfuserVersion.v18_r75184; - else + else if (!IsGetLenToPosStateMethodPrivate(type)) theVersion = ConfuserVersion.v18_r75367; + else + theVersion = ConfuserVersion.v19_r77172; } else if (IsDecryptMethod_v17_r73404(decyptMethod)) theVersion = ConfuserVersion.v17_r73404; @@ -201,6 +204,15 @@ namespace de4dot.code.deobfuscators.Confuser { version = theVersion; } + static bool IsGetLenToPosStateMethodPrivate(TypeDef type) { + foreach (var m in type.Methods) { + if (!DotNetUtils.IsMethod(m, "System.UInt32", "(System.UInt32)")) + continue; + return m.IsPrivate; + } + return false; + } + bool FindEntryPointToken(ISimpleDeobfuscator simpleDeobfuscator, MethodDef cctor, MethodDef entryPoint, out uint token) { token = 0; ulong @base; @@ -474,6 +486,7 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v17_r75076: return Decrypt_v17_r75076(data); case ConfuserVersion.v18_r75184: return Decrypt_v17_r75076(data); case ConfuserVersion.v18_r75367: return Decrypt_v17_r75076(data); + case ConfuserVersion.v19_r77172: return Decrypt_v17_r75076(data); default: throw new ApplicationException("Unknown version"); } } @@ -535,10 +548,10 @@ namespace de4dot.code.deobfuscators.Confuser { var reader = new BinaryReader(new MemoryStream(data)); byte[] key, iv; data = Decrypt_v15_r60785(reader, out key, out iv); - return SevenzipDecompress(DeobUtils.AesDecrypt(data, key, iv)); + return SevenZipDecompress(DeobUtils.AesDecrypt(data, key, iv)); } - static byte[] SevenzipDecompress(byte[] data) { + static byte[] SevenZipDecompress(byte[] data) { var reader = new BinaryReader(new MemoryStream(data)); int totalSize = reader.ReadInt32(); var props = reader.ReadBytes(5); @@ -628,6 +641,11 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v18_r75367: minRev = 75367; + maxRev = 77124; + return true; + + case ConfuserVersion.v19_r77172: + minRev = 77172; maxRev = int.MaxValue; return true; diff --git a/de4dot.code/deobfuscators/Confuser/VersionDetector.cs b/de4dot.code/deobfuscators/Confuser/VersionDetector.cs index 44cd040b..5d3ed330 100644 --- a/de4dot.code/deobfuscators/Confuser/VersionDetector.cs +++ b/de4dot.code/deobfuscators/Confuser/VersionDetector.cs @@ -43,7 +43,8 @@ namespace de4dot.code.deobfuscators.Confuser { 75306, 75318, 75349, 75367, 75369, 75402, 75459, 75461, 75573, 75719, 75720, 75725, 75806, 75807, 75926, 76101, 76119, 76163, 76186, 76271, 76360, 76509, 76542, 76548, - 76558, 76580, 76656, + 76558, 76580, 76656, 76871, 76923, 76924, 76933, 76934, + 76972, 76974, 77124, 77172, }; static Dictionary revToVersion = new Dictionary {