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); }
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."); }