/* Copyright (C) 2011-2014 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.Collections.Generic; using dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; using de4dot.code.renamer; namespace de4dot.code.deobfuscators.Eazfuscator_NET { public class DeobfuscatorInfo : DeobfuscatorInfoBase { public const string THE_NAME = "Eazfuscator.NET"; public const string THE_TYPE = "ef"; const string DEFAULT_REGEX = @"!^[a-zA-Z]$&!^#=&!^dje_.+_ejd$&" + DeobfuscatorBase.DEFAULT_ASIAN_VALID_NAME_REGEX; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { } public override string Name { get { return THE_NAME; } } public override string Type { get { return THE_TYPE; } } public override IDeobfuscator CreateDeobfuscator() { return new Deobfuscator(new Deobfuscator.Options { ValidNameRegex = validNameRegex.Get(), }); } } class Deobfuscator : DeobfuscatorBase { Options options; string obfuscatorName = DeobfuscatorInfo.THE_NAME; bool detectedVersion = false; DecrypterType decrypterType; StringDecrypter stringDecrypter; AssemblyResolver assemblyResolver; ResourceResolver resourceResolver; ResourceMethodsRestorer resourceMethodsRestorer; internal class Options : OptionsBase { } public override string Type { get { return DeobfuscatorInfo.THE_TYPE; } } public override string TypeLong { get { return DeobfuscatorInfo.THE_NAME; } } public override string Name { get { return obfuscatorName; } } public Deobfuscator(Options options) : base(options) { this.options = options; } protected override int DetectInternal() { int val = 0; int sum = ToInt32(stringDecrypter.Detected) + ToInt32(assemblyResolver.Detected) + ToInt32(resourceResolver.Detected); if (sum > 0) val += 100 + 10 * (sum - 1); if (detectedVersion) val += 10; return val; } protected override void ScanForObfuscator() { decrypterType = new DecrypterType(module, DeobfuscatedFile); stringDecrypter = new StringDecrypter(module, decrypterType); stringDecrypter.Find(); assemblyResolver = new AssemblyResolver(module, decrypterType); assemblyResolver.Find(); resourceResolver = new ResourceResolver(module, assemblyResolver); resourceResolver.Find(); if (stringDecrypter.Detected) DetectVersion(); } void DetectVersion() { var version = new VersionDetector(module, stringDecrypter).Detect(); if (version == null) return; detectedVersion = true; obfuscatorName = DeobfuscatorInfo.THE_NAME + " " + version; } public override void DeobfuscateBegin() { base.DeobfuscateBegin(); stringDecrypter.Initialize(DeobfuscatedFile); staticStringInliner.Add(stringDecrypter.RealMethod, (method2, gim, args) => { return stringDecrypter.Decrypt((int)args[0]); }); DeobfuscatedFile.StringDecryptersAdded(); assemblyResolver.Initialize(DeobfuscatedFile, this); assemblyResolver.InitializeEmbeddedFiles(); AddModuleCctorInitCallToBeRemoved(assemblyResolver.InitMethod); resourceResolver.Initialize(DeobfuscatedFile, this); foreach (var info in resourceResolver.MergeResources()) AddResourceToBeRemoved(info.Resource, "Encrypted resources"); AddModuleCctorInitCallToBeRemoved(resourceResolver.InitMethod); resourceMethodsRestorer = new ResourceMethodsRestorer(module); if ((Operations.RenamerFlags & (RenamerFlags.RenameTypes | RenamerFlags.RenameNamespaces)) != 0) resourceMethodsRestorer.Find(DeobfuscatedFile, this); DumpEmbeddedAssemblies(); } void DumpEmbeddedAssemblies() { foreach (var info in assemblyResolver.AssemblyInfos) { DeobfuscatedFile.CreateAssemblyFile(info.Data, info.SimpleName, info.Extension); AddResourceToBeRemoved(info.Resource, string.Format("Embedded assembly: {0}", info.AssemblyFullName)); } } public override void DeobfuscateMethodEnd(Blocks blocks) { resourceMethodsRestorer.Deobfuscate(blocks); assemblyResolver.Deobfuscate(blocks); base.DeobfuscateMethodEnd(blocks); } public override void DeobfuscateEnd() { if (CanRemoveStringDecrypterType) { AddTypesToBeRemoved(stringDecrypter.Types, "String decrypter type"); AddTypeToBeRemoved(decrypterType.Type, "Decrypter type"); AddTypesToBeRemoved(stringDecrypter.DynocodeTypes, "Dynocode type"); AddResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings"); } stringDecrypter.CloseServer(); AddTypeToBeRemoved(assemblyResolver.Type, "Assembly resolver type"); AddTypeToBeRemoved(assemblyResolver.OtherType, "Assembly resolver other type"); AddTypeToBeRemoved(resourceResolver.Type, "Resource resolver type"); AddTypeToBeRemoved(resourceMethodsRestorer.Type, "GetManifestResourceStream type"); AddResourceToBeRemoved(resourceMethodsRestorer.Resource, "GetManifestResourceStream type resource"); FixInterfaces(); StringDecrypterBugWorkaround(); base.DeobfuscateEnd(); } void StringDecrypterBugWorkaround() { // There's a bug in Eazfuscator.NET when the VM and string encryption features are // enabled. The string decrypter's initialization code checks to make sure it's not // called by eg. a dynamic method. When it's called from the VM code, it is // called by MethodBase.Invoke() and the string decrypter antis set in causing it // to fail. // One way to work around this is to make sure the string decrypter has been called // once. That way, any VM code calling it won't trigger a failure. // We can put this code in ::.cctor() since it gets executed before any // other code. // Note that we can't call the string decrypter from ::.cctor() since // its DeclaringType property will return null (since it's the global type). We // must call another created class which calls the string decrypter. // You must use --dont-rename --keep-types --preserve-tokens and decrypt strings if (!Operations.KeepObfuscatorTypes || Operations.DecryptStrings == OpDecryptString.None || (Operations.RenamerFlags & (RenamerFlags.RenameNamespaces | RenamerFlags.RenameTypes)) != 0) return; if (stringDecrypter.ValidStringDecrypterValue == null) return; var newType = module.UpdateRowId(new TypeDefUser(Guid.NewGuid().ToString("B"), module.CorLibTypes.Object.TypeDefOrRef)); module.Types.Add(newType); var newMethod = module.UpdateRowId(new MethodDefUser("x", MethodSig.CreateStatic(module.CorLibTypes.Void), 0, MethodAttributes.Static | MethodAttributes.HideBySig)); newType.Methods.Add(newMethod); newMethod.Body = new CilBody(); newMethod.Body.MaxStack = 1; newMethod.Body.Instructions.Add(Instruction.CreateLdcI4(stringDecrypter.ValidStringDecrypterValue.Value)); newMethod.Body.Instructions.Add(OpCodes.Call.ToInstruction(stringDecrypter.Method)); newMethod.Body.Instructions.Add(OpCodes.Pop.ToInstruction()); newMethod.Body.Instructions.Add(OpCodes.Ret.ToInstruction()); var cctor = module.GlobalType.FindOrCreateStaticConstructor(); var blocks = new Blocks(cctor); var block = blocks.MethodBlocks.GetAllBlocks()[0]; block.Insert(0, OpCodes.Call.ToInstruction(newMethod)); IList allInstructions; IList allExceptionHandlers; blocks.GetCode(out allInstructions, out allExceptionHandlers); DotNetUtils.RestoreBody(cctor, allInstructions, allExceptionHandlers); } protected override void Dispose(bool disposing) { if (disposing) { if (stringDecrypter != null) stringDecrypter.Dispose(); } base.Dispose(disposing); } public override IEnumerable GetStringDecrypterMethods() { var list = new List(); if (stringDecrypter.Method != null) list.Add(stringDecrypter.Method.MDToken.ToInt32()); return list; } } }