Example #1
0
        public static void Main(string[] args)
        {
            Console.WriteLine ("Assembly-CSharp Deobfuscator for 7 Days to Die [by the 7 Days to Die Modding Community]");

            ownFolder = Path.GetDirectoryName(Path.GetFullPath(Assembly.GetEntryAssembly ().Location));
            if (ownFolder == null) {
                ErrorExit("Unable to retrieve the folder containing Deobfuscator!");
                return;
            }
            bool verbosity = false;//(args.Length > 1) ? (args[0].ToLower().Equals("-v")) : false;
            if (File.Exists (Path.Combine(ownFolder, "config.xml")))
            {
                XmlDocument configDoc = new XmlDocument ();
                try {
                    configDoc.Load(Path.Combine(ownFolder, "config.xml"));
                } catch (Exception e) {
                    Console.WriteLine(Logger.Level_ToString(Logger.Level.WARNING) + "Unable to load config.xml : " + e.ToString ());
                }
                XmlNodeList configElems = configDoc.DocumentElement.ChildNodes;
                foreach (XmlNode curElem in configElems) {
                    if (!curElem.Name.ToLower().Equals("verbosity"))
                        continue;
                    XmlNode verbosityElem = curElem;
                    XmlAttributeCollection verbosityAttrs = verbosityElem.Attributes;
                    foreach (XmlNode curAttr in verbosityAttrs) {
                        if (curAttr.Name.ToLower().Equals("enabled")) {
                            verbosity = curAttr.Value.ToLower().Equals("true");
                            break;
                        }
                    }
                }
            }
            else
                Console.WriteLine(Path.Combine(ownFolder, "config.xml"));
            mainLogger = new Logger (Path.Combine(ownFolder, "mainlog.txt"), null, (int)(verbosity ? Logger.Level.INFO : Logger.Level.KEYINFO));
            mainLogger.Info("Started logging to mainlog.txt.");

            if ( args.Length == 0 || !args[0].ToLower().EndsWith(".dll") )
            {
                mainLogger.Write("Usage : deobfuscate \"<path to file>\"");
                mainLogger.Write("Alternatively, you can drag and drop file into deobfuscate.");
                ErrorExit("", 2);
            }
            var acsharpSource = Path.GetFullPath(args [0]);
            if (!File.Exists (acsharpSource)) {
                ErrorExit("Unable to retrieve the folder containing " + args[0]);
                return;
            }
            sourceAssemblyPath = acsharpSource;

            string patchersPath = Path.Combine(ownFolder, "patchers");
            if (!Directory.Exists (patchersPath)) {
                Directory.CreateDirectory (patchersPath);
            }

            DefaultAssemblyResolver resolver = new DefaultAssemblyResolver ();
            resolver.AddSearchDirectory (Path.GetDirectoryName(acsharpSource));

            AssemblyDefinition csharpDef = null;
            AssemblyDefinition mscorlibDef = null;

            try {
                csharpDef = AssemblyDefinition.ReadAssembly (acsharpSource, new ReaderParameters{ AssemblyResolver = resolver });
            } catch (Exception e) {
                ErrorExit("Unable to load " + args[0] + " :" + e);
                return;
            }
            try {
                mscorlibDef = AssemblyDefinition.ReadAssembly(Path.Combine(Path.GetDirectoryName(acsharpSource), "mscorlib.dll"), new ReaderParameters{ AssemblyResolver = resolver });
            } catch (Exception e) {
                mainLogger.Warning("Unable to load mscorlib.dll :" + e);
            }
            int csharpFileLen = (int)new FileInfo(acsharpSource).Length;
            if (csharpDef.Modules.Count == 0)
            {
                ErrorExit(args[0] + " is invalid!");
            }
            ModuleDefinition csharpModule = csharpDef.Modules[0];
            if (csharpModule.GetType("Deobfuscated") != null)
            {
                ErrorExit(args[0] + " already is deobfuscated!");
            }

            mainLogger.KeyInfo("Deobfuscating " + args[0] + "...");
            mainLogger.Write("___");
            string[] files = Directory.GetFiles(patchersPath, "*.dll");
            var patchers = new List<Patcher>();
            foreach (var file in files)
            {
                Assembly patcherAssembly;
                try
                {
                    patcherAssembly = Assembly.LoadFrom(file);
                }
                catch (Exception e)
                {
                    mainLogger.Error("Unable to load the patcher " + file + " :");
                    mainLogger.Error(e.ToString());
                    continue;
                }
                Type exttype = typeof (Patcher);
                Type extensiontype = null;
                foreach (var type in patcherAssembly.GetExportedTypes())
                {
                    if (exttype.IsAssignableFrom(type))
                    {
                        extensiontype = type;
                        break;
                    }
                }
                if (extensiontype == null)
                {
                    mainLogger.Error("Failed to load patcher " + file + " (Specified assembly does not implement an Patcher class)");
                    continue;
                }

                // Create and register the extension
                try
                {
                    var patcher = Activator.CreateInstance(extensiontype, new object[0]) as Patcher;
                    if (patcher != null)
                        patchers.Add(patcher);
                }
                catch (Exception e)
                {
                    mainLogger.Error("Unable to instantiate the patcher class " + file + " :");
                    mainLogger.Error(e.ToString());
                }
            }
            if (patchers.Count == 0)
            {
                ErrorExit("There are no patches to apply! Exiting.", 3);
                return;
            }
            foreach (var patcher in patchers)
            {
                mainLogger.KeyInfo("Executing patcher \"" + patcher.Name + "\" (by " + string.Join(",", patcher.Authors) + ")...");
                Logger curLogger = new Logger(Path.Combine(ownFolder, "log_" + patcher.Name + ".txt"), null, (int) (verbosity ? Logger.Level.INFO : Logger.Level.KEYINFO));
                try
                {
                    patcher.Patch(curLogger, csharpDef, null);
                }
                catch (TargetInvocationException e)
                {
                    mainLogger.Error("ERROR : Invoking the Patch method for " + patcher.Name + " resulted in an exception :");
                    mainLogger.Error(e.StackTrace);
                    mainLogger.Error(e.InnerException.StackTrace);
                }
                catch (Exception e)
                {
                    mainLogger.Error("ERROR : An exception occured while trying to invoke the Patch method of " + patcher.Name + " :");
                    mainLogger.Error(e.Message + Environment.NewLine + e.StackTrace);
                }
                curLogger.Close();
                mainLogger.Info("Writing the current Assembly-CSharp.dll to a MemoryStream...");
                var asmCSharpStream = new MemoryStream(csharpFileLen);
                csharpDef.Write(asmCSharpStream);
                mainLogger.Info("Reading the current Assembly-CSharp.dll from the MemoryStream...");
                asmCSharpStream.Seek(0, SeekOrigin.Begin);
                csharpDef = AssemblyDefinition.ReadAssembly(asmCSharpStream, new ReaderParameters {AssemblyResolver = resolver});
                asmCSharpStream.Close();
                csharpModule = csharpDef.Modules[0];
            }
            mainLogger.Write(); mainLogger.Write("___");

            if ((mscorlibDef != null) && (mscorlibDef.Modules.Count > 0))
            {
                csharpModule.Types.Add(new TypeDefinition("", "Deobfuscated", Mono.Cecil. TypeAttributes.Public, csharpDef.MainModule.TypeSystem.Object));
            }
            else
                mainLogger.Error("Unable to create the Deobufscated class!");
            string outputPath = Path.Combine(Path.GetDirectoryName(acsharpSource), Path.GetFileNameWithoutExtension(acsharpSource) + ".deobf.dll");
            mainLogger.KeyInfo ("Saving the new assembly to " + outputPath + " ...");
            try
            {
                csharpDef.Write (outputPath);
            }
            catch (Exception e)
            {
                ErrorExit ("Unable to save the assembly : " + e.Message + Environment.NewLine + e.StackTrace);
            }

            ErrorExit ("Success.", 0);
        }
