/* Copyright (C) 2011-2015 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.IO; using dnlib.IO; using dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; namespace de4dot.code.deobfuscators.CodeVeil { class ResourceDecrypter { ModuleDefMD module; TypeDef encryptedResourceStreamType; TypeDef encryptedResourceSetType; MethodDef encryptedResourceSet_GetDefaultReader; TypeDef encryptedResourceReaderType; GenericInstSig encryptedResourceReaderTypeDict; TypeDef resType; MethodDef resTypeCtor; TypeDef resourceFlagsType; TypeDef resourceEnumeratorType; MethodCallRestorerBase methodsRestorer; public bool CanRemoveTypes { get { return EncryptedResourceStreamType != null && EncryptedResourceSetType != null && EncryptedResourceReaderType != null && ResType != null && ResourceFlagsType != null && ResourceEnumeratorType != null; } } public TypeDef EncryptedResourceStreamType { get { return encryptedResourceStreamType; } } public TypeDef EncryptedResourceSetType { get { return encryptedResourceSetType; } } public TypeDef EncryptedResourceReaderType { get { return encryptedResourceReaderType; } } public TypeDef ResType { get { return resType; } } public TypeDef ResourceFlagsType { get { return resourceFlagsType; } } public TypeDef ResourceEnumeratorType { get { return resourceEnumeratorType; } } public ResourceDecrypter(ModuleDefMD module) { this.module = module; } public void Initialize() { methodsRestorer = new MethodCallRestorerBase(module); FindEncryptedResourceStreamType(); FindEncryptedResourceSet(); FindEncryptedResourceReader(); FindResType(); FindResourceFlags(); FindResourceEnumerator(); } 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.FullName != "System.Object") continue; if (!HasInterface(type, "System.Collections.IDictionaryEnumerator")) continue; if (!new FieldTypes(type).All(resourceEnumeratorType_fields)) continue; var ctor = type.FindMethod(".ctor"); if (ctor == null) continue; var sig = ctor.MethodSig; if (sig == null || sig.Params.Count != 1) continue; if (sig.Params[0].TryGetTypeDef() != encryptedResourceReaderType) continue; resourceEnumeratorType = type; return; } } void FindResourceFlags() { if (resTypeCtor == null) return; var sig = resTypeCtor.MethodSig; if (sig == null || sig.Params.Count != 4) return; var type = sig.Params[2].TryGetTypeDef(); 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].TryGetTypeDef(); if (type == null) return; if (type.BaseType == null || type.BaseType.FullName != "System.Object") return; var ctor = type.FindMethod(".ctor"); if (ctor == null) return; var sig = ctor.MethodSig; if (sig == null || sig.Params.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(TypeDef type, string interfaceFullName) { foreach (var iface in type.Interfaces) { if (iface.Interface.FullName == interfaceFullName) return true; } return false; } static GenericInstSig GetDlxResDict(TypeDef type) { foreach (var field in type.Fields) { var fieldType = field.FieldSig.GetFieldType().ToGenericInstSig(); if (fieldType == null) continue; if (fieldType.GenericType.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].TryGetTypeDef() == null) continue; return fieldType; } return null; } static TypeDef GetTypeFromCode(MethodDef 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 TypeDef; 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 = type.FindMethod(".ctor"); if (!DotNetUtils.IsMethod(ctor, "System.Void", "(System.Resources.IResourceReader)")) continue; var method = type.FindMethod("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 = type.FindMethod(".ctor"); if (!DotNetUtils.IsMethod(ctor, "System.Void", "(System.IO.Stream)")) continue; if (!new FieldTypes(type).All(encryptedResourceStreamType_fields)) continue; if (FindXxteaMethod(type) == null) continue; MethodDef getManifestResourceStreamMethodTmp1, getManifestResourceStreamMethodTmp2; if (!FindManifestResourceStreamMethods(type, out getManifestResourceStreamMethodTmp1, out getManifestResourceStreamMethodTmp2)) continue; methodsRestorer.CreateGetManifestResourceStream1(getManifestResourceStreamMethodTmp1); methodsRestorer.CreateGetManifestResourceStream2(getManifestResourceStreamMethodTmp2); encryptedResourceStreamType = type; return; } } static MethodDef FindXxteaMethod(TypeDef 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[])")) { if (!DeobUtils.HasInteger(method, 0x9E3779B9)) continue; } else if (DotNetUtils.IsMethod(method, "System.Void", "(System.UInt32[],System.UInt32[],System.UInt32,System.UInt32,System.UInt32,System.UInt32,System.UInt32,System.UInt32,System.UInt32,System.UInt32,System.UInt32)")) { // Here if 5.0. 0x9E3779B9 is passed to it as the last arg. } else continue; if (!DeobUtils.HasInteger(method, 52)) continue; return method; } return null; } static bool FindManifestResourceStreamMethods(TypeDef type, out MethodDef getManifestResourceStreamMethodTmp1, out MethodDef getManifestResourceStreamMethodTmp2) { getManifestResourceStreamMethodTmp1 = null; getManifestResourceStreamMethodTmp2 = null; foreach (var method in type.Methods) { if (!method.IsStatic || method.Body == null) continue; 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; if (resource == null) continue; var decrypted = Decrypt(resource.Data); if (decrypted == null) continue; Logger.v("Decrypted resource {0}", Utils.ToCsharpString(resource.Name)); module.Resources[i] = new EmbeddedResource(resource.Name, decrypted, resource.Attributes); } } byte[] Decrypt(IBinaryReader reader) { try { reader.Position = 0; uint sig = reader.ReadUInt32(); reader.Position = 0; if (sig == 0xBEEFCACE) return DecryptBeefcace(reader); if (sig == 0x58455245) return DecryptErex(reader); return null; } catch (InvalidDataException) { return null; } catch (Exception ex) { Logger.w("Got an exception when decrypting resources: {0} - {1}", ex.GetType(), ex.Message); return null; } } byte[] DecryptBeefcace(IBinaryReader reader) { var resourceReader = new ResourceReader(reader); return new ResourceConverter(module, resourceReader.Read()).Convert(); } byte[] DecryptErex(IBinaryReader reader) { return new ErexResourceReader(reader).Decrypt(); } public void Deobfuscate(Blocks blocks) { if (encryptedResourceStreamType == null) return; methodsRestorer.Deobfuscate(blocks); } } }