diff --git a/de4dot.code/deobfuscators/CodeVeil/Deobfuscator.cs b/de4dot.code/deobfuscators/CodeVeil/Deobfuscator.cs index b09a34fb..30d9926c 100644 --- a/de4dot.code/deobfuscators/CodeVeil/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/CodeVeil/Deobfuscator.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using Mono.Cecil; using Mono.MyStuff; +using de4dot.blocks; namespace de4dot.code.deobfuscators.CodeVeil { public class DeobfuscatorInfo : DeobfuscatorInfoBase { @@ -62,6 +63,7 @@ namespace de4dot.code.deobfuscators.CodeVeil { StringDecrypter stringDecrypter; AssemblyResolver assemblyResolver; TypeDefinition killType; + ResourceDecrypter resourceDecrypter; internal class Options : OptionsBase { } @@ -206,7 +208,17 @@ namespace de4dot.code.deobfuscators.CodeVeil { proxyDelegateFinder.initialize(); proxyDelegateFinder.find(); - new ResourceDecrypter(module).decrypt(); + resourceDecrypter = new ResourceDecrypter(module); + resourceDecrypter.initialize(); + resourceDecrypter.decrypt(); + if (resourceDecrypter.CanRemoveTypes) { + addTypeToBeRemoved(resourceDecrypter.ResourceFlagsType, "Obfuscator ResourceFlags type"); + addTypeToBeRemoved(resourceDecrypter.ResType, "Obfuscator Res type"); + addTypeToBeRemoved(resourceDecrypter.ResourceEnumeratorType, "Obfuscator ResourceEnumerator type"); + addTypeToBeRemoved(resourceDecrypter.EncryptedResourceReaderType, "Obfuscator EncryptedResourceReader type"); + addTypeToBeRemoved(resourceDecrypter.EncryptedResourceSetType, "Obfuscator EncryptedResourceSet type"); + addTypeToBeRemoved(resourceDecrypter.EncryptedResourceStreamType, "Obfuscator EncryptedResourceStream type"); + } } void removeTamperDetection() { @@ -224,13 +236,14 @@ namespace de4dot.code.deobfuscators.CodeVeil { addResourceToBeRemoved(assemblyResolver.BundleXmlFileResource, "Embedded assemblies XML file resource"); } - public override void deobfuscateMethodBegin(blocks.Blocks blocks) { + public override void deobfuscateMethodBegin(Blocks blocks) { proxyDelegateFinder.deobfuscate(blocks); base.deobfuscateMethodBegin(blocks); } - public override void deobfuscateMethodEnd(blocks.Blocks blocks) { + public override void deobfuscateMethodEnd(Blocks blocks) { mainType.removeInitCall(blocks); + resourceDecrypter.deobfuscate(blocks); base.deobfuscateMethodEnd(blocks); } diff --git a/de4dot.code/deobfuscators/CodeVeil/ResourceDecrypter.cs b/de4dot.code/deobfuscators/CodeVeil/ResourceDecrypter.cs index 37672f4b..24738b78 100644 --- a/de4dot.code/deobfuscators/CodeVeil/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/CodeVeil/ResourceDecrypter.cs @@ -20,15 +20,301 @@ using System; using System.IO; using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Metadata; +using de4dot.blocks; namespace de4dot.code.deobfuscators.CodeVeil { class ResourceDecrypter { ModuleDefinition module; + TypeDefinition encryptedResourceStreamType; + MethodDefinition getManifestResourceStreamMethod1; + MethodDefinition getManifestResourceStreamMethod2; + TypeDefinition encryptedResourceSetType; + MethodDefinition encryptedResourceSet_GetDefaultReader; + TypeDefinition encryptedResourceReaderType; + GenericInstanceType encryptedResourceReaderTypeDict; + TypeDefinition resType; + MethodDefinition resTypeCtor; + TypeDefinition resourceFlagsType; + TypeDefinition resourceEnumeratorType; + MethodReference Assembly_GetManifestResourceStream1; + MethodReference Assembly_GetManifestResourceStream2; + + public bool CanRemoveTypes { + get { + return EncryptedResourceStreamType != null && + EncryptedResourceSetType != null && + EncryptedResourceReaderType != null && + ResType != null && + ResourceFlagsType != null && + ResourceEnumeratorType != null; + } + } + + public TypeDefinition EncryptedResourceStreamType { + get { return encryptedResourceStreamType; } + } + + public TypeDefinition EncryptedResourceSetType { + get { return encryptedResourceSetType; } + } + + public TypeDefinition EncryptedResourceReaderType { + get { return encryptedResourceReaderType; } + } + + public TypeDefinition ResType { + get { return resType; } + } + + public TypeDefinition ResourceFlagsType { + get { return resourceFlagsType; } + } + + public TypeDefinition ResourceEnumeratorType { + get { return resourceEnumeratorType; } + } public ResourceDecrypter(ModuleDefinition module) { this.module = module; } + public void initialize() { + initGetManifestMethods(); + findEncryptedResourceStreamType(); + findEncryptedResourceSet(); + findEncryptedResourceReader(); + findResType(); + findResourceFlags(); + findResourceEnumerator(); + } + + void initGetManifestMethods() { + var assemblyType = new TypeReference("System.Reflection", "Assembly", module, module.TypeSystem.Corlib); + var typeType = new TypeReference("System", "Type", module, module.TypeSystem.Corlib); + var streamType = new TypeReference("System.IO", "Stream", module, module.TypeSystem.Corlib); + Assembly_GetManifestResourceStream1 = new MethodReference("GetManifestResourceStream", streamType, assemblyType); + Assembly_GetManifestResourceStream1.HasThis = true; + Assembly_GetManifestResourceStream1.Parameters.Add(new ParameterDefinition(module.TypeSystem.String)); + Assembly_GetManifestResourceStream2 = new MethodReference("GetManifestResourceStream", streamType, assemblyType); + Assembly_GetManifestResourceStream2.HasThis = true; + Assembly_GetManifestResourceStream2.Parameters.Add(new ParameterDefinition(typeType)); + Assembly_GetManifestResourceStream2.Parameters.Add(new ParameterDefinition(module.TypeSystem.String)); + } + + void findResourceEnumerator() { + if (encryptedResourceReaderType == null) + return; + + var resourceEnumeratorType_fields = new string[] { + "System.Collections.DictionaryEntry", + "System.Collections.IDictionaryEnumerator", + "System.Boolean", + encryptedResourceReaderType.FullName, + }; + foreach (var type in module.Types) { + if (type.Namespace != "") + continue; + if (type.BaseType == null || type.BaseType.EType != ElementType.Object) + continue; + if (!hasInterface(type, "System.Collections.IDictionaryEnumerator")) + continue; + if (!new FieldTypes(type).all(resourceEnumeratorType_fields)) + continue; + var ctor = DotNetUtils.getMethod(type, ".ctor"); + if (ctor == null) + continue; + if (ctor.Parameters.Count != 1) + continue; + if (ctor.Parameters[0].ParameterType != encryptedResourceReaderType) + continue; + + resourceEnumeratorType = type; + return; + } + } + + void findResourceFlags() { + if (resTypeCtor == null || resTypeCtor.Parameters.Count != 4) + return; + var type = resTypeCtor.Parameters[2].ParameterType as TypeDefinition; + if (type == null || !type.IsEnum) + return; + + resourceFlagsType = type; + } + + static string[] resType_fields = new string[] { + "System.Int32", + "System.Object", + "System.String", + }; + void findResType() { + if (encryptedResourceReaderTypeDict == null) + return; + var type = encryptedResourceReaderTypeDict.GenericArguments[1] as TypeDefinition; + if (type == null) + return; + if (type.BaseType == null || type.BaseType.EType != ElementType.Object) + return; + var ctor = DotNetUtils.getMethod(type, ".ctor"); + if (ctor == null || ctor.Parameters.Count != 4) + return; + + resTypeCtor = ctor; + resType = type; + } + + static string[] encryptedResourceReaderType_fields = new string[] { + "System.Boolean", + "System.Int32", + "System.Int64", + "System.IO.BinaryReader", + "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter", + }; + void findEncryptedResourceReader() { + var type = getTypeFromCode(encryptedResourceSet_GetDefaultReader); + if (type == null) + return; + if (type.BaseType == null || !hasInterface(type, "System.Resources.IResourceReader")) + return; + if (!new FieldTypes(type).all(encryptedResourceReaderType_fields)) + return; + var dictType = getDlxResDict(type); + if (dictType == null) + return; + if (findXxteaMethod(type) == null) + return; + + encryptedResourceReaderType = type; + encryptedResourceReaderTypeDict = dictType; + } + + static bool hasInterface(TypeDefinition type, string interfaceFullName) { + foreach (var iface in type.Interfaces) { + if (iface.FullName == interfaceFullName) + return true; + } + return false; + } + + static GenericInstanceType getDlxResDict(TypeDefinition type) { + foreach (var field in type.Fields) { + var fieldType = field.FieldType as GenericInstanceType; + if (fieldType == null) + continue; + if (fieldType.ElementType.FullName != "System.Collections.Generic.Dictionary`2") + continue; + if (fieldType.GenericArguments.Count != 2) + continue; + if (fieldType.GenericArguments[0].FullName != "System.String") + continue; + if (!(fieldType.GenericArguments[1] is TypeDefinition)) + continue; + return fieldType; + } + return null; + } + + static TypeDefinition getTypeFromCode(MethodDefinition method) { + if (method == null || method.Body == null) + return null; + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Ldtoken) + continue; + var type = instr.Operand as TypeDefinition; + if (type != null) + return type; + } + + return null; + } + + void findEncryptedResourceSet() { + foreach (var type in module.Types) { + if (type.Namespace != "") + continue; + if (type.BaseType == null || type.BaseType.FullName != "System.Resources.ResourceSet") + continue; + var ctor = DotNetUtils.getMethod(type, ".ctor"); + if (!DotNetUtils.isMethod(ctor, "System.Void", "(System.Resources.IResourceReader)")) + continue; + var method = DotNetUtils.getMethod(type, "GetDefaultReader"); + if (!DotNetUtils.isMethod(method, "System.Type", "()")) + continue; + if (method.Body == null || method.IsStatic || !method.IsVirtual) + continue; + + encryptedResourceSet_GetDefaultReader = method; + encryptedResourceSetType = type; + return; + } + } + + static string[] encryptedResourceStreamType_fields = new string[] { + "System.Byte", + "System.Byte[]", + "System.Boolean", + "System.Int32", + "System.Int64", + "System.IO.MemoryStream", + "System.IO.Stream", + "System.UInt32[]", + }; + void findEncryptedResourceStreamType() { + foreach (var type in module.Types) { + if (type.Namespace != "") + continue; + if (type.BaseType == null || type.BaseType.FullName != "System.IO.Stream") + continue; + var ctor = DotNetUtils.getMethod(type, ".ctor"); + if (!DotNetUtils.isMethod(ctor, "System.Void", "(System.IO.Stream)")) + continue; + if (!new FieldTypes(type).all(encryptedResourceStreamType_fields)) + continue; + if (findXxteaMethod(type) == null) + continue; + + MethodDefinition getManifestResourceStreamMethodTmp1, getManifestResourceStreamMethodTmp2; + if (!findManifestResourceStreamMethods(type, out getManifestResourceStreamMethodTmp1, out getManifestResourceStreamMethodTmp2)) + continue; + + getManifestResourceStreamMethod1 = getManifestResourceStreamMethodTmp1; + getManifestResourceStreamMethod2 = getManifestResourceStreamMethodTmp2; + encryptedResourceStreamType = type; + return; + } + } + + static MethodDefinition findXxteaMethod(TypeDefinition type) { + foreach (var method in type.Methods) { + if (!method.IsPrivate || method.IsStatic || method.Body == null) + continue; + if (!DotNetUtils.isMethod(method, "System.Void", "(System.UInt32[],System.UInt32[])")) + continue; + if (!DeobUtils.hasInteger(method, 0x9E3779B9)) + continue; + if (!DeobUtils.hasInteger(method, 52)) + continue; + + return method; + } + return null; + } + + static bool findManifestResourceStreamMethods(TypeDefinition type, out MethodDefinition getManifestResourceStreamMethodTmp1, out MethodDefinition getManifestResourceStreamMethodTmp2) { + getManifestResourceStreamMethodTmp1 = null; + getManifestResourceStreamMethodTmp2 = null; + foreach (var method in type.Methods) { + if (DotNetUtils.isMethod(method, "System.IO.Stream", "(System.Reflection.Assembly,System.String)")) + getManifestResourceStreamMethodTmp1 = method; + else if (DotNetUtils.isMethod(method, "System.IO.Stream", "(System.Reflection.Assembly,System.Type,System.String)")) + getManifestResourceStreamMethodTmp2 = method; + } + return getManifestResourceStreamMethodTmp1 != null && getManifestResourceStreamMethodTmp2 != null; + } + public void decrypt() { for (int i = 0; i < module.Resources.Count; i++) { var resource = module.Resources[i] as EmbeddedResource; @@ -61,7 +347,7 @@ namespace de4dot.code.deobfuscators.CodeVeil { return null; } catch (Exception ex) { - Log.w("Got an exception when decrypting resources: {0}", ex.GetType()); + Log.w("Got an exception when decrypting resources: {0} - {1}", ex.GetType(), ex.Message); return null; } } @@ -70,5 +356,29 @@ namespace de4dot.code.deobfuscators.CodeVeil { var resourceReader = new ResourceReader(reader); return new ResourceConverter(module, resourceReader.read()).convert(); } + + public void deobfuscate(Blocks blocks) { + foreach (var block in blocks.MethodBlocks.getAllBlocks()) { + var instrs = block.Instructions; + for (int i = 0; i < instrs.Count; i++) { + var call = instrs[i]; + if (call.OpCode.Code != Code.Call) + continue; + var calledMethod = call.Operand as MethodDefinition; + if (calledMethod == null) + continue; + + MethodReference newMethod = null; + if (calledMethod == getManifestResourceStreamMethod1) + newMethod = Assembly_GetManifestResourceStream1; + else if (calledMethod == getManifestResourceStreamMethod2) + newMethod = Assembly_GetManifestResourceStream2; + if (newMethod == null) + continue; + + instrs[i] = new Instr(Instruction.Create(OpCodes.Callvirt, newMethod)); + } + } + } } }