From 6b4a46275754d87f18a93227bbca003704a2f943 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 14 Jan 2012 10:37:15 +0100 Subject: [PATCH] Support v3.0 --- .../deobfuscators/Babel_NET/Deobfuscator.cs | 2 +- .../Babel_NET/ResourceDecrypter.cs | 42 +++- .../Babel_NET/StringDecrypter.cs | 201 +++++++++++++----- 3 files changed, 191 insertions(+), 54 deletions(-) diff --git a/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs index d63580ed..550fd488 100644 --- a/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs @@ -175,7 +175,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { if (stringDecrypter.Resource != null) { Log.v("Adding string decrypter. Resource: {0}", Utils.toCsharpString(stringDecrypter.Resource.Name)); staticStringDecrypter.add(stringDecrypter.DecryptMethod, (method, args) => { - return stringDecrypter.decrypt((int)args[0]); + return stringDecrypter.decrypt(args); }); DeobfuscatedFile.stringDecryptersAdded(); } diff --git a/de4dot.code/deobfuscators/Babel_NET/ResourceDecrypter.cs b/de4dot.code/deobfuscators/Babel_NET/ResourceDecrypter.cs index 13623ccc..2294482b 100644 --- a/de4dot.code/deobfuscators/Babel_NET/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/Babel_NET/ResourceDecrypter.cs @@ -30,13 +30,51 @@ namespace de4dot.code.deobfuscators.Babel_NET { } public byte[] decrypt(byte[] encryptedData) { + if (isV30(encryptedData)) + return decryptV30(encryptedData); + return decryptV35(encryptedData); + } + + static bool isV30(byte[] data) { + return data.Length > 10 && data[0] == 8 && data[9] <= 1 && data[10] == 8; + } + + // v3.0 + byte[] decryptV30(byte[] encryptedData) { + byte[] key, iv; + var reader = new BinaryReader(new MemoryStream(encryptedData)); + bool isCompressed = getHeaderData30(reader, out key, out iv); + var data = DeobUtils.desDecrypt(encryptedData, + (int)reader.BaseStream.Position, + (int)(reader.BaseStream.Length - reader.BaseStream.Position), + key, iv); + if (isCompressed) + data = DeobUtils.inflate(data, true); + return data; + } + + bool getHeaderData30(BinaryReader reader, out byte[] key, out byte[] iv) { + iv = reader.ReadBytes(reader.ReadByte()); + bool hasEmbeddedKey = reader.ReadBoolean(); + if (hasEmbeddedKey) + key = reader.ReadBytes(reader.ReadByte()); + else { + key = new byte[reader.ReadByte()]; + Array.Copy(module.Assembly.Name.PublicKey, 0, key, 0, key.Length); + } + + reader.ReadBytes(reader.ReadInt32()); // hash + return true; + } + + // v3.5+ + byte[] decryptV35(byte[] encryptedData) { int index = 0; byte[] key, iv; bool isCompressed = getKeyIv(getHeaderData(encryptedData, ref index), out key, out iv); var data = DeobUtils.desDecrypt(encryptedData, index, encryptedData.Length - index, key, iv); if (isCompressed) data = DeobUtils.inflate(data, true); - return data; } @@ -59,7 +97,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { bool getKeyIv(byte[] headerData, out byte[] key, out byte[] iv) { var reader = new BinaryReader(new MemoryStream(headerData)); - // 3.5 doesn't have this field + // 3.0 - 3.5 don't have this field if (headerData[(int)reader.BaseStream.Position] != 8) { var license = reader.ReadString(); } diff --git a/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs index bc0ef1fe..4aed6c71 100644 --- a/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs @@ -17,18 +17,77 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; using System.IO; +using System.Text; using Mono.Cecil; using de4dot.blocks; namespace de4dot.code.deobfuscators.Babel_NET { class StringDecrypter { ModuleDefinition module; - Dictionary offsetToString = new Dictionary(); TypeDefinition decrypterType; - MethodDefinition decryptMethod; EmbeddedResource encryptedResource; + IDecrypterInfo decrypterInfo; + + interface IDecrypterInfo { + MethodDefinition Decrypter { get; } + bool NeedsResource { get; } + void initialize(ModuleDefinition module, EmbeddedResource resource); + string decrypt(object[] args); + } + + class DecrypterInfoV1 : IDecrypterInfo { + byte[] key; + + public MethodDefinition Decrypter { get; set; } + public bool NeedsResource { + get { return true; } + } + + public void initialize(ModuleDefinition module, EmbeddedResource resource) { + key = resource.GetResourceData(); + if (key.Length != 0x100) + throw new ApplicationException(string.Format("Unknown key length: {0}", key.Length)); + } + + public string decrypt(object[] args) { + return decrypt((string)args[0], (int)args[1]); + } + + string decrypt(string s, int k) { + var sb = new StringBuilder(s.Length); + byte b = key[(byte)k]; + foreach (var c in s) + sb.Append((char)(c ^ (b | k))); + return sb.ToString(); + } + } + + class DecrypterInfoV2 : IDecrypterInfo { + Dictionary offsetToString = new Dictionary(); + + public MethodDefinition Decrypter { get; set; } + public bool NeedsResource { + get { return true; } + } + + public void initialize(ModuleDefinition module, EmbeddedResource resource) { + var decrypted = new ResourceDecrypter(module).decrypt(resource.GetResourceData()); + var reader = new BinaryReader(new MemoryStream(decrypted)); + while (reader.BaseStream.Position < reader.BaseStream.Length) + offsetToString[(int)reader.BaseStream.Position] = reader.ReadString(); + } + + public string decrypt(object[] args) { + return decrypt((int)args[0]); + } + + string decrypt(int offset) { + return offsetToString[offset]; + } + } public bool Detected { get { return decrypterType != null; } @@ -39,7 +98,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { } public MethodDefinition DecryptMethod { - get { return decryptMethod; } + get { return decrypterInfo == null ? null : decrypterInfo.Decrypter; } } public EmbeddedResource Resource { @@ -52,58 +111,98 @@ namespace de4dot.code.deobfuscators.Babel_NET { public void find() { foreach (var type in module.Types) { - if (!isDecrypterType(type)) - continue; - - var method = DotNetUtils.getMethod(type, "System.String", "(System.Int32)"); - if (method == null) + var info = checkDecrypterType(type); + if (info == null) continue; decrypterType = type; - decryptMethod = method; + decrypterInfo = info; return; } } - bool isDecrypterType(TypeDefinition type) { + IDecrypterInfo checkDecrypterType(TypeDefinition type) { if (type.HasEvents) - return false; - if (type.NestedTypes.Count != 1) - return false; - if (type.Fields.Count != 0) - return false; + return null; + if (type.NestedTypes.Count > 2) + return null; + if (type.Fields.Count > 1) + return null; - var nested = type.NestedTypes[0]; + foreach (var nested in type.NestedTypes) { + var info = checkNested(type, nested); + if (info != null) + return info; + } + return null; + } + + IDecrypterInfo checkNested(TypeDefinition type, TypeDefinition nested) { if (nested.HasProperties || nested.HasEvents) - return false; - - if (nested.Fields.Count == 1) { - // 4.2+ (maybe 4.0+) - - if (!MemberReferenceHelper.compareTypes(nested.Fields[0].FieldType, nested)) - return false; - - if (DotNetUtils.getMethod(nested, "System.Reflection.Emit.MethodBuilder", "(System.Reflection.Emit.TypeBuilder)") == null) - return false; - } - else if (nested.Fields.Count == 2) { - // 3.5 and maybe earlier - - var field1 = nested.Fields[0]; - var field2 = nested.Fields[1]; - if (field1.FieldType.FullName != "System.Collections.Hashtable" && field2.FieldType.FullName != "System.Collections.Hashtable") - return false; - if (!MemberReferenceHelper.compareTypes(field1.FieldType, nested) && !MemberReferenceHelper.compareTypes(field2.FieldType, nested)) - return false; - } - else - return false; + return null; if (DotNetUtils.getMethod(nested, ".ctor") == null) - return false; - if (DotNetUtils.getMethod(nested, "System.String", "(System.Int32)") == null) - return false; + return null; + if (nested.Fields.Count == 1) { + // 4.0+ + + if (!MemberReferenceHelper.compareTypes(nested.Fields[0].FieldType, nested)) + return null; + + if (DotNetUtils.getMethod(nested, "System.Reflection.Emit.MethodBuilder", "(System.Reflection.Emit.TypeBuilder)") == null) + return null; + + var nestedDecrypter = DotNetUtils.getMethod(nested, "System.String", "(System.Int32)"); + if (nestedDecrypter == null || nestedDecrypter.IsStatic) + return null; + var decrypter = DotNetUtils.getMethod(type, "System.String", "(System.Int32)"); + if (decrypter == null || !decrypter.IsStatic) + return null; + + return new DecrypterInfoV2 { Decrypter = decrypter }; + } + else if (nested.Fields.Count == 2) { + // 3.0 - 3.5 + + if (checkFields(nested, "System.Collections.Hashtable", nested)) { + // 3.5 + var nestedDecrypter = DotNetUtils.getMethod(nested, "System.String", "(System.Int32)"); + if (nestedDecrypter == null || nestedDecrypter.IsStatic) + return null; + var decrypter = DotNetUtils.getMethod(type, "System.String", "(System.Int32)"); + if (decrypter == null || !decrypter.IsStatic) + return null; + + return new DecrypterInfoV2 { Decrypter = decrypter }; + } + else if (checkFields(nested, "System.Byte[]", nested)) { + // 3.0 + var nestedDecrypter = DotNetUtils.getMethod(nested, "System.String", "(System.String,System.Int32)"); + if (nestedDecrypter == null || nestedDecrypter.IsStatic) + return null; + var decrypter = DotNetUtils.getMethod(type, "System.String", "(System.String,System.Int32)"); + if (decrypter == null || !decrypter.IsStatic) + return null; + + return new DecrypterInfoV1 { Decrypter = decrypter }; + } + else + return null; + } + + return null; + } + + bool checkFields(TypeDefinition type, string fieldType1, TypeDefinition fieldType2) { + if (type.Fields.Count != 2) + return false; + if (type.Fields[0].FieldType.FullName != fieldType1 && + type.Fields[1].FieldType.FullName != fieldType1) + return false; + if (!MemberReferenceHelper.compareTypes(type.Fields[0].FieldType, fieldType2) && + !MemberReferenceHelper.compareTypes(type.Fields[1].FieldType, fieldType2)) + return false; return true; } @@ -112,14 +211,14 @@ namespace de4dot.code.deobfuscators.Babel_NET { return; if (encryptedResource != null) return; - encryptedResource = findResource(); - if (encryptedResource == null) - return; - var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData()); - var reader = new BinaryReader(new MemoryStream(decrypted)); - while (reader.BaseStream.Position < reader.BaseStream.Length) - offsetToString[(int)reader.BaseStream.Position] = reader.ReadString(); + if (decrypterInfo.NeedsResource) { + encryptedResource = findResource(); + if (encryptedResource == null) + return; + } + + decrypterInfo.initialize(module, encryptedResource); } EmbeddedResource findResource() { @@ -133,8 +232,8 @@ namespace de4dot.code.deobfuscators.Babel_NET { return null; } - public string decrypt(int offset) { - return offsetToString[offset]; + public string decrypt(object[] args) { + return decrypterInfo.decrypt(args); } } }