Example #2
0
        public static void Apply(ModuleDefinition module, Logger logger)
        {
            int stat_obfCall = 0, stat_nullField = 0, stat_duppop = 0, stat_obfSwitch = 0, stat_obfIf = 0, stat_obfClasses = 0;

            //not as fancy as it could be; the alternate way would be checking the field's names (always 1 uppercase letter) but that method could more easily get outdated
            Dictionary<FieldDefinition, FieldRemoveInfo> suspectFields = new Dictionary<FieldDefinition, FieldRemoveInfo>();

            List<MethodDefinition> decryptMethods = new List<MethodDefinition>();
            MethodDefinition[] mdefs = HelperClass.findMembers<MethodDefinition>(module, null, true);
            for (int methodIndex = 0; methodIndex < mdefs.Length; methodIndex++)
            {
                MethodDefinition mdef = mdefs[methodIndex];
                if (mdef.HasBody)
                {
                    MethodBody mdefBody = mdef.Body;
                    for (int i = 0; i < mdefBody.Instructions.Count; i++)
                    {
                        Instruction instr = mdefBody.Instructions[i];
                        if (instr.OpCode == OpCodes.Call)
                        {
                            MethodReference mref = (MethodReference)instr.Operand;
                            MethodDefinition targetMethod = mref.Resolve();
                            //indicates that this is a method by the obfuscator that replaces single operations from other methods
                            if (targetMethod != null &&
                                targetMethod.IsStatic && targetMethod.DeclaringType.Namespace.Equals("A") &&
                                targetMethod.Body.Instructions.Count < 10 &&
                                targetMethod.DeclaringType.Methods.Count == 1 && targetMethod.DeclaringType.Fields.Count == 0)
                            {
                                //logger.Info(targetMethod.DeclaringType.FullName + "::" + targetMethod.Name + " -> " + mdef.DeclaringType.FullName + "::" + mdef.Name + " (@" + i + ")");
                                MethodBody targetBody = targetMethod.Body;
                                targetBody.SimplifyMacros();
                                bool canPatch = (targetBody.Instructions.Count > targetMethod.Parameters.Count) && !targetBody.HasVariables;
                                if (canPatch)
                                {
                                    for (int k = 0; k < targetMethod.Parameters.Count; k++)
                                    {
                                        if (targetBody.Instructions[k].OpCode != OpCodes.Ldarg || ((ParameterDefinition)targetBody.Instructions[k].Operand).Index != k)
                                        {
                                            canPatch = false;
                                            break;
                                        }
                                    }
                                    if (canPatch)
                                    {
                                        for (int k = targetMethod.Parameters.Count; k < targetBody.Instructions.Count; k++)
                                        {
                                            if (targetBody.Instructions[k].Operand is ParameterDefinition)
                                            {
                                                canPatch = false;
                                                break;
                                            }
                                        }
                                    }
                                }
                                if (!canPatch)
                                {
                                    logger.Warning("Cannot reimport the instructions from " + targetMethod.FullName + " (not yet supported)!");
                                    continue;
                                }
                                ILProcessor proc = mdefBody.GetILProcessor();
                                List<Instruction> targetInstructions = new List<Instruction>(targetBody.Instructions.Count-targetMethod.Parameters.Count);
                                int _k = 0; Instruction before = instr;
                                //create nop instructions to simplify creating jumps (the instruction and the operand will be changed afterwards)
                                for (int k = targetMethod.Parameters.Count; k < targetBody.Instructions.Count; k++, _k++)
                                {
                                    Instruction nopInstr = proc.Create(OpCodes.Nop);
                                    targetInstructions.Add(nopInstr);
                                    HelperClass.SafeInsertAfter(proc, before, nopInstr);
                                    //proc.InsertAfter(before, nopInstr);
                                    before = nopInstr;
                                }
                                _k = 0;
                                for (int k = targetMethod.Parameters.Count; k < targetBody.Instructions.Count; k++, _k++)
                                {
                                    Instruction curInstr = targetBody.Instructions[k];
                                    switch (curInstr.OpCode.Code)
                                    {
                                        case Mono.Cecil.Cil.Code.Ret://OpCodes.Ret.Code:
                                            HelperClass.SafeRemove(proc, targetInstructions[_k]);
                                            //proc.Remove(targetInstructions[_k]);
                                            targetInstructions.RemoveAt(_k);
                                            break;
                                        default:
                                            targetInstructions[_k].OpCode = curInstr.OpCode;
                                            if (curInstr.Operand is Instruction)
                                            {
                                                Instruction targetedInstruction = (Instruction)curInstr.Operand;
                                                int instrIndex = -1; int targetInstrIndex = -1;
                                                for (int l = targetMethod.Parameters.Count; l < targetBody.Instructions.Count; l++)
                                                {
                                                    if (targetBody.Instructions[l].Equals(targetedInstruction))
                                                    {
                                                        instrIndex = l;
                                                        targetInstrIndex = l - targetMethod.Parameters.Count;
                                                        break;
                                                    }
                                                }
                                                if (instrIndex == -1)
                                                    throw new Exception("Unable to find the target of instruction #" + k + " in " + targetMethod.FullName + "!");
                                                if (targetBody.Instructions[instrIndex].OpCode == OpCodes.Ret)
                                                    targetInstructions[_k].Operand = targetInstructions[targetInstructions.Count-1].Next;
                                                else
                                                    targetInstructions[_k].Operand = targetInstructions[targetInstrIndex];
                                            }
                                            else if (curInstr.Operand is Instruction[])
                                            {
                                                Instruction[] oldTargetList = (Instruction[])curInstr.Operand;
                                                Instruction[] newInstructions = new Instruction[oldTargetList.Length];
                                                for (int l = 0; l < oldTargetList.Length; l++)
                                                {
                                                    Instruction targetedInstruction = oldTargetList[l];
                                                    int instrIndex = -1; int targetInstrIndex = -1;
                                                    for (int m = targetMethod.Parameters.Count; m < targetBody.Instructions.Count; m++)
                                                    {
                                                        if (targetBody.Instructions[l].Equals(targetedInstruction))
                                                        {
                                                            instrIndex = l;
                                                            targetInstrIndex = l - targetMethod.Parameters.Count;
                                                            break;
                                                        }
                                                    }
                                                    if (instrIndex == -1)
                                                        throw new Exception("Unable to find the target of instruction #" + k + " in " + targetMethod.FullName + "!");
                                                    if (targetBody.Instructions[instrIndex].OpCode == OpCodes.Ret)
                                                        newInstructions[l] = targetInstructions[targetInstructions.Count-1].Next;
                                                    else
                                                        newInstructions[l] = targetInstructions[targetInstrIndex];
                                                }
                                                targetInstructions[_k].Operand = newInstructions;
                                            }
                                            else if (curInstr.Operand is MethodReference)
                                            {
                                                targetInstructions[_k].Operand = curInstr.Operand;//module.Import(((MethodReference)curInstr.Operand).Resolve());
                                            }
                                            else if (curInstr.Operand is FieldReference)
                                            {
                                                targetInstructions[_k].Operand = curInstr.Operand;//module.Import(((FieldReference)curInstr.Operand).Resolve());
                                            }
                                            else if (curInstr.Operand is TypeReference)
                                            {
                                                targetInstructions[_k].Operand = curInstr.Operand;//module.Import(((TypeReference)curInstr.Operand).Resolve());
                                            }
                                            else if (curInstr.Operand is IMetadataTokenProvider)
                                            {
                                                throw new Exception("Unsupported operand type IMetadataTokenProvider in " + targetMethod.FullName + "!");
                                                //IMetadataTokenProvider provider = (IMetadataTokenProvider)curInstr.Operand;
                                                //string name = provider.MetadataToken.ToString();
                                                //targetInstructions[_k].Operand = null;//module.Import(((TypeReference)curInstr.Operand).Resolve());
                                            }
                                            else
                                            {
                                                targetInstructions[_k].Operand = curInstr.Operand;
                                            }
                                            break;
                                    }
                                }

                                HelperClass.PatchInstructionReferences(mdefBody, instr, (targetInstructions.Count > 0) ? targetInstructions[0] : instr.Next);
                                HelperClass.SafeRemove(proc, instr);
                                i--;

                                stat_obfCall++;
                            }
                        }
                        else if (instr.OpCode == OpCodes.Ldsfld)
                        {
                            //remove fields generated by the obfuscator that always are null
                            FieldReference fref = (FieldReference)instr.Operand;
                            FieldDefinition targetField = fref.Resolve();
                            if (targetField != null && targetField.DeclaringType.Namespace.Equals("A") && targetField.IsAssembly)
                            {
                                if (targetField.DeclaringType.Fields.Count == 1 && targetField.DeclaringType.Methods.Count == 0)
                                {
                                    if (!suspectFields.ContainsKey(targetField))
                                    {
                                        FieldRemoveInfo fieldInfo = new FieldRemoveInfo();
                                        suspectFields.Add(targetField, fieldInfo);
                                    }
                                    suspectFields[targetField].references.Add(new InstructionIdentifier(mdefBody, instr));
                                }
                            }
                        }
                        else if (instr.OpCode == OpCodes.Stsfld)
                        {
                            //make sure the fields to remove really are not written to (doesn't work for structs whose members are altered)
                            FieldReference fref = (FieldReference)instr.Operand;
                            FieldDefinition targetField = fref.Resolve();
                            if (targetField != null && targetField.DeclaringType.Namespace.Equals("A") && targetField.IsAssembly)
                            {
                                if (targetField.DeclaringType.Fields.Count == 1 && targetField.DeclaringType.Methods.Count == 0)
                                {
                                    if (!suspectFields.ContainsKey(targetField))
                                    {
                                        FieldRemoveInfo fieldInfo = new FieldRemoveInfo();
                                        suspectFields.Add(targetField, fieldInfo);
                                    }
                                    suspectFields[targetField].shouldRemove = false;
                                }
                            }
                        }
                        else if ((instr.OpCode == OpCodes.Dup) && ((i+1) < mdefBody.Instructions.Count) && (mdefBody.Instructions[i+1].OpCode == OpCodes.Pop))
                        {
                            ILProcessor proc = mdefBody.GetILProcessor();
                            HelperClass.PatchInstructionReferences(mdefBody, instr, mdefBody.Instructions[i+2]);
                            HelperClass.SafeRemove(proc, mdefBody.Instructions[i+1]);
                            HelperClass.SafeRemove(proc, instr);
                            i--;
                            stat_duppop++;
                        }
                        else if (i > 0 &&
                            (instr.OpCode == OpCodes.Switch) &&
                            ((Instruction[])instr.Operand).Length == 1 && ((Instruction[])instr.Operand)[0] == mdefBody.Instructions[i-1])
                        {
                            Instruction ldInstr = mdefBody.Instructions[i-1];
                            HelperClass.PatchInstructionReferences(mdefBody, ldInstr, mdefBody.Instructions[i+1]);
                            ILProcessor proc = mdefBody.GetILProcessor();
                            HelperClass.SafeRemove(proc, mdefBody.Instructions[i-1]);
                            HelperClass.SafeRemove(proc, instr);
                            i -= 2;
                            stat_obfSwitch++;
                        }
                        else if ((instr.OpCode == OpCodes.Ldc_I4_1) && ((i+4) < mdefBody.Instructions.Count) &&
                            (mdefBody.Instructions[i+1].OpCode.Code == Code.Brtrue_S) &&
                            (mdefBody.Instructions[i+2].OpCode.Code == Code.Ldtoken) &&
                            (mdefBody.Instructions[i+3].OpCode.Code == Code.Pop)
                        )
                        {
                            Instruction ldInstr = mdefBody.Instructions[i-1];
                            HelperClass.PatchInstructionReferences(mdefBody, instr, mdefBody.Instructions[i+4]);
                            ILProcessor proc = mdefBody.GetILProcessor();
                            for (int k = 0; k < 4; k++)
                                HelperClass.SafeRemove(proc, mdefBody.Instructions[i]);
                            i--;
                            stat_obfIf++;
                        }
                    }
                }
                if ((methodIndex % (mdefs.Length/10)) == 0 && methodIndex > 0)
                    logger.KeyInfo("Removed garbage from method #" + (methodIndex + 1) + ".");
            }
            foreach (FieldRemoveInfo info in suspectFields.Values)
            {
                if (info.shouldRemove)
                {
                    foreach (InstructionIdentifier ident in info.references)
                    {
                        ILProcessor proc = ident.body.GetILProcessor();
                        Instruction newInstr = proc.Create(OpCodes.Ldnull);
                        HelperClass.PatchInstructionReferences(ident.body, ident.instr, newInstr);
                        HelperClass.SafeInsertAfter(proc, ident.instr, newInstr);
                        HelperClass.SafeRemove(proc, ident.instr);
                        stat_nullField++;
                    }
                }
            }
            List<TypeDefinition> referencedTypes = new List<TypeDefinition>();

            for (int methodIndex = 0; methodIndex < mdefs.Length; methodIndex++)
            {
                MethodDefinition mdef = mdefs[methodIndex];
                if (mdef.HasBody)
                {
                    MethodBody mdefBody = mdef.Body;
                    for (int k = 0; k < mdefBody.Instructions.Count; k++)
                    {
                        Instruction instr = mdefBody.Instructions[k];
                        if (instr.Operand is MethodReference)
                        {
                            MethodDefinition targetMethod = ((MethodReference)instr.Operand).Resolve();
                            if (targetMethod != null)
                            {
                                referencedTypes.Add(targetMethod.DeclaringType);
                            }
                        }
                        else if (instr.Operand is FieldReference)
                        {
                            FieldDefinition targetField = ((FieldReference)instr.Operand).Resolve();
                            if (targetField != null)
                            {
                                referencedTypes.Add(targetField.DeclaringType);
                            }
                        }
                        else if (instr.Operand is TypeReference)
                        {
                            TypeDefinition targetType = ((TypeReference)instr.Operand).Resolve();
                            if (targetType != null)
                            {
                                referencedTypes.Add(targetType);
                            }
                        }
                    }
                }
            }
            for (int i = module.Types.Count-1; i >= 0; i--)
            {
                TypeDefinition tdef = module.Types[i];
                if (tdef.Namespace.Equals("A") && !tdef.IsEnum && !tdef.Name.Equals("AssemblyInfoAttribute"))
                {
                    if (!referencedTypes.Contains(tdef))
                    {
                        module.Types.RemoveAt(i);
                        stat_obfClasses++;
                    }
                }
            }
            logger.KeyInfo("Removed " +
                stat_obfCall + " extracted method calls, " +
                stat_nullField + " always null fields, " +
                stat_duppop + " senseless dup/pop, " +
                stat_obfSwitch + " senseless switches, " +
                stat_obfIf + " senseless conditions, " +
                stat_obfClasses + " obfuscator classes.");
        }