From a67529ff35d90dc13b94593cc015b0cde615e551 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 30 Nov 2012 22:53:15 +0100 Subject: [PATCH 01/71] Cecil is now removed so this isn't needed anymore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index dc68a226..670f1e87 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ /Debug/ /Release/ /de4dot.snk -/cecil/ From 99c7cf8eb52f37e6cdc163d8c6efee2b80c152d9 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 1 Dec 2012 01:40:23 +0100 Subject: [PATCH 02/71] Load target asm's CLR version when decrypting strings dynamically --- .../AssemblyClient/AssemblyClientFactory.cs | 21 +++++++++++++++++++ de4dot.code/ObfuscatedFile.cs | 6 +++++- de4dot.code/deobfuscators/MethodsDecrypter.cs | 17 +-------------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/de4dot.code/AssemblyClient/AssemblyClientFactory.cs b/de4dot.code/AssemblyClient/AssemblyClientFactory.cs index 615d4014..ed302653 100644 --- a/de4dot.code/AssemblyClient/AssemblyClientFactory.cs +++ b/de4dot.code/AssemblyClient/AssemblyClientFactory.cs @@ -17,6 +17,8 @@ along with de4dot. If not, see . */ +using dot10.DotNet; + namespace de4dot.code.AssemblyClient { public interface IAssemblyClientFactory { IAssemblyClient create(); @@ -45,8 +47,27 @@ namespace de4dot.code.AssemblyClient { this.serverVersion = serverVersion; } + public IAssemblyClient create(ModuleDef module) { + return new AssemblyClient(new NewProcessAssemblyServerLoader(getServerClrVersion(module))); + } + public IAssemblyClient create() { return new AssemblyClient(new NewProcessAssemblyServerLoader(serverVersion)); } + + internal static ServerClrVersion getServerClrVersion(ModuleDef module) { + switch (module.GetPointerSize()) { + default: + case 4: + if (module.IsClr40) + return ServerClrVersion.CLR_v40_x86; + return ServerClrVersion.CLR_v20_x86; + + case 8: + if (module.IsClr40) + return ServerClrVersion.CLR_v40_x64; + return ServerClrVersion.CLR_v20_x64; + } + } } } diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 15ac0462..f5aa11bd 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -355,7 +355,11 @@ namespace de4dot.code { case DecrypterType.Delegate: case DecrypterType.Emulate: checkSupportedStringDecrypter(StringFeatures.AllowDynamicDecryption); - assemblyClient = assemblyClientFactory.create(); + var newProcFactory = assemblyClientFactory as NewProcessAssemblyClientFactory; + if (newProcFactory != null) + assemblyClient = newProcFactory.create(module); + else + assemblyClient = assemblyClientFactory.create(); assemblyClient.connect(); break; diff --git a/de4dot.code/deobfuscators/MethodsDecrypter.cs b/de4dot.code/deobfuscators/MethodsDecrypter.cs index dcb484a5..6c052d02 100644 --- a/de4dot.code/deobfuscators/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/MethodsDecrypter.cs @@ -25,7 +25,7 @@ using de4dot.mdecrypt; namespace de4dot.code.deobfuscators { static class MethodsDecrypter { public static DumpedMethods decrypt(ModuleDef module, byte[] moduleCctorBytes) { - return decrypt(getServerClrVersion(module), module.Location, moduleCctorBytes); + return decrypt(NewProcessAssemblyClientFactory.getServerClrVersion(module), module.Location, moduleCctorBytes); } public static DumpedMethods decrypt(ServerClrVersion serverVersion, string filename, byte[] moduleCctorBytes) { @@ -39,20 +39,5 @@ namespace de4dot.code.deobfuscators { return client.Service.decryptMethods(); } } - - static ServerClrVersion getServerClrVersion(ModuleDef module) { - switch (module.GetPointerSize()) { - default: - case 4: - if (module.IsClr40) - return ServerClrVersion.CLR_v40_x86; - return ServerClrVersion.CLR_v20_x86; - - case 8: - if (module.IsClr40) - return ServerClrVersion.CLR_v40_x64; - return ServerClrVersion.CLR_v20_x64; - } - } } } From b3e06c4ce61a75451cc1b172dbd26980477b0193 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 1 Dec 2012 02:12:13 +0100 Subject: [PATCH 03/71] Add options to disable renaming types, fields, etc --- de4dot.cui/CommandLineParser.cs | 15 +++++++++++++++ de4dot.cui/FilesDeobfuscator.cs | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/de4dot.cui/CommandLineParser.cs b/de4dot.cui/CommandLineParser.cs index 42ce5233..a5134dce 100644 --- a/de4dot.cui/CommandLineParser.cs +++ b/de4dot.cui/CommandLineParser.cs @@ -120,6 +120,21 @@ namespace de4dot.cui { miscOptions.Add(new NoArgOption(null, "dont-rename", "Don't rename classes, methods, etc.", () => { filesOptions.RenameSymbols = false; })); + miscOptions.Add(new OneArgOption(null, "keep-names", "Don't rename n(amespaces), t(ypes), p(rops), e(vents), f(ields), m(ethods), a(rgs), g(enericparams). Can be combined, eg. efm", "flags", (val) => { + foreach (var c in val) { + switch (c) { + case 'n': filesOptions.RenameNamespaces = false; break; + case 't': filesOptions.RenameTypes = false; break; + case 'p': filesOptions.RenameProperties = false; break; + case 'e': filesOptions.RenameEvents = false; break; + case 'f': filesOptions.RenameFields = false; break; + case 'm': filesOptions.RenameMethods = false; break; + case 'a': filesOptions.RenameMethodArgs = false; break; + case 'g': filesOptions.RenameGenericParams = false; break; + default: throw new UserException(string.Format("Unrecognized --keep-names char: '{0}'", c)); + } + } + })); miscOptions.Add(new NoArgOption(null, "dont-restore-props", "Don't restore properties/events", () => { filesOptions.RestorePropsEvents = false; })); diff --git a/de4dot.cui/FilesDeobfuscator.cs b/de4dot.cui/FilesDeobfuscator.cs index f84dd0e2..972a60a9 100644 --- a/de4dot.cui/FilesDeobfuscator.cs +++ b/de4dot.cui/FilesDeobfuscator.cs @@ -38,6 +38,14 @@ namespace de4dot.cui { public IList Files { get; set; } public IList SearchDirs { get; set; } public bool DetectObfuscators { get; set; } + public bool RenameNamespaces { get; set; } + public bool RenameTypes { get; set; } + public bool RenameProperties { get; set; } + public bool RenameEvents { get; set; } + public bool RenameFields { get; set; } + public bool RenameMethods { get; set; } + public bool RenameMethodArgs { get; set; } + public bool RenameGenericParams { get; set; } public bool RenameSymbols { get; set; } public bool RestorePropsEvents { get; set; } public bool ControlFlowDeobfuscation { get; set; } @@ -53,6 +61,14 @@ namespace de4dot.cui { Files = new List(); SearchDirs = new List(); DefaultStringDecrypterMethods = new List(); + RenameNamespaces = true; + RenameTypes = true; + RenameProperties = true; + RenameEvents = true; + RenameFields = true; + RenameMethods = true; + RenameMethodArgs = true; + RenameGenericParams = true; RenameSymbols = true; RestorePropsEvents = true; ControlFlowDeobfuscation = true; @@ -369,6 +385,14 @@ namespace de4dot.cui { if (!options.RenameSymbols) return; var renamer = new Renamer(deobfuscatorContext, theFiles) { + RenameNamespaces = options.RenameNamespaces, + RenameTypes = options.RenameTypes, + RenameProperties = options.RenameProperties, + RenameEvents = options.RenameEvents, + RenameFields = options.RenameFields, + RenameMethods = options.RenameMethods, + RenameMethodArgs = options.RenameMethodArgs, + RenameGenericParams = options.RenameGenericParams, RestorePropertiesFromNames = options.RestorePropsEvents, RestoreEventsFromNames = options.RestorePropsEvents, }; From dcdbe25a0f6763be4471f88227f9b0952501050c Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 1 Dec 2012 02:22:59 +0100 Subject: [PATCH 04/71] Add option to disable creating new ParamDefs when renaming --- de4dot.code/renamer/Renamer.cs | 7 ++++++- de4dot.cui/CommandLineParser.cs | 3 +++ de4dot.cui/FilesDeobfuscator.cs | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/de4dot.code/renamer/Renamer.cs b/de4dot.code/renamer/Renamer.cs index a26c1945..753138a2 100644 --- a/de4dot.code/renamer/Renamer.cs +++ b/de4dot.code/renamer/Renamer.cs @@ -39,6 +39,7 @@ namespace de4dot.code.renamer { public bool RestorePropertiesFromNames { get; set; } public bool RestoreEvents { get; set; } public bool RestoreEventsFromNames { get; set; } + public bool DontCreateNewParamDefs { get; set; } Modules modules; MemberInfos memberInfos = new MemberInfos(); @@ -321,7 +322,11 @@ namespace de4dot.code.renamer { var paramInfo = memberInfos.param(param); if (!paramInfo.gotNewName()) continue; - param.ParameterDef.CreateParamDef(); + if (!param.ParameterDef.HasParamDef) { + if (DontCreateNewParamDefs) + continue; + param.ParameterDef.CreateParamDef(); + } param.ParameterDef.Name = paramInfo.newName; if (isVerbose) { if (param.IsReturnParameter) diff --git a/de4dot.cui/CommandLineParser.cs b/de4dot.cui/CommandLineParser.cs index a5134dce..493cf8eb 100644 --- a/de4dot.cui/CommandLineParser.cs +++ b/de4dot.cui/CommandLineParser.cs @@ -135,6 +135,9 @@ namespace de4dot.cui { } } })); + miscOptions.Add(new NoArgOption(null, "dont-create-params", "Don't create method params when renaming", () => { + filesOptions.DontCreateNewParamDefs = true; + })); miscOptions.Add(new NoArgOption(null, "dont-restore-props", "Don't restore properties/events", () => { filesOptions.RestorePropsEvents = false; })); diff --git a/de4dot.cui/FilesDeobfuscator.cs b/de4dot.cui/FilesDeobfuscator.cs index 972a60a9..03a511c3 100644 --- a/de4dot.cui/FilesDeobfuscator.cs +++ b/de4dot.cui/FilesDeobfuscator.cs @@ -38,6 +38,7 @@ namespace de4dot.cui { public IList Files { get; set; } public IList SearchDirs { get; set; } public bool DetectObfuscators { get; set; } + public bool DontCreateNewParamDefs { get; set; } public bool RenameNamespaces { get; set; } public bool RenameTypes { get; set; } public bool RenameProperties { get; set; } @@ -385,6 +386,7 @@ namespace de4dot.cui { if (!options.RenameSymbols) return; var renamer = new Renamer(deobfuscatorContext, theFiles) { + DontCreateNewParamDefs = options.DontCreateNewParamDefs, RenameNamespaces = options.RenameNamespaces, RenameTypes = options.RenameTypes, RenameProperties = options.RenameProperties, From 643e155cf8187cb69f40dc0af6cb2d70f86ef9a8 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 1 Dec 2012 03:24:12 +0100 Subject: [PATCH 05/71] Add options to preserve rids, heaps --- de4dot.code/AssemblyModule.cs | 12 +----- de4dot.code/ObfuscatedFile.cs | 33 ++++++++++++---- de4dot.code/deobfuscators/DeobfuscatorBase.cs | 4 ++ de4dot.code/deobfuscators/IDeobfuscator.cs | 4 +- de4dot.code/deobfuscators/Operations.cs | 4 ++ de4dot.cui/CommandLineParser.cs | 39 +++++++++++++++++++ de4dot.cui/FilesDeobfuscator.cs | 5 +++ 7 files changed, 82 insertions(+), 19 deletions(-) diff --git a/de4dot.code/AssemblyModule.cs b/de4dot.code/AssemblyModule.cs index f42bb347..a4150619 100644 --- a/de4dot.code/AssemblyModule.cs +++ b/de4dot.code/AssemblyModule.cs @@ -51,17 +51,7 @@ namespace de4dot.code { return module; } - public void save(string newFilename, bool preserveTokens, bool updateMaxStack, IModuleWriterListener writerListener) { - MetaDataFlags mdFlags = 0; - if (!updateMaxStack) - mdFlags |= MetaDataFlags.KeepOldMaxStack; - if (preserveTokens) { - mdFlags |= MetaDataFlags.PreserveRids | - MetaDataFlags.PreserveUSOffsets | - MetaDataFlags.PreserveBlobOffsets | - MetaDataFlags.PreserveExtraSignatureData; - } - + public void save(string newFilename, MetaDataFlags mdFlags, IModuleWriterListener writerListener) { if (module.IsILOnly) { var writerOptions = new ModuleWriterOptions(module, writerListener); writerOptions.MetaDataOptions.Flags |= mdFlags; diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index f5aa11bd..26e3646f 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -88,6 +88,8 @@ namespace de4dot.code { public List StringDecrypterMethods { get; private set; } public bool ControlFlowDeobfuscation { get; set; } public bool KeepObfuscatorTypes { get; set; } + public bool PreserveTokens { get; set; } + public MetaDataFlags MetaDataFlags { get; set; } public Options() { StringDecrypterType = DecrypterType.Default; @@ -251,6 +253,7 @@ namespace de4dot.code { } op.KeepObfuscatorTypes = options.KeepObfuscatorTypes; + op.MetaDataFlags = options.MetaDataFlags; return op; } @@ -322,13 +325,26 @@ namespace de4dot.code { return detected; } - bool ShouldPreserveTokens() { - return options.KeepObfuscatorTypes || deob.Type == "un"; + MetaDataFlags getMetaDataFlags() { + var mdFlags = options.MetaDataFlags | deob.MetaDataFlags; + + // Always preserve tokens if it's an unknown obfuscator + if (deob.Type == "un") { + mdFlags |= MetaDataFlags.PreserveRids | + MetaDataFlags.PreserveUSOffsets | + MetaDataFlags.PreserveBlobOffsets | + MetaDataFlags.PreserveExtraSignatureData; + } + + return mdFlags; } public void save() { Logger.n("Saving {0}", options.NewFilename); - assemblyModule.save(options.NewFilename, ShouldPreserveTokens(), options.ControlFlowDeobfuscation, deob as IModuleWriterListener); + var mdFlags = getMetaDataFlags(); + if (!options.ControlFlowDeobfuscation) + mdFlags |= MetaDataFlags.KeepOldMaxStack; + assemblyModule.save(options.NewFilename, mdFlags, deob as IModuleWriterListener); } IList getAllMethods() { @@ -556,7 +572,7 @@ namespace de4dot.code { deob.DeobfuscatedFile = null; if (!options.ControlFlowDeobfuscation) { - if (ShouldPreserveTokens()) + if (options.KeepObfuscatorTypes || deob.Type == "un") return; } @@ -611,6 +627,11 @@ namespace de4dot.code { } } + bool CanOptimizeLocals() { + // Don't remove any locals if we must preserve StandAloneSig table + return (getMetaDataFlags() & MetaDataFlags.PreserveStandAloneSigRids) == 0; + } + void deobfuscate(MethodDef method, BlocksCflowDeobfuscator cflowDeobfuscator, MethodPrinter methodPrinter, bool isVerbose, bool isVV) { if (!hasNonEmptyBody(method)) return; @@ -629,9 +650,7 @@ namespace de4dot.code { cflowDeobfuscator.deobfuscate(); if (options.ControlFlowDeobfuscation) { - // Don't remove any locals if we should preserve tokens or we won't be able - // to always preserve StandAloneSig tokens. - if (!ShouldPreserveTokens()) + if (CanOptimizeLocals()) numRemovedLocals = blocks.optimizeLocals(); blocks.repartitionBlocks(); } diff --git a/de4dot.code/deobfuscators/DeobfuscatorBase.cs b/de4dot.code/deobfuscators/DeobfuscatorBase.cs index 66b59fff..f52ce371 100644 --- a/de4dot.code/deobfuscators/DeobfuscatorBase.cs +++ b/de4dot.code/deobfuscators/DeobfuscatorBase.cs @@ -77,6 +77,10 @@ namespace de4dot.code.deobfuscators { public virtual RenamingOptions RenamingOptions { get; set; } public DecrypterType DefaultDecrypterType { get; set; } + public virtual MetaDataFlags MetaDataFlags { + get { return Operations.MetaDataFlags; } + } + public abstract string Type { get; } public abstract string TypeLong { get; } public abstract string Name { get; } diff --git a/de4dot.code/deobfuscators/IDeobfuscator.cs b/de4dot.code/deobfuscators/IDeobfuscator.cs index 6920144e..a65b7aa1 100644 --- a/de4dot.code/deobfuscators/IDeobfuscator.cs +++ b/de4dot.code/deobfuscators/IDeobfuscator.cs @@ -19,8 +19,9 @@ using System; using System.Collections.Generic; -using dot10.DotNet; using dot10.PE; +using dot10.DotNet; +using dot10.DotNet.Writer; using de4dot.blocks; using de4dot.blocks.cflow; using de4dot.code.renamer; @@ -58,6 +59,7 @@ namespace de4dot.code.deobfuscators { string Name { get; } IDeobfuscatorOptions TheOptions { get; } IOperations Operations { get; set; } + MetaDataFlags MetaDataFlags { get; } StringFeatures StringFeatures { get; } RenamingOptions RenamingOptions { get; } DecrypterType DefaultDecrypterType { get; } diff --git a/de4dot.code/deobfuscators/Operations.cs b/de4dot.code/deobfuscators/Operations.cs index 961e1e3c..d374f37c 100644 --- a/de4dot.code/deobfuscators/Operations.cs +++ b/de4dot.code/deobfuscators/Operations.cs @@ -17,6 +17,8 @@ along with de4dot. If not, see . */ +using dot10.DotNet.Writer; + namespace de4dot.code.deobfuscators { public enum OpDecryptString { None, @@ -26,11 +28,13 @@ namespace de4dot.code.deobfuscators { public interface IOperations { bool KeepObfuscatorTypes { get; } + MetaDataFlags MetaDataFlags { get; } OpDecryptString DecryptStrings { get; } } class Operations : IOperations { public bool KeepObfuscatorTypes { get; set; } + public MetaDataFlags MetaDataFlags { get; set; } public OpDecryptString DecryptStrings { get; set; } } } diff --git a/de4dot.cui/CommandLineParser.cs b/de4dot.cui/CommandLineParser.cs index 493cf8eb..7f25bf44 100644 --- a/de4dot.cui/CommandLineParser.cs +++ b/de4dot.cui/CommandLineParser.cs @@ -21,6 +21,7 @@ using System; using System.IO; using System.Collections.Generic; using dot10.DotNet; +using dot10.DotNet.Writer; using de4dot.code; using de4dot.code.deobfuscators; using de4dot.code.AssemblyClient; @@ -159,6 +160,43 @@ namespace de4dot.cui { miscOptions.Add(new NoArgOption(null, "keep-types", "Keep obfuscator types, fields, methods", () => { filesOptions.KeepObfuscatorTypes = true; })); + miscOptions.Add(new NoArgOption(null, "preserve-tokens", "Preserve important tokens, #US, #Blob, extra sig data", () => { + filesOptions.MetaDataFlags |= MetaDataFlags.PreserveRids | + MetaDataFlags.PreserveUSOffsets | + MetaDataFlags.PreserveBlobOffsets | + MetaDataFlags.PreserveExtraSignatureData; + })); + miscOptions.Add(new OneArgOption(null, "preserve-table", "Preserve rids in table: tr (TypeRef), td (TypeDef), fd (Field), md (Method), pd (Param), mr (MemberRef), s (StandAloneSig), ed (Event), pr (Property), ts (TypeSpec), ms (MethodSpec). Can be combined: ed,fd,md", "flags", (val) => { + foreach (var s in val.Split(',')) { + switch (s.Trim()) { + case "": break; + case "tr": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveTypeRefRids; break; + case "td": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveTypeDefRids; break; + case "fd": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveFieldRids; break; + case "md": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveMethodRids; break; + case "pd": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveParamRids; break; + case "mr": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveMemberRefRids; break; + case "s": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveStandAloneSigRids; break; + case "ed": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveEventRids; break; + case "pr": filesOptions.MetaDataFlags |= MetaDataFlags.PreservePropertyRids; break; + case "ts": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveTypeSpecRids; break; + case "ms": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveMethodSpecRids; break; + default: throw new UserException(string.Format("Invalid --preserve-table option: {0}", s)); + } + } + })); + miscOptions.Add(new NoArgOption(null, "preserve-strings", "Preserve #Strings heap offsets", () => { + filesOptions.MetaDataFlags |= MetaDataFlags.PreserveStringsOffsets; + })); + miscOptions.Add(new NoArgOption(null, "preserve-us", "Preserve #US heap offsets", () => { + filesOptions.MetaDataFlags |= MetaDataFlags.PreserveUSOffsets; + })); + miscOptions.Add(new NoArgOption(null, "preserve-blob", "Preserve #Blob heap offsets", () => { + filesOptions.MetaDataFlags |= MetaDataFlags.PreserveBlobOffsets; + })); + miscOptions.Add(new NoArgOption(null, "preserve-sig-data", "Preserve extra data at the end of signatures", () => { + filesOptions.MetaDataFlags |= MetaDataFlags.PreserveExtraSignatureData; + })); miscOptions.Add(new NoArgOption(null, "one-file", "Deobfuscate one file at a time", () => { filesOptions.OneFileAtATime = true; })); @@ -183,6 +221,7 @@ namespace de4dot.cui { Filename = val, ControlFlowDeobfuscation = filesOptions.ControlFlowDeobfuscation, KeepObfuscatorTypes = filesOptions.KeepObfuscatorTypes, + MetaDataFlags = filesOptions.MetaDataFlags, }; if (defaultStringDecrypterType != null) newFileOptions.StringDecrypterType = defaultStringDecrypterType.Value; diff --git a/de4dot.cui/FilesDeobfuscator.cs b/de4dot.cui/FilesDeobfuscator.cs index 03a511c3..47c650ec 100644 --- a/de4dot.cui/FilesDeobfuscator.cs +++ b/de4dot.cui/FilesDeobfuscator.cs @@ -21,6 +21,7 @@ using System; using System.IO; using System.Collections.Generic; using dot10.DotNet; +using dot10.DotNet.Writer; using de4dot.blocks; using de4dot.code; using de4dot.code.renamer; @@ -37,6 +38,7 @@ namespace de4dot.cui { public IList DeobfuscatorInfos { get; set; } public IList Files { get; set; } public IList SearchDirs { get; set; } + public MetaDataFlags MetaDataFlags { get; set; } public bool DetectObfuscators { get; set; } public bool DontCreateNewParamDefs { get; set; } public bool RenameNamespaces { get; set; } @@ -163,6 +165,7 @@ namespace de4dot.cui { DeobfuscatorContext = deobfuscatorContext, ControlFlowDeobfuscation = options.ControlFlowDeobfuscation, KeepObfuscatorTypes = options.KeepObfuscatorTypes, + MetaDataFlags = options.MetaDataFlags, CreateDestinationDir = !onlyScan, }); @@ -186,6 +189,7 @@ namespace de4dot.cui { public IDeobfuscatorContext DeobfuscatorContext { get; set; } public bool ControlFlowDeobfuscation { get; set; } public bool KeepObfuscatorTypes { get; set; } + public MetaDataFlags MetaDataFlags { get; set; } public bool CreateDestinationDir { get; set; } } @@ -312,6 +316,7 @@ namespace de4dot.cui { Filename = Utils.getFullPath(filename), ControlFlowDeobfuscation = options.ControlFlowDeobfuscation, KeepObfuscatorTypes = options.KeepObfuscatorTypes, + MetaDataFlags = options.MetaDataFlags, }; if (options.DefaultStringDecrypterType != null) fileOptions.StringDecrypterType = options.DefaultStringDecrypterType.Value; From 6be080ca5978f17c8afc975b57dc4e6539444efa Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 1 Dec 2012 04:08:00 +0100 Subject: [PATCH 06/71] Add updated submodule --- dot10 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot10 b/dot10 index 31b036f1..24fbe310 160000 --- a/dot10 +++ b/dot10 @@ -1 +1 @@ -Subproject commit 31b036f1956ce58fb065555ade3015685df751ec +Subproject commit 24fbe310c6a6efab779c04eed70d90673d9a20cc From 8a36c8eea6ebadbd3070c7ee8634bda131264827 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 1 Dec 2012 04:35:39 +0100 Subject: [PATCH 07/71] Add an option to not rename delegate fields --- de4dot.code/renamer/Renamer.cs | 4 ++++ de4dot.cui/CommandLineParser.cs | 3 ++- de4dot.cui/FilesDeobfuscator.cs | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/de4dot.code/renamer/Renamer.cs b/de4dot.code/renamer/Renamer.cs index 753138a2..3884d4fc 100644 --- a/de4dot.code/renamer/Renamer.cs +++ b/de4dot.code/renamer/Renamer.cs @@ -40,6 +40,7 @@ namespace de4dot.code.renamer { public bool RestoreEvents { get; set; } public bool RestoreEventsFromNames { get; set; } public bool DontCreateNewParamDefs { get; set; } + public bool DontRenameDelegateFields { get; set; } Modules modules; MemberInfos memberInfos = new MemberInfos(); @@ -255,10 +256,13 @@ namespace de4dot.code.renamer { void renameFields(TypeInfo info) { if (!RenameFields) return; + bool isDelegateType = isDelegateClass.check(info.type); foreach (var fieldDef in info.type.AllFieldsSorted) { var fieldInfo = memberInfos.field(fieldDef); if (!fieldInfo.gotNewName()) continue; + if (isDelegateType && DontRenameDelegateFields) + continue; fieldDef.FieldDef.Name = new UTF8String(fieldInfo.newName); if (isVerbose) Logger.v("Field: {0} ({1:X8}) => {2}", diff --git a/de4dot.cui/CommandLineParser.cs b/de4dot.cui/CommandLineParser.cs index 7f25bf44..8c874b0e 100644 --- a/de4dot.cui/CommandLineParser.cs +++ b/de4dot.cui/CommandLineParser.cs @@ -121,7 +121,7 @@ namespace de4dot.cui { miscOptions.Add(new NoArgOption(null, "dont-rename", "Don't rename classes, methods, etc.", () => { filesOptions.RenameSymbols = false; })); - miscOptions.Add(new OneArgOption(null, "keep-names", "Don't rename n(amespaces), t(ypes), p(rops), e(vents), f(ields), m(ethods), a(rgs), g(enericparams). Can be combined, eg. efm", "flags", (val) => { + miscOptions.Add(new OneArgOption(null, "keep-names", "Don't rename n(amespaces), t(ypes), p(rops), e(vents), f(ields), m(ethods), a(rgs), g(enericparams), d(elegate fields). Can be combined, eg. efm", "flags", (val) => { foreach (var c in val) { switch (c) { case 'n': filesOptions.RenameNamespaces = false; break; @@ -132,6 +132,7 @@ namespace de4dot.cui { case 'm': filesOptions.RenameMethods = false; break; case 'a': filesOptions.RenameMethodArgs = false; break; case 'g': filesOptions.RenameGenericParams = false; break; + case 'd': filesOptions.DontRenameDelegateFields = true; break; default: throw new UserException(string.Format("Unrecognized --keep-names char: '{0}'", c)); } } diff --git a/de4dot.cui/FilesDeobfuscator.cs b/de4dot.cui/FilesDeobfuscator.cs index 47c650ec..0ffc28e9 100644 --- a/de4dot.cui/FilesDeobfuscator.cs +++ b/de4dot.cui/FilesDeobfuscator.cs @@ -49,6 +49,7 @@ namespace de4dot.cui { public bool RenameMethods { get; set; } public bool RenameMethodArgs { get; set; } public bool RenameGenericParams { get; set; } + public bool DontRenameDelegateFields { get; set; } public bool RenameSymbols { get; set; } public bool RestorePropsEvents { get; set; } public bool ControlFlowDeobfuscation { get; set; } @@ -400,6 +401,7 @@ namespace de4dot.cui { RenameMethods = options.RenameMethods, RenameMethodArgs = options.RenameMethodArgs, RenameGenericParams = options.RenameGenericParams, + DontRenameDelegateFields = options.DontRenameDelegateFields, RestorePropertiesFromNames = options.RestorePropsEvents, RestoreEventsFromNames = options.RestorePropsEvents, }; From 938bdcd1047f99ce90771b5a988ac8b24d416f65 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 2 Dec 2012 16:18:06 +0100 Subject: [PATCH 08/71] Add updated submodule --- dot10 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot10 b/dot10 index 24fbe310..24c8e766 160000 --- a/dot10 +++ b/dot10 @@ -1 +1 @@ -Subproject commit 24fbe310c6a6efab779c04eed70d90673d9a20cc +Subproject commit 24c8e766c37519c828d7c91743a478708701784e From ca6812bca78fce02388bca48239ff8678d875a76 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 2 Dec 2012 16:20:25 +0100 Subject: [PATCH 09/71] Support latest Rummage --- de4dot.code/de4dot.code.csproj | 1 + .../deobfuscators/Rummage/Deobfuscator.cs | 15 +- .../deobfuscators/Rummage/RummageVersion.cs | 26 ++ .../deobfuscators/Rummage/StringDecrypter.cs | 397 +++++++++++++----- 4 files changed, 332 insertions(+), 107 deletions(-) create mode 100644 de4dot.code/deobfuscators/Rummage/RummageVersion.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 3e8937e9..72a87098 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -231,6 +231,7 @@ + diff --git a/de4dot.code/deobfuscators/Rummage/Deobfuscator.cs b/de4dot.code/deobfuscators/Rummage/Deobfuscator.cs index 19dfe1fe..d7a6ad42 100644 --- a/de4dot.code/deobfuscators/Rummage/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Rummage/Deobfuscator.cs @@ -46,6 +46,7 @@ namespace de4dot.code.deobfuscators.Rummage { } class Deobfuscator : DeobfuscatorBase { + string obfuscatorName = DeobfuscatorInfo.THE_NAME; StringDecrypter stringDecrypter; internal class Options : OptionsBase { @@ -60,7 +61,7 @@ namespace de4dot.code.deobfuscators.Rummage { } public override string Name { - get { return TypeLong; } + get { return obfuscatorName; } } public Deobfuscator(Options options) @@ -80,6 +81,18 @@ namespace de4dot.code.deobfuscators.Rummage { protected override void scanForObfuscator() { stringDecrypter = new StringDecrypter(module); stringDecrypter.find(); + detectVersion(); + } + + void detectVersion() { + string version; + switch (stringDecrypter.Version) { + case RummageVersion.V1_1_445: version = "v1.1 - v2.0"; break; + case RummageVersion.V2_1_729: version = "v2.1 - v2.2"; break; + default: version = null; break; + } + if (version != null) + obfuscatorName += " " + version; } public override void deobfuscateBegin() { diff --git a/de4dot.code/deobfuscators/Rummage/RummageVersion.cs b/de4dot.code/deobfuscators/Rummage/RummageVersion.cs new file mode 100644 index 00000000..2bc4a7d4 --- /dev/null +++ b/de4dot.code/deobfuscators/Rummage/RummageVersion.cs @@ -0,0 +1,26 @@ +/* + Copyright (C) 2011-2012 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 . +*/ + +namespace de4dot.code.deobfuscators.Rummage { + enum RummageVersion { + Unknown, + V1_1_445, // 1.1.445.2781 - 2.0.640.3707 + V2_1_729, // 2.1.729.3909 - 2.2.750.3964 + } +} diff --git a/de4dot.code/deobfuscators/Rummage/StringDecrypter.cs b/de4dot.code/deobfuscators/Rummage/StringDecrypter.cs index 03f8c6ae..b2a623d0 100644 --- a/de4dot.code/deobfuscators/Rummage/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Rummage/StringDecrypter.cs @@ -28,11 +28,275 @@ using de4dot.blocks; namespace de4dot.code.deobfuscators.Rummage { class StringDecrypter { ModuleDefMD module; - MethodDef stringDecrypterMethod; FieldDefAndDeclaringTypeDict stringInfos = new FieldDefAndDeclaringTypeDict(); - int fileDispl; - uint[] key; - BinaryReader reader; + IDecrypter decrypter; + + interface IDecrypter { + RummageVersion Version { get; } + MethodDef Method { get; } + void initialize(); + string decrypt(int stringId); + } + + abstract class DecrypterBaseV11 : IDecrypter { + RummageVersion version; + MethodDef decrypterMethod; + protected int fileDispl; + protected BinaryReader reader; + protected uint[] key; + + public RummageVersion Version { + get { return version; } + } + + public MethodDef Method { + get { return decrypterMethod; } + } + + protected DecrypterBaseV11(RummageVersion version, MethodDef decrypterMethod, int fileDispl) { + this.version = version; + this.decrypterMethod = decrypterMethod; + this.fileDispl = fileDispl; + } + + public void initialize() { + reader = new BinaryReader(new FileStream(decrypterMethod.DeclaringType.Module.Location, FileMode.Open, FileAccess.Read, FileShare.Read)); + initializeImpl(); + } + + protected abstract void initializeImpl(); + + protected static MethodDef findDecrypterMethod(TypeDef type) { + MethodDef cctor = null, decrypterMethod = null; + foreach (var method in type.Methods) { + if (!method.IsStatic || method.Body == null) + return null; + if (method.Name == ".cctor") + cctor = method; + else if (DotNetUtils.isMethod(method, "System.String", "(System.Int32)")) + decrypterMethod = method; + else + return null; + } + if (cctor == null || decrypterMethod == null) + return null; + + return decrypterMethod; + } + + public abstract string decrypt(int stringId); + + protected string decryptInternal(int stringId) { + uint v0 = reader.ReadUInt32(); + uint v1 = reader.ReadUInt32(); + DeobUtils.xteaDecrypt(ref v0, ref v1, key, 32); + int utf8Length = (int)v0; + var decrypted = new uint[(utf8Length + 11) / 8 * 2 - 1]; + decrypted[0] = v1; + for (int i = 1; i + 1 < decrypted.Length; i += 2) { + v0 = reader.ReadUInt32(); + v1 = reader.ReadUInt32(); + DeobUtils.xteaDecrypt(ref v0, ref v1, key, 32); + decrypted[i] = v0; + decrypted[i + 1] = v1; + } + + var utf8 = new byte[utf8Length]; + Buffer.BlockCopy(decrypted, 0, utf8, 0, utf8.Length); + return Encoding.UTF8.GetString(utf8); + } + } + + class DecrypterV11 : DecrypterBaseV11 { + DecrypterV11(MethodDef decrypterMethod, int fileDispl) + : base(RummageVersion.V1_1_445, decrypterMethod, fileDispl) { + } + + public static DecrypterV11 create(MethodDef cctor) { + var method = checkType(cctor); + if (method == null) + return null; + int fileDispl; + if (!getDispl(method, out fileDispl)) + return null; + + return new DecrypterV11(method, fileDispl); + } + + static readonly string[] requiredFields = new string[] { + "System.UInt32[]", + }; + static readonly string[] requiredLocals = new string[] { + "System.Byte[]", + "System.Int32", + "System.IO.FileStream", + }; + static MethodDef checkType(MethodDef cctor) { + var type = cctor.DeclaringType; + if (!new FieldTypes(type).exactly(requiredFields)) + return null; + if (!new LocalTypes(cctor).all(requiredLocals)) + return null; + + return findDecrypterMethod(type); + } + + static bool getDispl(MethodDef method, out int displ) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 2; i++) { + var mul = instrs[i]; + if (mul.OpCode.Code != Code.Mul) + continue; + + var ldci4 = instrs[i + 1]; + if (!ldci4.IsLdcI4()) + continue; + + var sub = instrs[i + 2]; + if (sub.OpCode.Code != Code.Sub) + continue; + + displ = ldci4.GetLdcI4Value(); + return true; + } + + displ = 0; + return false; + } + + protected override void initializeImpl() { + initKey(); + } + + void initKey() { + reader.BaseStream.Position = reader.BaseStream.Length - 48; + key = new uint[4]; + for (int i = 0; i < key.Length; i++) + key[i] = reader.ReadUInt32(); + } + + public override string decrypt(int stringId) { + reader.BaseStream.Position = reader.BaseStream.Length + (stringId * 4 - fileDispl); + return decryptInternal(stringId); + } + } + + class DecrypterV21 : DecrypterBaseV11 { + long baseOffs; + + public DecrypterV21(MethodDef decrypterMethod, int fileDispl) + : base(RummageVersion.V2_1_729, decrypterMethod, fileDispl) { + } + + public static DecrypterV21 create(MethodDef cctor) { + var method = checkType(cctor); + if (method == null) + return null; + int fileDispl; + if (!getDispl(method, out fileDispl)) + return null; + + return new DecrypterV21(method, fileDispl); + } + + static readonly string[] requiredFields = new string[] { + "System.UInt32[]", + "System.Int64", + }; + static readonly string[] requiredLocals = new string[] { + "System.Boolean", + "System.Byte[]", + "System.Int32", + "System.IO.FileStream", + }; + static MethodDef checkType(MethodDef cctor) { + var type = cctor.DeclaringType; + if (!new FieldTypes(type).exactly(requiredFields)) + return null; + if (!new LocalTypes(cctor).all(requiredLocals)) + return null; + + return findDecrypterMethod(type); + } + + static bool getDispl(MethodDef method, out int displ) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 6; i++) { + var ldci4_1 = instrs[i]; + if (!ldci4_1.IsLdcI4() || ldci4_1.GetLdcI4Value() != 4) + continue; + if (instrs[i + 1].OpCode.Code != Code.Mul) + continue; + if (instrs[i + 2].OpCode.Code != Code.Conv_I8) + continue; + if (instrs[i + 3].OpCode.Code != Code.Add) + continue; + + var ldci4 = instrs[i + 4]; + if (!ldci4.IsLdcI4()) + continue; + + if (instrs[i + 5].OpCode.Code != Code.Conv_I8) + continue; + if (instrs[i + 6].OpCode.Code != Code.Sub) + continue; + + displ = ldci4.GetLdcI4Value(); + return true; + } + + displ = 0; + return false; + } + + static readonly byte[] magic = new byte[32] { + 0xC9, 0x76, 0xC3, 0x0D, 0xE2, 0x83, 0x72, 0xE4, + 0xD5, 0xC7, 0x35, 0xF8, 0x86, 0xD0, 0x60, 0x69, + 0xEE, 0xE1, 0x4C, 0x5E, 0x07, 0xA1, 0xC1, 0xFE, + 0x61, 0xE3, 0xAA, 0xBC, 0xE4, 0xB1, 0xF0, 0x92, + }; + + protected override void initializeImpl() { + baseOffs = initializeBaseOffs(); + initKey(); + } + + void initKey() { + reader.BaseStream.Position = baseOffs - 16; + key = new uint[4]; + for (int i = 0; i < key.Length; i++) + key[i] = reader.ReadUInt32(); + } + + long initializeBaseOffs() { + byte[] buf = new byte[0x1000]; // Must be 4096 bytes + reader.BaseStream.Position = reader.BaseStream.Length - buf.Length; + while (true) { + if (reader.Read(buf, 0, buf.Length) != buf.Length) + throw new ApplicationException("Could not read"); + + for (int bi = buf.Length - 1; bi > magic.Length; ) { + int mi = magic.Length - 1; + if (buf[bi--] != magic[mi--] || + buf[bi] != magic[mi--]) + continue; + while (true) { + if (buf[--bi] != magic[mi--]) + break; + if (mi == -1) + return reader.BaseStream.Position - buf.Length + bi; + } + } + + reader.BaseStream.Position -= buf.Length * 2 - 0x20; + } + } + + public override string decrypt(int stringId) { + reader.BaseStream.Position = baseOffs + stringId * 4 - fileDispl; + return decryptInternal(stringId); + } + } class StringInfo { public readonly FieldDef field; @@ -51,8 +315,12 @@ namespace de4dot.code.deobfuscators.Rummage { } } + public RummageVersion Version { + get { return decrypter == null ? RummageVersion.Unknown : decrypter.Version; } + } + public TypeDef Type { - get { return stringDecrypterMethod != null ? stringDecrypterMethod.DeclaringType : null; } + get { return decrypter != null ? decrypter.Method.DeclaringType : null; } } public IEnumerable OtherTypes { @@ -65,7 +333,7 @@ namespace de4dot.code.deobfuscators.Rummage { } public bool Detected { - get { return stringDecrypterMethod != null; } + get { return decrypter != null; } } public StringDecrypter(ModuleDefMD module) { @@ -73,93 +341,30 @@ namespace de4dot.code.deobfuscators.Rummage { } public void find() { - foreach (var type in module.Types) { - var method = checkType(type); - if (method == null) - continue; - if (!getDispl(method, ref fileDispl)) + foreach (var type in module.GetTypes()) { + var cctor = type.FindStaticConstructor(); + if (cctor == null) continue; - stringDecrypterMethod = method; - break; + decrypter = DecrypterV11.create(cctor); + if (decrypter != null) + break; + + decrypter = DecrypterV21.create(cctor); + if (decrypter != null) + break; } } - static readonly string[] requiredFields = new string[] { - "System.UInt32[]", - }; - static readonly string[] requiredLocals = new string[] { - "System.Byte[]", - "System.Int32", - "System.IO.FileStream", - }; - static MethodDef checkType(TypeDef type) { - if (!new FieldTypes(type).exactly(requiredFields)) - return null; - var cctor = type.FindStaticConstructor(); - if (cctor == null) - return null; - if (!new LocalTypes(cctor).all(requiredLocals)) - return null; - - return checkMethods(type); - } - - static MethodDef checkMethods(TypeDef type) { - MethodDef cctor = null, decrypterMethod = null; - foreach (var method in type.Methods) { - if (!method.IsStatic || method.Body == null) - return null; - if (method.Name == ".cctor") - cctor = method; - else if (DotNetUtils.isMethod(method, "System.String", "(System.Int32)")) - decrypterMethod = method; - else - return null; - } - if (cctor == null || decrypterMethod == null) - return null; - - return decrypterMethod; - } - - static bool getDispl(MethodDef method, ref int displ) { - var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count - 2; i++) { - var mul = instrs[i]; - if (mul.OpCode.Code != Code.Mul) - continue; - - var ldci4 = instrs[i + 1]; - if (!ldci4.IsLdcI4()) - continue; - - var sub = instrs[i + 2]; - if (sub.OpCode.Code != Code.Sub) - continue; - - displ = ldci4.GetLdcI4Value(); - return true; - } - - return false; - } - public void initialize() { - reader = new BinaryReader(new FileStream(module.Location, FileMode.Open, FileAccess.Read, FileShare.Read)); - initKey(); + if (decrypter == null) + return; + decrypter.initialize(); foreach (var type in module.Types) initType(type); } - void initKey() { - reader.BaseStream.Position = reader.BaseStream.Length - 48; - key = new uint[4]; - for (int i = 0; i < key.Length; i++) - key[i] = reader.ReadUInt32(); - } - void initType(TypeDef type) { var cctor = type.FindStaticConstructor(); if (cctor == null) @@ -185,7 +390,7 @@ namespace de4dot.code.deobfuscators.Rummage { if (call.OpCode.Code != Code.Call) continue; var calledMethod = call.Operand as IMethod; - if (!MethodEqualityComparer.CompareDeclaringTypes.Equals(stringDecrypterMethod, calledMethod)) + if (!MethodEqualityComparer.CompareDeclaringTypes.Equals(decrypter.Method, calledMethod)) continue; var stsfld = instrs[i + 2]; @@ -202,6 +407,8 @@ namespace de4dot.code.deobfuscators.Rummage { } public void deobfuscate(Blocks blocks) { + if (decrypter == null) + return; foreach (var block in blocks.MethodBlocks.getAllBlocks()) { var instrs = block.Instructions; for (int i = 0; i < instrs.Count; i++) { @@ -226,31 +433,9 @@ namespace de4dot.code.deobfuscators.Rummage { string decrypt(StringInfo info) { if (info.decrypted == null) - info.decrypted = decrypt(info.stringId); + info.decrypted = decrypter.decrypt(info.stringId); return info.decrypted; } - - string decrypt(int stringId) { - reader.BaseStream.Position = reader.BaseStream.Length + (stringId * 4 - fileDispl); - - uint v0 = reader.ReadUInt32(); - uint v1 = reader.ReadUInt32(); - DeobUtils.xteaDecrypt(ref v0, ref v1, key, 32); - int utf8Length = (int)v0; - var decrypted = new uint[(utf8Length + 11) / 8 * 2 - 1]; - decrypted[0] = v1; - for (int i = 1; i + 1 < decrypted.Length; i += 2) { - v0 = reader.ReadUInt32(); - v1 = reader.ReadUInt32(); - DeobUtils.xteaDecrypt(ref v0, ref v1, key, 32); - decrypted[i] = v0; - decrypted[i + 1] = v1; - } - - var utf8 = new byte[utf8Length]; - Buffer.BlockCopy(decrypted, 0, utf8, 0, utf8.Length); - return Encoding.UTF8.GetString(utf8); - } } } From 9a4cd237e56ab02d34d2c9b1bfe9dd6b053cebe8 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sun, 2 Dec 2012 23:24:00 +0100 Subject: [PATCH 10/71] Fix detection of SN string decrypter --- de4dot.code/deobfuscators/Spices_Net/StringDecrypter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/Spices_Net/StringDecrypter.cs b/de4dot.code/deobfuscators/Spices_Net/StringDecrypter.cs index 23eff5d5..241a447a 100644 --- a/de4dot.code/deobfuscators/Spices_Net/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Spices_Net/StringDecrypter.cs @@ -93,7 +93,7 @@ namespace de4dot.code.deobfuscators.Spices_Net { continue; if (type.HasEvents || type.HasProperties) continue; - if (type.Fields.Count != 2) + if (type.Fields.Count < 2 || type.Fields.Count > 3) continue; if ((type.Attributes & ~TypeAttributes.Sealed) != 0) continue; From faf37a4a47cdb5a9ede8a4996686387e24c0ff62 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 3 Dec 2012 01:22:14 +0100 Subject: [PATCH 11/71] Use a char[] instead of a StringBuilder since length is known --- de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs index c7b858d6..14685340 100644 --- a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs @@ -76,10 +76,10 @@ namespace de4dot.code.deobfuscators.Agile_NET { public string decrypt(string es) { if (stringDecrypterKey == null) throw new ApplicationException("Trying to decrypt strings when stringDecrypterKey is null (could not find it!)"); - StringBuilder sb = new StringBuilder(es.Length); + char[] buf = new char[es.Length]; for (int i = 0; i < es.Length; i++) - sb.Append(Convert.ToChar((int)(es[i] ^ stringDecrypterKey[i % stringDecrypterKey.Length]))); - return sb.ToString(); + buf[i] = (char)(es[i] ^ stringDecrypterKey[i % stringDecrypterKey.Length]); + return new string(buf); } } } From 8e69452edb0d7a333dd1cd1461e7f71c40e25474 Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 4 Dec 2012 02:29:41 +0100 Subject: [PATCH 12/71] Support .NET Reactor 4.5 --- .../dotNET_Reactor/v4/Deobfuscator.cs | 10 ++++- .../dotNET_Reactor/v4/EncryptedResource.cs | 6 +++ .../dotNET_Reactor/v4/MethodsDecrypter.cs | 2 +- .../dotNET_Reactor/v4/NativeImageUnpacker.cs | 42 +++++++++++++++---- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index 59cb0b46..ec0b2a6c 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -345,8 +345,14 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return DeobfuscatorInfo.THE_NAME + " 4.1"; return DeobfuscatorInfo.THE_NAME + " 4.0"; } - if (!hasCorEnableProfilingString) - return DeobfuscatorInfo.THE_NAME + " 4.x"; + if (!hasCorEnableProfilingString) { + // 4.x or 4.5 + bool callsReverse = DotNetUtils.callsMethod(methodsDecrypter.Method, "System.Void System.Array::Reverse(System.Array)"); + if (!callsReverse) + return DeobfuscatorInfo.THE_NAME + " 4.x"; + return DeobfuscatorInfo.THE_NAME + " 4.5"; + } + // 4.2-4.4 if (!localTypes.exists("System.Byte&")) diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs index 60bbda7b..490ba263 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/EncryptedResource.cs @@ -116,6 +116,8 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { iv = ArrayFinder.getInitializedByteArray(resourceDecrypterMethod, 16); if (iv == null) throw new ApplicationException("Could not find resource decrypter IV"); + if (needReverse()) + Array.Reverse(iv); // DNR 4.5.0.0 if (usesPublicKeyToken()) { var publicKeyToken = module.Assembly.PublicKeyToken; if (publicKeyToken != null && publicKeyToken.Data.Length > 0) { @@ -146,6 +148,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return false; } + bool needReverse() { + return DotNetUtils.callsMethod(resourceDecrypterMethod, "System.Void System.Array::Reverse(System.Array)"); + } + EmbeddedResource findMethodsDecrypterResource(MethodDef method) { foreach (var s in DotNetUtils.getCodeStrings(method)) { var resource = DotNetUtils.getResource(module, s) as EmbeddedResource; diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs index d0588984..83390106 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs @@ -168,7 +168,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { } } else { - // DNR 4.0 - 4.4 (jitter is hooked) + // DNR 4.0 - 4.5 (jitter is hooked) var methodDef = peImage.DotNetFile.MetaData.TablesStream.MethodTable; var rvaToIndex = new Dictionary((int)methodDef.Rows); diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/NativeImageUnpacker.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/NativeImageUnpacker.cs index a87b8a32..e7cea77b 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/NativeImageUnpacker.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/NativeImageUnpacker.cs @@ -22,11 +22,13 @@ using System.IO; using ICSharpCode.SharpZipLib.Zip.Compression; using dot10.PE; using dot10.IO; +using dot10.DotNet; namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { class NativeImageUnpacker { MyPEImage peImage; bool isNet1x; + const int loaderHeaderSizeV45 = 14; public NativeImageUnpacker(IPEImage peImage) { this.peImage = new MyPEImage(peImage); @@ -60,18 +62,44 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return null; } - if (BitConverter.ToInt16(inflatedData, 0) != 0x5A4D) - return null; + // CLR 1.x or DNR v4.0 - v4.4 + if (BitConverter.ToInt16(inflatedData, 0) == 0x5A4D) + return inflatedData; - return inflatedData; + // DNR v4.5 + if (BitConverter.ToInt16(inflatedData, loaderHeaderSizeV45) == 0x5A4D) + return unpackLoader(inflatedData); + + return null; } - static uint[] baseOffsets = new uint[] { + static byte[] unpackLoader(byte[] loaderData) { + var loaderBytes = new byte[loaderData.Length - loaderHeaderSizeV45]; + Array.Copy(loaderData, loaderHeaderSizeV45, loaderBytes, 0, loaderBytes.Length); + + try { + using (var asmLoader = ModuleDefMD.Load(loaderBytes)) { + if (asmLoader.Resources.Count == 0) + return null; + var resource = asmLoader.Resources[0] as EmbeddedResource; + if (resource == null) + return null; + + return resource.Data.ReadAllBytes(); + } + } + catch { + return null; + } + } + + static readonly uint[] baseOffsets = new uint[] { 0x1C00, // DNR 4.0 & 4.1 0x1900, // DNR 4.2.7.5 - 0x1B60, // DNR 4.2.8.4, 4.3 & 4.4 + 0x1B60, // DNR 4.2.8.4, 4.3, 4.4, 4.5 + 0x700, // DNR 4.5.0.0 }; - static short[] decryptMethodPattern = new short[] { + static readonly short[] decryptMethodPattern = new short[] { /* 00 */ 0x83, 0xEC, 0x38, // sub esp, 38h /* 03 */ 0x53, // push ebx /* 04 */ 0xB0, -1, // mov al, ??h @@ -84,7 +112,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { /* 1C */ 0x55, // push ebp /* 1D */ 0x56, // push esi }; - static short[] startMethodNet1xPattern = new short[] { + static readonly short[] startMethodNet1xPattern = new short[] { /* 00 */ 0x55, // push ebp /* 01 */ 0x8B, 0xEC, // mov ebp, esp /* 03 */ 0xB9, 0x14, 0x00, 0x00, 0x00, // mov ecx, 14h From b38aaca5824d0e5120b0f3b9c4b75acf951e1a8d Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 4 Dec 2012 20:17:17 +0100 Subject: [PATCH 13/71] Add updated submodule --- dot10 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot10 b/dot10 index 24c8e766..6f1b6a58 160000 --- a/dot10 +++ b/dot10 @@ -1 +1 @@ -Subproject commit 24c8e766c37519c828d7c91743a478708701784e +Subproject commit 6f1b6a586846b47b88bc519d60608275c64e9717 From 0ba3a0c1e28c501620976d167f005a82a5d5dd0a Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 4 Dec 2012 23:58:34 +0100 Subject: [PATCH 14/71] Better support of DNR + .NET 1.x assemblies --- .../dotNET_Reactor/v4/Deobfuscator.cs | 19 ++++++++++++++++++- .../dotNET_Reactor/v4/MethodsDecrypter.cs | 15 ++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index ec0b2a6c..ea158ce9 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -382,7 +382,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { return false; var tokenToNativeCode = new Dictionary(); - if (!methodsDecrypter.decrypt(peImage, DeobfuscatedFile, ref dumpedMethods, tokenToNativeCode)) + if (!methodsDecrypter.decrypt(peImage, DeobfuscatedFile, ref dumpedMethods, tokenToNativeCode, unpackedNativeFile)) return false; newFileData = fileData; @@ -584,9 +584,26 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { else Logger.v("Could not remove decrypter type"); + fixEntryPoint(); + base.deobfuscateEnd(); } + void fixEntryPoint() { + if (!module.IsClr1x) + return; + + var ep = module.EntryPoint; + if (ep == null) + return; + if (ep.MethodSig.GetParamCount() <= 1) + return; + + ep.MethodSig = MethodSig.CreateStatic(ep.MethodSig.RetType, new SZArraySig(module.CorLibTypes.String)); + ep.ParamList.Clear(); + ep.Parameters.UpdateParameterTypes(); + } + void removeInlinedMethods() { if (!options.InlineMethods || !options.RemoveInlinedMethods) return; diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs index 83390106..056ed542 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/MethodsDecrypter.cs @@ -123,7 +123,7 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { static short[] nativeLdci4 = new short[] { 0x55, 0x8B, 0xEC, 0xB8, -1, -1, -1, -1, 0x5D, 0xC3 }; static short[] nativeLdci4_0 = new short[] { 0x55, 0x8B, 0xEC, 0x33, 0xC0, 0x5D, 0xC3 }; - public bool decrypt(MyPEImage peImage, ISimpleDeobfuscator simpleDeobfuscator, ref DumpedMethods dumpedMethods, Dictionary tokenToNativeCode) { + public bool decrypt(MyPEImage peImage, ISimpleDeobfuscator simpleDeobfuscator, ref DumpedMethods dumpedMethods, Dictionary tokenToNativeCode, bool unpackedNativeFile) { if (encryptedResource.Method == null) return false; @@ -158,13 +158,22 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { } else if (!hooksJitter || mode == 1) { // DNR 3.9.8.0, 4.0, 4.1, 4.2, 4.3, 4.4 + + // If it's .NET 1.x, then offsets are used, not RVAs. + bool useOffsets = unpackedNativeFile && module.IsClr1x; + patchDwords(peImage, methodsDataReader, patchCount); while (methodsDataReader.Position < methodsData.Length - 1) { uint rva = methodsDataReader.ReadUInt32(); uint token = methodsDataReader.ReadUInt32(); // token, unknown, or index int size = methodsDataReader.ReadInt32(); - if (size > 0) - peImage.dotNetSafeWrite(rva, methodsDataReader.ReadBytes(size)); + if (size > 0) { + var newData = methodsDataReader.ReadBytes(size); + if (useOffsets) + peImage.dotNetSafeWriteOffset(rva, newData); + else + peImage.dotNetSafeWrite(rva, newData); + } } } else { From 20309b022534abad0d1922065080ab24b99ff63c Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 7 Dec 2012 14:59:53 +0100 Subject: [PATCH 15/71] Add updated submodule --- dot10 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot10 b/dot10 index 6f1b6a58..9d725f97 160000 --- a/dot10 +++ b/dot10 @@ -1 +1 @@ -Subproject commit 6f1b6a586846b47b88bc519d60608275c64e9717 +Subproject commit 9d725f9717558c92f3f1e725b62594835eac3602 From fa4e1fcc6b5d6b01f5a716dab211d52d166ed4fa Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 7 Dec 2012 15:06:38 +0100 Subject: [PATCH 16/71] Add RenamerFlags --- de4dot.code/ObfuscatedFile.cs | 2 + de4dot.code/deobfuscators/Operations.cs | 3 + de4dot.code/renamer/Renamer.cs | 174 ++++++++++++++++++++---- de4dot.cui/CommandLineParser.cs | 25 ++-- de4dot.cui/FilesDeobfuscator.cs | 51 +++---- 5 files changed, 183 insertions(+), 72 deletions(-) diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 26e3646f..d8833226 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -90,6 +90,7 @@ namespace de4dot.code { public bool KeepObfuscatorTypes { get; set; } public bool PreserveTokens { get; set; } public MetaDataFlags MetaDataFlags { get; set; } + public RenamerFlags RenamerFlags { get; set; } public Options() { StringDecrypterType = DecrypterType.Default; @@ -254,6 +255,7 @@ namespace de4dot.code { op.KeepObfuscatorTypes = options.KeepObfuscatorTypes; op.MetaDataFlags = options.MetaDataFlags; + op.RenamerFlags = options.RenamerFlags; return op; } diff --git a/de4dot.code/deobfuscators/Operations.cs b/de4dot.code/deobfuscators/Operations.cs index d374f37c..0240916d 100644 --- a/de4dot.code/deobfuscators/Operations.cs +++ b/de4dot.code/deobfuscators/Operations.cs @@ -18,6 +18,7 @@ */ using dot10.DotNet.Writer; +using de4dot.code.renamer; namespace de4dot.code.deobfuscators { public enum OpDecryptString { @@ -29,12 +30,14 @@ namespace de4dot.code.deobfuscators { public interface IOperations { bool KeepObfuscatorTypes { get; } MetaDataFlags MetaDataFlags { get; } + RenamerFlags RenamerFlags { get; } OpDecryptString DecryptStrings { get; } } class Operations : IOperations { public bool KeepObfuscatorTypes { get; set; } public MetaDataFlags MetaDataFlags { get; set; } + public RenamerFlags RenamerFlags { get; set; } public OpDecryptString DecryptStrings { get; set; } } } diff --git a/de4dot.code/renamer/Renamer.cs b/de4dot.code/renamer/Renamer.cs index 3884d4fc..7908daa3 100644 --- a/de4dot.code/renamer/Renamer.cs +++ b/de4dot.code/renamer/Renamer.cs @@ -26,21 +26,152 @@ using de4dot.code.renamer.asmmodules; using de4dot.blocks; namespace de4dot.code.renamer { + [Flags] + public enum RenamerFlags { + RenameNamespaces = 1, + RenameTypes = 2, + RenameProperties = 4, + RenameEvents = 8, + RenameFields = 0x10, + RenameMethods = 0x20, + RenameMethodArgs = 0x40, + RenameGenericParams = 0x80, + RestoreProperties = 0x100, + RestorePropertiesFromNames = 0x200, + RestoreEvents = 0x400, + RestoreEventsFromNames = 0x800, + DontCreateNewParamDefs = 0x1000, + DontRenameDelegateFields = 0x2000, + } + public class Renamer { - public bool RenameNamespaces { get; set; } - public bool RenameTypes { get; set; } - public bool RenameProperties { get; set; } - public bool RenameEvents { get; set; } - public bool RenameFields { get; set; } - public bool RenameMethods { get; set; } - public bool RenameMethodArgs { get; set; } - public bool RenameGenericParams { get; set; } - public bool RestoreProperties { get; set; } - public bool RestorePropertiesFromNames { get; set; } - public bool RestoreEvents { get; set; } - public bool RestoreEventsFromNames { get; set; } - public bool DontCreateNewParamDefs { get; set; } - public bool DontRenameDelegateFields { get; set; } + public RenamerFlags RenamerFlags { get; set; } + public bool RenameNamespaces { + get { return (RenamerFlags & RenamerFlags.RenameNamespaces) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameNamespaces; + else + RenamerFlags &= ~RenamerFlags.RenameNamespaces; + } + } + public bool RenameTypes { + get { return (RenamerFlags & RenamerFlags.RenameTypes) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameTypes; + else + RenamerFlags &= ~RenamerFlags.RenameTypes; + } + } + public bool RenameProperties { + get { return (RenamerFlags & RenamerFlags.RenameProperties) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameProperties; + else + RenamerFlags &= ~RenamerFlags.RenameProperties; + } + } + public bool RenameEvents { + get { return (RenamerFlags & RenamerFlags.RenameEvents) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameEvents; + else + RenamerFlags &= ~RenamerFlags.RenameEvents; + } + } + public bool RenameFields { + get { return (RenamerFlags & RenamerFlags.RenameFields) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameFields; + else + RenamerFlags &= ~RenamerFlags.RenameFields; + } + } + public bool RenameMethods { + get { return (RenamerFlags & RenamerFlags.RenameMethods) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameMethods; + else + RenamerFlags &= ~RenamerFlags.RenameMethods; + } + } + public bool RenameMethodArgs { + get { return (RenamerFlags & RenamerFlags.RenameMethodArgs) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameMethodArgs; + else + RenamerFlags &= ~RenamerFlags.RenameMethodArgs; + } + } + public bool RenameGenericParams { + get { return (RenamerFlags & RenamerFlags.RenameGenericParams) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RenameGenericParams; + else + RenamerFlags &= ~RenamerFlags.RenameGenericParams; + } + } + public bool RestoreProperties { + get { return (RenamerFlags & RenamerFlags.RestoreProperties) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RestoreProperties; + else + RenamerFlags &= ~RenamerFlags.RestoreProperties; + } + } + public bool RestorePropertiesFromNames { + get { return (RenamerFlags & RenamerFlags.RestorePropertiesFromNames) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RestorePropertiesFromNames; + else + RenamerFlags &= ~RenamerFlags.RestorePropertiesFromNames; + } + } + public bool RestoreEvents { + get { return (RenamerFlags & RenamerFlags.RestoreEvents) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RestoreEvents; + else + RenamerFlags &= ~RenamerFlags.RestoreEvents; + } + } + public bool RestoreEventsFromNames { + get { return (RenamerFlags & RenamerFlags.RestoreEventsFromNames) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.RestoreEventsFromNames; + else + RenamerFlags &= ~RenamerFlags.RestoreEventsFromNames; + } + } + public bool DontCreateNewParamDefs { + get { return (RenamerFlags & RenamerFlags.DontCreateNewParamDefs) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.DontCreateNewParamDefs; + else + RenamerFlags &= ~RenamerFlags.DontCreateNewParamDefs; + } + } + public bool DontRenameDelegateFields { + get { return (RenamerFlags & RenamerFlags.DontRenameDelegateFields) != 0; } + set { + if (value) + RenamerFlags |= RenamerFlags.DontRenameDelegateFields; + else + RenamerFlags &= ~RenamerFlags.DontRenameDelegateFields; + } + } Modules modules; MemberInfos memberInfos = new MemberInfos(); @@ -53,19 +184,8 @@ namespace de4dot.code.renamer { "System.MulticastDelegate", }; - public Renamer(IDeobfuscatorContext deobfuscatorContext, IEnumerable files) { - RenameNamespaces = true; - RenameTypes = true; - RenameProperties = true; - RenameEvents = true; - RenameFields = true; - RenameMethods = true; - RenameMethodArgs = true; - RenameGenericParams = true; - RestoreProperties = true; - RestorePropertiesFromNames = true; - RestoreEvents = true; - RestoreEventsFromNames = true; + public Renamer(IDeobfuscatorContext deobfuscatorContext, IEnumerable files, RenamerFlags flags) { + RenamerFlags = flags; modules = new Modules(deobfuscatorContext); isDelegateClass = new DerivedFrom(delegateClasses); diff --git a/de4dot.cui/CommandLineParser.cs b/de4dot.cui/CommandLineParser.cs index 8c874b0e..05105f3c 100644 --- a/de4dot.cui/CommandLineParser.cs +++ b/de4dot.cui/CommandLineParser.cs @@ -25,6 +25,7 @@ using dot10.DotNet.Writer; using de4dot.code; using de4dot.code.deobfuscators; using de4dot.code.AssemblyClient; +using de4dot.code.renamer; namespace de4dot.cui { class CommandLineParser { @@ -120,28 +121,29 @@ namespace de4dot.cui { })); miscOptions.Add(new NoArgOption(null, "dont-rename", "Don't rename classes, methods, etc.", () => { filesOptions.RenameSymbols = false; + filesOptions.RenamerFlags = 0; })); miscOptions.Add(new OneArgOption(null, "keep-names", "Don't rename n(amespaces), t(ypes), p(rops), e(vents), f(ields), m(ethods), a(rgs), g(enericparams), d(elegate fields). Can be combined, eg. efm", "flags", (val) => { foreach (var c in val) { switch (c) { - case 'n': filesOptions.RenameNamespaces = false; break; - case 't': filesOptions.RenameTypes = false; break; - case 'p': filesOptions.RenameProperties = false; break; - case 'e': filesOptions.RenameEvents = false; break; - case 'f': filesOptions.RenameFields = false; break; - case 'm': filesOptions.RenameMethods = false; break; - case 'a': filesOptions.RenameMethodArgs = false; break; - case 'g': filesOptions.RenameGenericParams = false; break; - case 'd': filesOptions.DontRenameDelegateFields = true; break; + case 'n': filesOptions.RenamerFlags &= ~RenamerFlags.RenameNamespaces; break; + case 't': filesOptions.RenamerFlags &= ~RenamerFlags.RenameTypes; break; + case 'p': filesOptions.RenamerFlags &= ~RenamerFlags.RenameProperties; break; + case 'e': filesOptions.RenamerFlags &= ~RenamerFlags.RenameEvents; break; + case 'f': filesOptions.RenamerFlags &= ~RenamerFlags.RenameFields; break; + case 'm': filesOptions.RenamerFlags &= ~RenamerFlags.RenameMethods; break; + case 'a': filesOptions.RenamerFlags &= ~RenamerFlags.RenameMethodArgs; break; + case 'g': filesOptions.RenamerFlags &= ~RenamerFlags.RenameGenericParams; break; + case 'd': filesOptions.RenamerFlags |= RenamerFlags.DontRenameDelegateFields; break; default: throw new UserException(string.Format("Unrecognized --keep-names char: '{0}'", c)); } } })); miscOptions.Add(new NoArgOption(null, "dont-create-params", "Don't create method params when renaming", () => { - filesOptions.DontCreateNewParamDefs = true; + filesOptions.RenamerFlags |= RenamerFlags.DontCreateNewParamDefs; })); miscOptions.Add(new NoArgOption(null, "dont-restore-props", "Don't restore properties/events", () => { - filesOptions.RestorePropsEvents = false; + filesOptions.RenamerFlags &= ~(RenamerFlags.RestorePropertiesFromNames | RenamerFlags.RestoreEventsFromNames); })); miscOptions.Add(new OneArgOption(null, "default-strtyp", "Default string decrypter type", "type", (val) => { object decrypterType; @@ -223,6 +225,7 @@ namespace de4dot.cui { ControlFlowDeobfuscation = filesOptions.ControlFlowDeobfuscation, KeepObfuscatorTypes = filesOptions.KeepObfuscatorTypes, MetaDataFlags = filesOptions.MetaDataFlags, + RenamerFlags = filesOptions.RenamerFlags, }; if (defaultStringDecrypterType != null) newFileOptions.StringDecrypterType = defaultStringDecrypterType.Value; diff --git a/de4dot.cui/FilesDeobfuscator.cs b/de4dot.cui/FilesDeobfuscator.cs index 0ffc28e9..a0e3c08e 100644 --- a/de4dot.cui/FilesDeobfuscator.cs +++ b/de4dot.cui/FilesDeobfuscator.cs @@ -40,18 +40,8 @@ namespace de4dot.cui { public IList SearchDirs { get; set; } public MetaDataFlags MetaDataFlags { get; set; } public bool DetectObfuscators { get; set; } - public bool DontCreateNewParamDefs { get; set; } - public bool RenameNamespaces { get; set; } - public bool RenameTypes { get; set; } - public bool RenameProperties { get; set; } - public bool RenameEvents { get; set; } - public bool RenameFields { get; set; } - public bool RenameMethods { get; set; } - public bool RenameMethodArgs { get; set; } - public bool RenameGenericParams { get; set; } - public bool DontRenameDelegateFields { get; set; } + public RenamerFlags RenamerFlags { get; set; } public bool RenameSymbols { get; set; } - public bool RestorePropsEvents { get; set; } public bool ControlFlowDeobfuscation { get; set; } public bool KeepObfuscatorTypes { get; set; } public bool OneFileAtATime { get; set; } @@ -65,16 +55,19 @@ namespace de4dot.cui { Files = new List(); SearchDirs = new List(); DefaultStringDecrypterMethods = new List(); - RenameNamespaces = true; - RenameTypes = true; - RenameProperties = true; - RenameEvents = true; - RenameFields = true; - RenameMethods = true; - RenameMethodArgs = true; - RenameGenericParams = true; + RenamerFlags = RenamerFlags.RenameNamespaces | + RenamerFlags.RenameTypes | + RenamerFlags.RenameProperties | + RenamerFlags.RenameEvents | + RenamerFlags.RenameFields | + RenamerFlags.RenameMethods | + RenamerFlags.RenameMethodArgs | + RenamerFlags.RenameGenericParams | + RenamerFlags.RestorePropertiesFromNames | + RenamerFlags.RestoreEventsFromNames | + RenamerFlags.RestoreProperties | + RenamerFlags.RestoreEvents; RenameSymbols = true; - RestorePropsEvents = true; ControlFlowDeobfuscation = true; } } @@ -167,6 +160,7 @@ namespace de4dot.cui { ControlFlowDeobfuscation = options.ControlFlowDeobfuscation, KeepObfuscatorTypes = options.KeepObfuscatorTypes, MetaDataFlags = options.MetaDataFlags, + RenamerFlags = options.RenamerFlags, CreateDestinationDir = !onlyScan, }); @@ -191,6 +185,7 @@ namespace de4dot.cui { public bool ControlFlowDeobfuscation { get; set; } public bool KeepObfuscatorTypes { get; set; } public MetaDataFlags MetaDataFlags { get; set; } + public RenamerFlags RenamerFlags { get; set; } public bool CreateDestinationDir { get; set; } } @@ -318,6 +313,7 @@ namespace de4dot.cui { ControlFlowDeobfuscation = options.ControlFlowDeobfuscation, KeepObfuscatorTypes = options.KeepObfuscatorTypes, MetaDataFlags = options.MetaDataFlags, + RenamerFlags = options.RenamerFlags, }; if (options.DefaultStringDecrypterType != null) fileOptions.StringDecrypterType = options.DefaultStringDecrypterType.Value; @@ -391,20 +387,7 @@ namespace de4dot.cui { void rename(IEnumerable theFiles) { if (!options.RenameSymbols) return; - var renamer = new Renamer(deobfuscatorContext, theFiles) { - DontCreateNewParamDefs = options.DontCreateNewParamDefs, - RenameNamespaces = options.RenameNamespaces, - RenameTypes = options.RenameTypes, - RenameProperties = options.RenameProperties, - RenameEvents = options.RenameEvents, - RenameFields = options.RenameFields, - RenameMethods = options.RenameMethods, - RenameMethodArgs = options.RenameMethodArgs, - RenameGenericParams = options.RenameGenericParams, - DontRenameDelegateFields = options.DontRenameDelegateFields, - RestorePropertiesFromNames = options.RestorePropsEvents, - RestoreEventsFromNames = options.RestorePropsEvents, - }; + var renamer = new Renamer(deobfuscatorContext, theFiles, options.RenamerFlags); renamer.rename(); } } From 8e79777cdfef0ea68ad5c0467449e5842ee1f2a9 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 7 Dec 2012 15:06:52 +0100 Subject: [PATCH 17/71] Return immediately if there's nothing to do --- de4dot.code/deobfuscators/MethodCallRestorerBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/de4dot.code/deobfuscators/MethodCallRestorerBase.cs b/de4dot.code/deobfuscators/MethodCallRestorerBase.cs index c841f897..48ff3833 100644 --- a/de4dot.code/deobfuscators/MethodCallRestorerBase.cs +++ b/de4dot.code/deobfuscators/MethodCallRestorerBase.cs @@ -100,6 +100,8 @@ namespace de4dot.code.deobfuscators { } public void deobfuscate(Blocks blocks) { + if (oldToNewMethod.Count == 0) + return; foreach (var block in blocks.MethodBlocks.getAllBlocks()) { var instrs = block.Instructions; for (int i = 0; i < instrs.Count; i++) { From f5967715f24cf4b9a34121c106afb349c29d6c2e Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 7 Dec 2012 15:07:30 +0100 Subject: [PATCH 18/71] Only remove the type if we rename types --- de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs index 3bdde714..d1a1b365 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using dot10.DotNet; using de4dot.blocks; +using de4dot.code.renamer; namespace de4dot.code.deobfuscators.Eazfuscator_NET { public class DeobfuscatorInfo : DeobfuscatorInfoBase { @@ -130,7 +131,8 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { addModuleCctorInitCallToBeRemoved(resourceResolver.InitMethod); resourceMethodsRestorer = new ResourceMethodsRestorer(module); - resourceMethodsRestorer.find(DeobfuscatedFile, this); + if ((Operations.RenamerFlags & (RenamerFlags.RenameTypes | RenamerFlags.RenameNamespaces)) != 0) + resourceMethodsRestorer.find(DeobfuscatedFile, this); dumpEmbeddedAssemblies(); } From d6958fac23c8cc1923af8ac3fce523ec4f454c10 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 7 Dec 2012 23:54:34 +0100 Subject: [PATCH 19/71] Add updated submodule --- dot10 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot10 b/dot10 index 9d725f97..2b81c474 160000 --- a/dot10 +++ b/dot10 @@ -1 +1 @@ -Subproject commit 9d725f9717558c92f3f1e725b62594835eac3602 +Subproject commit 2b81c474d9bde1def25b17fbfb6002ebf0ad4a85 From dcbcaa098ea99959209fc92f4c227aa0f7b1d2bb Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 8 Dec 2012 01:12:20 +0100 Subject: [PATCH 20/71] Work around a bug in EF --- .../Eazfuscator_NET/Deobfuscator.cs | 47 +++++++++++++++++++ .../Eazfuscator_NET/StringDecrypter.cs | 6 +++ 2 files changed, 53 insertions(+) diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs index d1a1b365..e95669fc 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs @@ -17,8 +17,10 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; using dot10.DotNet; +using dot10.DotNet.Emit; using de4dot.blocks; using de4dot.code.renamer; @@ -163,9 +165,54 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { 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(Instruction.Create(OpCodes.Call, stringDecrypter.Method)); + newMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Pop)); + newMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + + var cctor = module.GlobalType.FindOrCreateStaticConstructor(); + var blocks = new Blocks(cctor); + var block = blocks.MethodBlocks.getAllBlocks()[0]; + block.insert(0, Instruction.Create(OpCodes.Call, newMethod)); + + IList allInstructions; + IList allExceptionHandlers; + blocks.getCode(out allInstructions, out allExceptionHandlers); + DotNetUtils.restoreBody(cctor, allInstructions, allExceptionHandlers); + } + public override IEnumerable getStringDecrypterMethods() { var list = new List(); if (stringDecrypter.Method != null) diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs index 2813e55d..05ccc01f 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs @@ -45,6 +45,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { StreamHelperType streamHelperType; EfConstantsReader stringMethodConsts; bool isV32OrLater; + int? validStringDecrypterValue; class StreamHelperType { public TypeDef type; @@ -76,6 +77,10 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } } + public int? ValidStringDecrypterValue { + get { return validStringDecrypterValue;} + } + public TypeDef Type { get { return stringType; } } @@ -391,6 +396,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } public string decrypt(int val) { + validStringDecrypterValue = val; while (true) { int offset = magic1 ^ i3 ^ val ^ i6; reader.BaseStream.Position = offset; From 4a205e6093c28af73921925ee9dc884ac793ee12 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 8 Dec 2012 12:08:09 +0100 Subject: [PATCH 21/71] Add updated submodule --- dot10 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot10 b/dot10 index 2b81c474..0b93864f 160000 --- a/dot10 +++ b/dot10 @@ -1 +1 @@ -Subproject commit 2b81c474d9bde1def25b17fbfb6002ebf0ad4a85 +Subproject commit 0b93864f38fba97187d3569efd2496950dfe368d From 219736651d0022908322fd385a8ccd14cab5496e Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 8 Dec 2012 12:13:21 +0100 Subject: [PATCH 22/71] Update --preserve-table argument to allow 'all' and to disable table(s) --- de4dot.cui/CommandLineParser.cs | 40 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/de4dot.cui/CommandLineParser.cs b/de4dot.cui/CommandLineParser.cs index 05105f3c..1225355c 100644 --- a/de4dot.cui/CommandLineParser.cs +++ b/de4dot.cui/CommandLineParser.cs @@ -169,23 +169,35 @@ namespace de4dot.cui { MetaDataFlags.PreserveBlobOffsets | MetaDataFlags.PreserveExtraSignatureData; })); - miscOptions.Add(new OneArgOption(null, "preserve-table", "Preserve rids in table: tr (TypeRef), td (TypeDef), fd (Field), md (Method), pd (Param), mr (MemberRef), s (StandAloneSig), ed (Event), pr (Property), ts (TypeSpec), ms (MethodSpec). Can be combined: ed,fd,md", "flags", (val) => { - foreach (var s in val.Split(',')) { + miscOptions.Add(new OneArgOption(null, "preserve-table", "Preserve rids in table: tr (TypeRef), td (TypeDef), fd (Field), md (Method), pd (Param), mr (MemberRef), s (StandAloneSig), ed (Event), pr (Property), ts (TypeSpec), ms (MethodSpec), all (all previous tables). Use - to disable (eg. all,-pd). Can be combined: ed,fd,md", "flags", (val) => { + foreach (var t in val.Split(',')) { + var s = t.Trim(); + if (s.Length == 0) + continue; + bool clear = s[0] == '-'; + if (clear) + s = s.Substring(1); + MetaDataFlags flag; switch (s.Trim()) { - case "": break; - case "tr": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveTypeRefRids; break; - case "td": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveTypeDefRids; break; - case "fd": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveFieldRids; break; - case "md": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveMethodRids; break; - case "pd": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveParamRids; break; - case "mr": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveMemberRefRids; break; - case "s": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveStandAloneSigRids; break; - case "ed": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveEventRids; break; - case "pr": filesOptions.MetaDataFlags |= MetaDataFlags.PreservePropertyRids; break; - case "ts": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveTypeSpecRids; break; - case "ms": filesOptions.MetaDataFlags |= MetaDataFlags.PreserveMethodSpecRids; break; + case "": flag = 0; break; + case "all": flag = MetaDataFlags.PreserveRids; break; + case "tr": flag = MetaDataFlags.PreserveTypeRefRids; break; + case "td": flag = MetaDataFlags.PreserveTypeDefRids; break; + case "fd": flag = MetaDataFlags.PreserveFieldRids; break; + case "md": flag = MetaDataFlags.PreserveMethodRids; break; + case "pd": flag = MetaDataFlags.PreserveParamRids; break; + case "mr": flag = MetaDataFlags.PreserveMemberRefRids; break; + case "s": flag = MetaDataFlags.PreserveStandAloneSigRids; break; + case "ed": flag = MetaDataFlags.PreserveEventRids; break; + case "pr": flag = MetaDataFlags.PreservePropertyRids; break; + case "ts": flag = MetaDataFlags.PreserveTypeSpecRids; break; + case "ms": flag = MetaDataFlags.PreserveMethodSpecRids; break; default: throw new UserException(string.Format("Invalid --preserve-table option: {0}", s)); } + if (clear) + filesOptions.MetaDataFlags &= ~flag; + else + filesOptions.MetaDataFlags |= flag; } })); miscOptions.Add(new NoArgOption(null, "preserve-strings", "Preserve #Strings heap offsets", () => { From a66ee5ff3916181e867318616d8f09591f0bd42d Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 8 Dec 2012 12:33:01 +0100 Subject: [PATCH 23/71] Don't print ERROR since the logger also adds it --- de4dot.cui/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs index 68b3cf20..e6b843ef 100644 --- a/de4dot.cui/Program.cs +++ b/de4dot.cui/Program.cs @@ -84,7 +84,7 @@ namespace de4dot.cui { exitCode = ex.code; } catch (UserException ex) { - Logger.Instance.LogErrorDontIgnore("ERROR: {0}", ex.Message); + Logger.Instance.LogErrorDontIgnore("{0}", ex.Message); exitCode = 1; } catch (Exception ex) { From 721cd1578a19c58c6050d01939fe9b1160d683c8 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 10 Dec 2012 21:42:14 +0100 Subject: [PATCH 24/71] Update EF version detector --- .../Eazfuscator_NET/VersionDetector.cs | 117 +++++++++++++++--- 1 file changed, 101 insertions(+), 16 deletions(-) diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs index 878aaf37..54bf084c 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/VersionDetector.cs @@ -616,6 +616,10 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { return "3.3"; } + ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////// + var fields33_149 = new string[] { getNestedTypeName(0), getNestedTypeName(1), @@ -662,8 +666,63 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { decryptStringMethod.Body.MaxStack <= 8 && (decryptStringMethod.Body.ExceptionHandlers.Count == 1 || decryptStringMethod.Body.ExceptionHandlers.Count == 2) && new LocalTypes(decryptStringMethod).exactly(locals33_149) && - checkTypeFields(fields33_149)) { - return "3.3"; // 3.3.149 (but not SL or CF) + checkTypeFields2(fields33_149)) { + return "3.3.149 - 3.4"; // 3.3.149+ (but not SL or CF) + } + + ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////// + + var fields35 = new string[] { + getNestedTypeName(0), + getNestedTypeName(1), + "System.Byte[]", + "System.Int16", + "System.Int32", + "System.Byte[]", + "System.Int32", + "System.Int32", + getNestedTypeName(2), + }; + var locals35 = createLocalsArray( + "System.Boolean", + "System.Byte", + "System.Byte[]", + "System.Char[]", + "System.Collections.Generic.IEnumerator`1", + getNestedTypeName(0), + "System.Diagnostics.StackFrame", + "System.Diagnostics.StackTrace", + "System.Int16", + "System.Int32", + "System.Int64", + "System.IO.Stream", + "System.Reflection.Assembly", + "System.Reflection.AssemblyName", + "System.Reflection.MethodBase", + "System.String", + "System.Text.StringBuilder", + "System.Type" + ); + var olocals35 = createLocalsArray( + "System.Int32" + ); + if (otherMethods.Count == 1 && + decryptStringType.NestedTypes.Count == 3 && + DotNetUtils.isMethod(otherMethods[0], "System.Void", "(System.Byte[],System.Int32,System.Byte[])") && + otherMethods[0].IsPrivate && + otherMethods[0].IsStatic && + new LocalTypes(otherMethods[0]).exactly(olocals35) && + decryptStringMethod.IsNoInlining && + decryptStringMethod.IsAssembly && + !decryptStringMethod.IsSynchronized && + decryptStringMethod.Body.MaxStack >= 1 && + decryptStringMethod.Body.MaxStack <= 8 && + decryptStringMethod.Body.ExceptionHandlers.Count >= 2 && + new LocalTypes(decryptStringMethod).all(locals35) && + checkTypeFields2(fields35)) { + return "3.5"; } } @@ -673,21 +732,28 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { TypeDef getNestedType(int n) { var type = stringDecrypter.Type; - int fieldIndex; - switch (n) { - case 0: fieldIndex = 0; break; - case 1: fieldIndex = 1; break; - case 2: fieldIndex = 8; break; - default: throw new ApplicationException("Invalid index: " + n); + if (n == 0) { + foreach (var nested in type.NestedTypes) { + if (nested.NestedTypes.Count == 1) + return nested; + } } - - if (fieldIndex >= type.Fields.Count) - return null; - var nestedType = type.Fields[fieldIndex].FieldType.TryGetTypeDef(); - if (nestedType == null || type.NestedTypes.IndexOf(nestedType) < 0) - return null; - - return nestedType; + else if (n == 1) { + foreach (var nested in type.NestedTypes) { + if (nested.IsEnum) + continue; + if (nested.NestedTypes.Count != 0) + continue; + return nested; + } + } + else if (n == 2) { + foreach (var nested in type.NestedTypes) { + if (nested.IsEnum) + return nested; + } + } + return null; } string getNestedTypeName(int n) { @@ -705,6 +771,25 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { return true; } + bool checkTypeFields2(string[] fieldTypes) { + if (fieldTypes.Length != stringDecrypter.Type.Fields.Count) + return false; + + var fieldTypes1 = new List(fieldTypes); + fieldTypes1.Sort(); + + var fieldTypes2 = new List(); + foreach (var f in stringDecrypter.Type.Fields) + fieldTypes2.Add(f.FieldType.FullName); + fieldTypes2.Sort(); + + for (int i = 0; i < fieldTypes1.Count; i++) { + if (fieldTypes1[i] != fieldTypes2[i]) + return false; + } + return true; + } + static Dictionary removeLocals_cf = new Dictionary(StringComparer.Ordinal) { { "System.Diagnostics.StackFrame", true }, { "System.Diagnostics.StackTrace", true }, From 61eff4008220fb535c6bfdcc07c2674e3c7976bd Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 10 Dec 2012 21:42:37 +0100 Subject: [PATCH 25/71] Add props to access the locals / values --- de4dot.code/deobfuscators/ConstantsReader.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/de4dot.code/deobfuscators/ConstantsReader.cs b/de4dot.code/deobfuscators/ConstantsReader.cs index ffd1cb52..8326677f 100644 --- a/de4dot.code/deobfuscators/ConstantsReader.cs +++ b/de4dot.code/deobfuscators/ConstantsReader.cs @@ -31,6 +31,18 @@ namespace de4dot.code.deobfuscators { protected Dictionary localsValuesDouble = new Dictionary(); bool emulateConvInstrs; + public IEnumerable> Locals32 { + get { return localsValuesInt32; } + } + + public IEnumerable> Locals64 { + get { return localsValuesInt64; } + } + + public IEnumerable> LocalsDouble { + get { return localsValuesDouble; } + } + public interface IInstructions { int Count { get; } Instruction this[int index] { get; } From ac7694b237d97c626d0a812b67e375ce8feec413 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 10 Dec 2012 21:42:49 +0100 Subject: [PATCH 26/71] Add Int64Method property --- de4dot.code/deobfuscators/Eazfuscator_NET/DecrypterType.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/DecrypterType.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/DecrypterType.cs index b05a931f..2a8d3639 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/DecrypterType.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/DecrypterType.cs @@ -36,6 +36,10 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { int m1_i1, m2_i1, m2_i2, m3_i1; MethodDef[] efConstMethods; + public MethodDef Int64Method { + get { return int64Method; } + } + public TypeDef Type { get { return type; } set { From d5681d9db4877483464e7f8d968659da5d19f1ac Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 10 Dec 2012 21:43:56 +0100 Subject: [PATCH 27/71] Emulate instructions instead of finding constants --- .../Eazfuscator_NET/StringDecrypter.cs | 79 +++++++++++++------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs index 05ccc01f..c1c948e1 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs @@ -24,6 +24,7 @@ using System.Text; using dot10.DotNet; using dot10.DotNet.Emit; using de4dot.blocks; +using de4dot.blocks.cflow; namespace de4dot.code.deobfuscators.Eazfuscator_NET { class StringDecrypter { @@ -249,8 +250,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { if (isV32OrLater) { bool initializedAll; - if (!findInts(out initializedAll)) - return false; + int index = findInitIntsIndex(stringMethod, out initializedAll); var cctor = stringType.FindStaticConstructor(); if (!initializedAll && cctor != null) { @@ -261,6 +261,9 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { if (decrypterType.Detected && !decrypterType.initialize()) return false; + + if (!findInts(index)) + return false; } initializeFlags(); @@ -389,10 +392,6 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { theKey = reader.ReadBytes(len); else keyLen = reader.ReadInt16() ^ s2; - - magic1 = i1 ^ i2; - if (decrypterType.Detected) - magic1 ^= (int)decrypterType.getMagic(); } public string decrypt(int val) { @@ -586,40 +585,68 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { return stringMethodConsts.getInt16(ref index, out s); } - bool findInts(out bool initializedAll) { - int index = findInitIntsIndex(stringMethod, out initializedAll); + bool findInts(int index) { if (index < 0) return false; i2 = 0; - bool returnValue = false; var instrs = stringMethod.Body.Instructions; + + var emu = new InstructionEmulator(stringMethod); + foreach (var kv in stringMethodConsts.Locals32) + emu.setLocal(kv.Key, new Int32Value(kv.Value)); + + var fields = new Dictionary(); for (int i = index; i < instrs.Count - 2; i++) { var instr = instrs[i]; - if (instr.OpCode.Code == Code.Ldsfld && - instrs[i + 1].OpCode.Code == Code.Ldc_I4 && - (int)instrs[i + 1].Operand == 268435314) - break; - if (instr.OpCode.Code != Code.Call && instr.OpCode.FlowControl != FlowControl.Next) + FieldDef field; + switch (instr.OpCode.Code) { + case Code.Ldsfld: + field = instr.Operand as FieldDef; + if (field == null || field.DeclaringType != stringMethod.DeclaringType || field.FieldType.GetElementType() != ElementType.I4) + goto default; + fields[field] = null; + emu.push(new Int32Value(i1)); break; - if (!stringMethodConsts.isLoadConstantInt32(instr)) - continue; + case Code.Stsfld: + field = instr.Operand as FieldDef; + if (field == null || field.DeclaringType != stringMethod.DeclaringType || field.FieldType.GetElementType() != ElementType.I4) + goto default; + if (fields.ContainsKey(field) && fields[field] == null) + goto default; + var val = emu.pop() as Int32Value; + if (val == null || !val.allBitsValid()) + fields[field] = null; + else + fields[field] = val.value; + break; - int tmp; - if (!stringMethodConsts.getNextInt32(ref i, out tmp)) - continue; - if ((instrs[i - 1].OpCode.Code == Code.Xor && instrs[i].IsStloc()) || - (instrs[i].OpCode.Code == Code.Xor && instrs[i + 1].IsStloc()) || - instrs[i].IsLdloc()) { - i2 ^= tmp; - returnValue = true; + case Code.Call: + var method = instr.Operand as MethodDef; + if (!decrypterType.Detected || method != decrypterType.Int64Method) + goto done; + emu.push(new Int64Value((long)decrypterType.getMagic())); + break; + + default: + if (instr.OpCode.FlowControl != FlowControl.Next) + goto done; + emu.emulate(instr); + break; } - i--; + } +done: ; + + foreach (var val in fields.Values) { + if (val == null) + continue; + magic1 = i2 = val.Value; + return true; } - return returnValue; + return false; } static int findInitIntsIndex(MethodDef method, out bool initializedAll) { From b8430ea2e388d25ee3d45bb3ba948a784b9ccfa3 Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 10 Dec 2012 21:51:58 +0100 Subject: [PATCH 28/71] Update README --- README.md | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) diff --git a/README.md b/README.md index e69de29b..5317484b 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,264 @@ +Description +=========== + +de4dot is an open source (GPLv3) .NET deobfuscator and unpacker written in C#. It will try its best to restore a packed and obfuscated assembly to almost the original assembly. Most of the obfuscation can be completely restored (eg. string encryption), but symbol renaming is impossible to restore since the original names aren't (usually) part of the obfuscated assembly. + +Features +======== + +Here's a pseudo random list of the things it will do depending on what obfuscator was used to obfuscate an assembly: + +* Inline methods. Some obfuscators move small parts of a method to another static method and calls it. +* Decrypt strings statically or dynamically +* Decrypt other constants. Some obfuscators can also encrypt other constants, such as all integers, all doubles, etc. +* Decrypt methods statically or dynamically +* Remove proxy methods. Many obfuscators replace most/all call instructions with a call to a delegate. This delegate in turn calls the real method. +* Rename symbols. Even though most symbols can't be restored, it will rename them to human readable strings. Sometimes, some of the original names can be restored, though. +* Devirtualize virtualized code +* Decrypt resources. Many obfuscators have an option to encrypt .NET resources. +* Decrypt embedded files. Many obfuscators have an option to embed and possibly encrypt/compress other assemblies. +* Remove tamper detection code +* Remove anti-debug code +* Control flow deobfuscation. Many obfuscators modify the IL code so it looks like spaghetti code making it very difficult to understand the code. +* Restore class fields. Some obfuscators can move fields from one class to some other obfuscator created class. +* Convert a PE exe to a .NET exe. Some obfuscators wrap a .NET assembly inside a Win32 PE so a .NET decompiler can't read the file. +* Removes most/all junk classes added by the obfuscator. +* Fixes some peverify errors. Many of the obfuscators are buggy and create unverifiable code by mistake. +* Restore the types of method parameters and fields + + +Supported obfuscators/packers +============================= + +* Agile.NET (aka CliSecure) +* Babel.NET +* CodeFort +* CodeVeil +* CodeWall +* CryptoObfuscator +* DeepSea Obfuscator +* Dotfuscator +* .NET Reactor +* Eazfuscator.NET +* Goliath.NET +* ILProtector +* MaxtoCode +* MPRESS +* Rummage +* Skater.NET +* SmartAssembly +* Spices.Net +* Xenocode + +Some of the above obfuscators are rarely used (eg. Goliath.NET), so they have had much less testing. Help me out by reporting bugs or problems you find. + + +Tiny FAQ +======== + +Is this a cracker only tool? +---------------------------- + +Of course. Not. Here's some legitimate uses of this software: + +* Malware analysis + +Many malware try to protect against analysis. They think obfuscating the code makes it hard. Mistake no. 1 was to use .NET. + +* Speed up a program / use less memory + +Unless only symbol renaming was used, the obfuscated assembly is usually slower and requires more memory at runtime compared to the original assembly. By unpacking and deobfuscating it, the program's memory usage and speed will be almost identical to the original program. + +* Make the assembly compatible with mono + +Most obfuscators don't support mono, even if the original assembly does. By unpacking and deobfuscating it, mono support will be restored. + +* You lost your source code and only have the obfuscated .NET assemblies + +By unpacking and deobfuscating your assemblies, you can then use any .NET decompiler (eg. the open source ILSpy) to get back your source code. + +* Obfuscator created unverifiable code but code must be verifiable + +Some of the obfuscators are buggy and create unverifiable code due to bugs in the software. Some of these errors are fixed by de4dot. + + +I've "protected" my app with some obfuscator but I just found out about de4dot. Is .NET obfuscation useless? +------------------------------------------------------------------------------------------------------------ + +Yes. It's simply way too easy to restore most of these "protections". + + +What do you think of these obfuscators? They're good, right? +------------------------------------------------------------ + +:D + +Speaking from experience with a lot of obfuscators, I can say that their protection is really weak. You see the same weak "protection" in pretty much every obfuscator. Copying ideas from other obfuscators seems to be their best skill. + +99% of the people working for these companies have absolutely no experience in reverse engineering. If you have no experience in what is a good or a bad protection, it's very unlikely that you're able to write a good protection. + +To show you an example, most obfuscators can encrypt all the strings in your assemblies. What they fail to tell you is that it's child's play to decrypt the strings. Here's an example from SecureTeam's Agile.NET (aka CliSecure). de4dot's Agile.NET string decrypter code is only 85 lines long, and that includes the GPLv3 comment at the top of the file and the code that detects the string decrypter in the assembly! + +The actual string decrypter code is 4 lines long, and it's a simple XOR loop! When Agile.NET (aka CliSecure) encrypts your strings, it replaces the original strings with an XOR'd copy, and adds a call to their string decrypter. This decrypter merely XOR's every character and returns the decrypted string. Here's the string decrypter code de4dot uses: + +```C# + public string decrypt(string es) { + char[] buf = new char[es.Length]; + for (int i = 0; i < es.Length; i++) + buf[i] = (char)(es[i] ^ stringDecrypterKey[i % stringDecrypterKey.Length]); + return new string(buf); + } +``` + +Your code might look like this: + +```C# + string myString = "Hello World"; +``` + +and the obfuscator (eg. Agile.NET / CliSecure) will replace that with something similar to this: + +```C# + string myString = DecryptClass.decrypt("AoF41Fk5422"); +``` + +Yes, Agile.NET's string encryption feature really is this bad! I bet you that none of their customers knows about this. And SecureTeam sure wants to keep it that way. :) + +Even though most of the other obfuscators' string encryption feature isn't as bad as Agile.NET's string encryption, they still have one thing in common: it's very easy to decrypt the strings again. + +I must use .NET so what's the best protection? +---------------------------------------------- + +If you don't count "don't distribute it" as a solution, the best obfuscator feature is symbol renaming. It's impossible to restore the symbols unless they're part of the assembly. All of the other "protections" are 100% reversible. + + +How to use de4dot +================= + +N00b users +---------- + +If you're command line challenged, drag and drop the file(s) onto de4dot.exe and wait a few seconds. + +Deobfuscate more than one file at a time +---------------------------------------- + +When more than one assembly has been obfuscated, it's very likely that you must deobfuscate them all at the same time unless you disable symbol renaming. The reason is that if assembly A has a reference to class C in assembly B, and you rename symbols only in assembly B, then class C could be renamed to eg. Class0 but the reference in assembly A still references a class called C in assembly B. If you deobfuscate both assemblies at the same time, all references will also be updated. + +Find all obfuscated files and deobfuscate them +---------------------------------------------- + +The following command line will deobfuscate all assemblies that have been obfuscated by a supported obfuscator and save the assemblies to `c:\output` + + de4dot -r c:\input -ru -ro c:\output + +`-r` means recursive search. `-ru` means it should ignore unknown files. `-ro` means it should place the output files in the following directory. Typically, you'd first copy `c:\input` to `c:\output`, and then run the command. That way all the files will be in `c:\output`, even non-assemblies and non-processed assemblies. When de4dot is finished, you'd just double click the main assembly in `c:\output` and it should hopefully start. + +Detect obfuscator +----------------- + +Use the `-d` option to detect the obfuscator without deobfuscating any assembly. + +Find all .NET assemblies and detect obfuscator. If it's an unsupported obfuscator or if it's not obfuscated, it will print "Unknown obfuscator". + + de4dot -d -r c:\input + +Same as above except that it will only show which files have been obfuscated by a supported obfuscator. + + de4dot -d -r c:\input -ru + +Detect obfuscator + + de4dot -d file1.dll file2.dll file3.dll + +Preserving metadata tokens +-------------------------- + +Sometimes in rare cases, you'd want to preserve the metadata tokens. Use `--preserve-tokens` or `--preserve-table`. Also consider using `--keep-types` since it won't remove any types and methods added by the obfuscator. Another useful option is `--dont-create-params`. If used, the renamer won't create Param rows for method parameters that don't have a Param row. That way the ParamPtr table won't be added to your assemblies. Peverify has a bug and doesn't support it (you'll see lots of "errors"). + +The #Strings, #US and #Blob heaps can also be preserved by using `--preserve-strings`, `--preserve-us`, and `--preserve-blob` respectively. Of these three, `--preserve-us` is the most useful one since `ldstr` instruction and `module.ResolveString()` directly reference the #US heap. + +`--preserve-sig-data` should be used if the obfuscator adds extra data at the end of signatures that it uses for its own purpose, eg. as decryption keys. Confuser is one obfuscator that does this. + +`--preserve-tokens` preserves all important tokens but will also enable `--preserve-us`, `--preserve-blob` and `--preserve-sig-data`. + +If it's detected as an unknown (unsupported) obfuscator (or if you force it with `-p un`), all tokens are preserved, including the #US heap and any extra data at the end of signatures. Also, no obfuscator types, fields or methods are removed. + +Preserve all important tokens, #US, #Blob, extra sig data. + + de4dot --preserve-tokens file1.dll + +Preserve all important tokens, #US, #Blob, extra sig data and don't remove types/fields added by the obfuscator + + de4dot --keep-types --preserve-tokens file1.dll + +Preserve all important tokens, #US, #Blob, extra sig data and don't create extra Param rows to prevent the ParamPtr table from being created. + + de4dot --dont-create-params --preserve-tokens file1.dll + +Preserve all important tokens except the Param tokens. + + de4dot --preserve-table all,-pd file1.dll + +Dynamically decrypt strings +--------------------------- + +Although `de4dot` supports a lot of obfuscators, there's still some it doesn't support. To decrypt strings, you'll first need to figure out which method or methods decrypt strings. To get the method token of these string decrypters, you can use ILDASM with the 'show metadata tokens' option enabled. A method token is a 32-bit number and begins with 06, eg. 06012345. + +This command will load assembly file1.dll into memory by calling `Assembly.Load()`. When it detects calls to the two string decrypters (06012345 and 060ABCDE), it will call them by creating a dynamic method, and save the result (the decrypted string). The call to the string decrypter will be removed and the decrypted string will be in its place. + + de4dot file1.dll --strtyp delegate --strtok 06012345 --strtok 060ABCDE + +Since the assembly is loaded and executed, make sure you run this in a sandbox if you suspect the file to be malware. + +Forcing detection of a certain obfuscator +----------------------------------------- + +`de4dot` isn't perfect. If it fails to detect an obfuscator, you can use the `-p` option to force it to assume it's been obfuscated by it. + +Force SmartAssembly + + de4dot file1.dll -p sa + +Force unsupported obfuscator + + de4dot file1.dll -p un + +For other obfuscator types, see the help screen. + +Disable renaming symbols +------------------------ + +Renaming symbols isn't as easy as renaming A to B when reflection is involved. `de4dot` currently doesn't support renaming XAML so if you suspect that it uses WPF (or if it's a Silverlight app) you should disable renaming if the assembly fails to run. + + de4dot --dont-rename file1.dll file2.dll + +`--keep-names` can also be used to tell `de4dot` not to rename certain symbols, eg. "don't rename fields". + +Rename everything that should be renamed except properties, events and methods. + + de4dot --keep-names pem file1.dll + +Using a different rename regex +------------------------------ + +The default regexes should be enough, except possibly the one that is used when an unsupported obfuscator is detected. To see all default regexes, start `de4dot` without any arguments and it will list all options and all default values. + +Eg., currently the following is the default regex used when Dotfuscator is detected + + !^[a-z][a-z0-9]{0,2}$&!^A_[0-9]+$&^[a-zA-Z_<{$][a-zA-Z_0-9<>{}$.`-]*$ + +As you can see, it's not just one regex, it's more than one. Each is separated by `&` and each regex can be negated by using `!` in front of it. To show it more clearly, these regexes are used: + + (negated) ^[a-z][a-z0-9]{0,2}$ + (negated) ^A_[0-9]+$ + ^[a-zA-Z_<{$][a-zA-Z_0-9<>{}$.`-]*$ + +To change the regex(es), you must know the short type name of the obfuscator (see help screen). Eg. it's `sa` if it's SmartAssembly, and `un` if it's an unsupported/unknown obfuscator. The option to use is `--TYPE-name` (eg. `--sa-name` for SmartAssembly and `--un-name` for unknown/unsupported obfuscators): + + de4dot --un-name "^[a-zA-Z]\w*$" file1.dll + +Other options +------------- + +Start `de4dot` without any arguments and it will show all options. From 245d875d5ff1449b7e72859c68d8d934a41e9fac Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 11 Dec 2012 00:23:16 +0100 Subject: [PATCH 29/71] Support Eazfuscator.NET 3.5 string encrypter --- de4dot.code/de4dot.code.csproj | 1 + .../Eazfuscator_NET/Deobfuscator.cs | 1 + .../deobfuscators/Eazfuscator_NET/Dynocode.cs | 286 ++++++++++++++++++ .../Eazfuscator_NET/StringDecrypter.cs | 77 +++++ 4 files changed, 365 insertions(+) create mode 100644 de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 72a87098..a44e993b 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -153,6 +153,7 @@ + diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs index e95669fc..9de3dd16 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Deobfuscator.cs @@ -156,6 +156,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { if (CanRemoveStringDecrypterType) { addTypesToBeRemoved(stringDecrypter.Types, "String decrypter type"); addTypeToBeRemoved(decrypterType.Type, "Decrypter type"); + addTypesToBeRemoved(stringDecrypter.DynocodeTypes, "Dynocode type"); addResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings"); } addTypeToBeRemoved(assemblyResolver.Type, "Assembly resolver type"); diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs new file mode 100644 index 00000000..2454bfa5 --- /dev/null +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs @@ -0,0 +1,286 @@ +/* + Copyright (C) 2011-2012 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.Collections.Generic; +using dot10.DotNet; +using dot10.DotNet.Emit; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Eazfuscator_NET { + interface IDynocodeGenerator { + IEnumerable getValues(int input); + } + + // Something added in EF 3.5 which they call Dynocode. The string decrypter can now + // call some iterator classes that will return some integers that it will use to + // XOR some key. + class Dynocode { + ISimpleDeobfuscator simpleDeobfuscator; + Dictionary typeToDCGen = new Dictionary(); + + class DCGen1 : IDynocodeGenerator { + public int magic1; + public int magic2; + public int magic3; + public int magic4; + public int magic5; + public int magic6; + public int magic7; + + public IEnumerable getValues(int input) { + yield return magic1; + yield return magic2; + yield return input ^ magic3; + yield return magic4; + yield return magic5; + yield return magic6; + yield return input ^ magic7; + } + } + + class DCGen2 : IDynocodeGenerator { + public int magic1; + + public IEnumerable getValues(int input) { + int x = 0; + int y = 1; + while (true) { + yield return y; + if (--input == 0) + break; + int tmp = y; + y = (x + y + input) ^ magic1; + x = tmp; + } + } + } + + class DCGen3 : IDynocodeGenerator { + public int magic1; + public int magic2; + public DCGen2 dc2; + + public IEnumerable getValues(int input) { + int i = 7; + foreach (var val in dc2.getValues(input)) { + int x = val ^ input; + if ((x % 4) == 0) + x ^= magic1; + if ((x % 16) == 0) + x ^= magic2; + yield return x; + if (--i == 0) + break; + } + } + } + + public IEnumerable Types { + get { return typeToDCGen.Keys; } + } + + public Dynocode(ISimpleDeobfuscator simpleDeobfuscator) { + this.simpleDeobfuscator = simpleDeobfuscator; + } + + public IDynocodeGenerator getDynocodeGenerator(TypeDef type) { + if (type == null) + return null; + var dt = type.DeclaringType; + if (dt == null) + return null; + IDynocodeGenerator dcGen; + if (typeToDCGen.TryGetValue(type, out dcGen)) + return dcGen; + + if (dt.NestedTypes.Count == 1) + dcGen = getDCGen1(type); + else if (dt.NestedTypes.Count == 2) + dcGen = getDCGen3(type); + + typeToDCGen[type] = dcGen; + + return dcGen; + } + + DCGen1 getDCGen1(TypeDef type) { + var method = getMoveNext(type); + if (method == null) + return null; + simpleDeobfuscator.deobfuscate(method); + var swLabels = getSwitchLabels(method); + if (swLabels == null || swLabels.Count < 7) + return null; + + var dcGen = new DCGen1(); + if (!getMagicDC1(method, swLabels[0], out dcGen.magic1)) + return null; + if (!getMagicDC1(method, swLabels[1], out dcGen.magic2)) + return null; + if (!getMagicXorDC1(method, swLabels[2], out dcGen.magic3)) + return null; + if (!getMagicDC1(method, swLabels[3], out dcGen.magic4)) + return null; + if (!getMagicDC1(method, swLabels[4], out dcGen.magic5)) + return null; + if (!getMagicDC1(method, swLabels[5], out dcGen.magic6)) + return null; + if (!getMagicXorDC1(method, swLabels[6], out dcGen.magic7)) + return null; + + return dcGen; + } + + static IList getSwitchLabels(MethodDef method) { + foreach (var instr in method.Body.Instructions) { + if (instr.OpCode.Code != Code.Switch) + continue; + return instr.Operand as IList; + } + return null; + } + + static bool getMagicDC1(MethodDef method, Instruction target, out int magic) { + magic = 0; + var instrs = method.Body.Instructions; + int index = instrs.IndexOf(target); + if (index < 0) + return false; + + for (int i = index; i < instrs.Count - 3; i++) { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + return false; + if (instr.OpCode.Code != Code.Stfld) + continue; + if (instrs[i + 1].OpCode.Code != Code.Ldarg_0) + continue; + var ldci4 = instrs[i + 2]; + if (!ldci4.IsLdcI4()) + continue; + if (instrs[i + 3].OpCode.Code != Code.Stfld) + continue; + + magic = ldci4.GetLdcI4Value(); + return true; + } + + return false; + } + + static bool getMagicXorDC1(MethodDef method, Instruction target, out int magic) { + magic = 0; + var instrs = method.Body.Instructions; + int index = instrs.IndexOf(target); + if (index < 0) + return false; + + for (int i = index; i < instrs.Count - 2; i++) { + var instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + return false; + if (!instr.IsLdcI4()) + continue; + if (instrs[i + 1].OpCode.Code != Code.Xor) + continue; + if (instrs[i + 2].OpCode.Code != Code.Stfld) + continue; + + magic = instr.GetLdcI4Value(); + return true; + } + + return false; + } + + DCGen3 getDCGen3(TypeDef type) { + var method = getMoveNext(type); + if (method == null) + return null; + simpleDeobfuscator.deobfuscate(method); + + var dcGen = new DCGen3(); + int index = 0; + if (!getMagicDC3(method, ref index, out dcGen.magic1)) + return null; + if (!getMagicDC3(method, ref index, out dcGen.magic2)) + return null; + + var dt = type.DeclaringType; + dcGen.dc2 = getDCGen2(dt.NestedTypes[0] == type ? dt.NestedTypes[1] : dt.NestedTypes[0]); + + return dcGen; + } + + static bool getMagicDC3(MethodDef method, ref int index, out int magic) { + var instrs = method.Body.Instructions; + for (int i = index; i < instrs.Count - 2; i++) { + var ldci4 = instrs[i]; + if (!ldci4.IsLdcI4()) + continue; + if (instrs[i + 1].OpCode.Code != Code.Xor) + continue; + if (instrs[i + 2].OpCode.Code != Code.Stfld) + continue; + + index = i + 3; + magic = ldci4.GetLdcI4Value(); + return true; + } + + magic = 0; + return false; + } + + DCGen2 getDCGen2(TypeDef type) { + var method = getMoveNext(type); + if (method == null) + return null; + simpleDeobfuscator.deobfuscate(method); + + var dcGen = new DCGen2(); + int index = 0; + if (!getMagicDC3(method, ref index, out dcGen.magic1)) + return null; + + return dcGen; + } + + static MethodDef getMoveNext(TypeDef type) { + foreach (var m in type.Methods) { + if (!m.IsVirtual) + continue; + foreach (var mo in m.Overrides) { + if (mo.MethodDeclaration.FullName == "System.Boolean System.Collections.IEnumerator::MoveNext()") + return m; + } + } + foreach (var m in type.Methods) { + if (!m.IsVirtual) + continue; + if (m.Name != "MoveNext") + continue; + if (!DotNetUtils.isMethod(m, "System.Boolean", "()")) + continue; + return m; + } + return null; + } + } +} diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs index c1c948e1..fe07d7ea 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/StringDecrypter.cs @@ -47,6 +47,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { EfConstantsReader stringMethodConsts; bool isV32OrLater; int? validStringDecrypterValue; + Dynocode dynocode; class StreamHelperType { public TypeDef type; @@ -99,6 +100,10 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } } + public IEnumerable DynocodeTypes { + get { return dynocode.Types; } + } + public MethodDef Method { get { return stringMethod; } } @@ -223,6 +228,7 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } bool findConstants(ISimpleDeobfuscator simpleDeobfuscator) { + dynocode = new Dynocode(simpleDeobfuscator); simpleDeobfuscator.deobfuscate(stringMethod); stringMethodConsts = new EfConstantsReader(stringMethod); @@ -630,6 +636,11 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { emu.push(new Int64Value((long)decrypterType.getMagic())); break; + case Code.Newobj: + if (!emulateDynocode(emu, ref i)) + goto default; + break; + default: if (instr.OpCode.FlowControl != FlowControl.Next) goto done; @@ -649,6 +660,72 @@ done: ; return false; } + bool emulateDynocode(InstructionEmulator emu, ref int index) { + var instrs = stringMethod.Body.Instructions; + var instr = instrs[index]; + + var ctor = instr.Operand as MethodDef; + if (ctor == null || ctor.MethodSig.GetParamCount() != 1 || ctor.MethodSig.Params[0].ElementType != ElementType.I4) + return false; + + if (index + 4 >= instrs.Count) + return false; + var ldloc = instrs[index + 3]; + if (!ldloc.IsLdloc() || instrs[index + 4].OpCode.Code != Code.Stfld) + return false; + + var initValue = emu.getLocal(ldloc.GetLocal(stringMethod.Body.Variables)) as Int32Value; + if (initValue == null || !initValue.allBitsValid()) + return false; + + int leaveIndex = findLeave(instrs, index); + if (leaveIndex < 0) + return false; + var afterLoop = instrs[leaveIndex].Operand as Instruction; + if (afterLoop == null) + return false; + int newIndex = instrs.IndexOf(afterLoop); + var loopLocal = getDCLoopLocal(index, newIndex); + if (loopLocal == null) + return false; + var initValue2 = emu.getLocal(loopLocal) as Int32Value; + if (initValue2 == null || !initValue2.allBitsValid()) + return false; + + var dcGen = dynocode.getDynocodeGenerator(ctor.DeclaringType); + if (dcGen == null) + return false; + int loopLocalValue = initValue2.value; + foreach (var val in dcGen.getValues(initValue.value)) + loopLocalValue ^= val; + + emu.setLocal(loopLocal, new Int32Value(loopLocalValue)); + emu.emulate(instr); + index = newIndex - 1; + return true; + } + + Local getDCLoopLocal(int start, int end) { + var instrs = stringMethod.Body.Instructions; + for (int i = start; i < end - 1; i++) { + if (instrs[i].OpCode.Code != Code.Xor) + continue; + var stloc = instrs[i + 1]; + if (!stloc.IsStloc()) + continue; + return stloc.GetLocal(stringMethod.Body.Variables); + } + return null; + } + + static int findLeave(IList instrs, int index) { + for (int i = index; i < instrs.Count; i++) { + if (instrs[i].OpCode.Code == Code.Leave_S || instrs[i].OpCode.Code == Code.Leave) + return i; + } + return -1; + } + static int findInitIntsIndex(MethodDef method, out bool initializedAll) { initializedAll = false; From b9d91043fc181e4b2fc360dfe2f502fd3a76282b Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 11 Dec 2012 12:02:40 +0100 Subject: [PATCH 30/71] Support the latest CryptoObfuscator version --- .../CryptoObfuscator/ResourceDecrypter.cs | 94 +++++++++---------- .../CryptoObfuscator/TamperDetection.cs | 2 +- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs b/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs index db3a1788..f4b9b598 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs @@ -125,24 +125,25 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { if (type.Fields.Count != 0) continue; - var method = getDecrypterMethod(type); - if (method == null) - continue; - if (!new LocalTypes(method).exactly(requiredLocals_v1)) - continue; - if (!DotNetUtils.callsMethod(method, "System.Int64", "()")) - continue; - if (!DotNetUtils.callsMethod(method, "System.Int32", "(System.Byte[],System.Int32,System.Int32)")) - continue; - if (!DotNetUtils.callsMethod(method, "System.Void", "(System.Array,System.Int32,System.Array,System.Int32,System.Int32)")) - continue; - if (!DotNetUtils.callsMethod(method, "System.Security.Cryptography.ICryptoTransform", "()")) - continue; - if (!DotNetUtils.callsMethod(method, "System.Byte[]", "(System.Byte[],System.Int32,System.Int32)")) - continue; + foreach (var method in getDecrypterMethods(type)) { + if (method == null) + continue; + if (!new LocalTypes(method).exactly(requiredLocals_v1)) + continue; + if (!DotNetUtils.callsMethod(method, "System.Int64", "()")) + continue; + if (!DotNetUtils.callsMethod(method, "System.Int32", "(System.Byte[],System.Int32,System.Int32)")) + continue; + if (!DotNetUtils.callsMethod(method, "System.Void", "(System.Array,System.Int32,System.Array,System.Int32,System.Int32)")) + continue; + if (!DotNetUtils.callsMethod(method, "System.Security.Cryptography.ICryptoTransform", "()")) + continue; + if (!DotNetUtils.callsMethod(method, "System.Byte[]", "(System.Byte[],System.Int32,System.Int32)")) + continue; - resourceDecrypterType = type; - return true; + resourceDecrypterType = type; + return true; + } } return false; } @@ -158,22 +159,24 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { continue; if (type.HasNestedTypes || type.HasGenericParameters) continue; - var method = getDecrypterMethod(type); - if (method == null) - continue; - if (!new LocalTypes(method).exactly(requiredLocals_sl)) - continue; - resourceDecrypterType = type; - break; + foreach (var method in getDecrypterMethods(type)) { + if (method == null) + continue; + if (!new LocalTypes(method).exactly(requiredLocals_sl)) + continue; + + resourceDecrypterType = type; + return; + } } } void initializeHeaderInfo(ISimpleDeobfuscator simpleDeobfuscator) { skipBytes = 0; - if (resourceDecrypterType != null) { - if (updateFlags(getDecrypterMethod(), simpleDeobfuscator)) + foreach (var method in getDecrypterMethods(resourceDecrypterType)) { + if (updateFlags(method, simpleDeobfuscator)) return; } @@ -203,7 +206,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { } bool updateFlags(MethodDef method, ISimpleDeobfuscator simpleDeobfuscator) { - if (method == null || method.Body == null) + if (method == null || method.Body == null || method.Body.Variables.Count < 3) return false; var constants = new List(); @@ -276,7 +279,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { if (loopCount < 2 || loopCount > 3) continue; var blt = instrs[i + 1]; - if (blt.OpCode.Code != Code.Blt && blt.OpCode.Code != Code.Blt_S) + if (blt.OpCode.Code != Code.Blt && blt.OpCode.Code != Code.Blt_S && blt.OpCode.Code != Code.Clt) continue; return loopCount - 1; } @@ -291,28 +294,25 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { return false; } - MethodDef getDecrypterMethod() { - return getDecrypterMethod(resourceDecrypterType); - } - - static MethodDef getDecrypterMethod(TypeDef type) { + static IEnumerable getDecrypterMethods(TypeDef type) { + if (type == null) + yield break; foreach (var method in type.Methods) { if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.IO.Stream)")) - return method; - if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Int32,System.IO.Stream)")) - return method; - if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Int16,System.IO.Stream)")) - return method; - if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Byte,System.IO.Stream)")) - return method; - if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.SByte,System.IO.Stream)")) - return method; - if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Byte,System.IO.Stream,System.Int32)")) - return method; - if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.SByte,System.IO.Stream,System.UInt32)")) - return method; + yield return method; + else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Int32,System.IO.Stream)")) + yield return method; + else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Int16,System.IO.Stream)")) + yield return method; + else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Byte,System.IO.Stream)")) + yield return method; + else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.SByte,System.IO.Stream)")) + yield return method; + else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.Byte,System.IO.Stream,System.Int32)")) + yield return method; + else if (DotNetUtils.isMethod(method, "System.Byte[]", "(System.SByte,System.IO.Stream,System.UInt32)")) + yield return method; } - return null; } public byte[] decrypt(Stream resourceStream) { diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs b/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs index 5d109be5..fa3ac9b3 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/TamperDetection.cs @@ -84,7 +84,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { if (!method.IsStatic || !DotNetUtils.isMethod(method, "System.Void", "()")) return false; - if (type.Methods.Count < 3 || type.Methods.Count > 14) + if (type.Methods.Count < 3 || type.Methods.Count > 16) return false; if (DotNetUtils.getPInvokeMethod(type, "mscoree", "StrongNameSignatureVerificationEx") != null) { } From 3e7d4033346a8758742b74acf2948a733caa6a6f Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 11 Dec 2012 12:36:59 +0100 Subject: [PATCH 31/71] Remove the dynocode declaring types --- de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs b/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs index 2454bfa5..4c9204cf 100644 --- a/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs +++ b/de4dot.code/deobfuscators/Eazfuscator_NET/Dynocode.cs @@ -92,7 +92,10 @@ namespace de4dot.code.deobfuscators.Eazfuscator_NET { } public IEnumerable Types { - get { return typeToDCGen.Keys; } + get { + foreach (var type in typeToDCGen.Keys) + yield return type.DeclaringType; + } } public Dynocode(ISimpleDeobfuscator simpleDeobfuscator) { From cf6af49ae7f17b89e97a9dc80a301197ee5cd9a9 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 13 Dec 2012 12:02:41 +0100 Subject: [PATCH 32/71] Add a CRC32 class --- de4dot.code/de4dot.code.csproj | 1 + de4dot.code/deobfuscators/CRC32.cs | 100 +++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 de4dot.code/deobfuscators/CRC32.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index a44e993b..45dfd2bc 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -123,6 +123,7 @@ + diff --git a/de4dot.code/deobfuscators/CRC32.cs b/de4dot.code/deobfuscators/CRC32.cs new file mode 100644 index 00000000..50b94a84 --- /dev/null +++ b/de4dot.code/deobfuscators/CRC32.cs @@ -0,0 +1,100 @@ +/* + Copyright (C) 2011-2012 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 . +*/ + +namespace de4dot.code.deobfuscators { + class CRC32 { + static readonly uint[] table = new uint[256] { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, + }; + + public static uint checksum(byte[] data) { + if (data == null) + return 0; + uint cs = uint.MaxValue; + foreach (var b in data) { + int i = (byte)(cs ^ b); + cs = (cs >> 8) ^ table[i]; + } + return ~cs; + } + } +} From 1dd572f2ef3960e0afb8a0d5a8fe4115f67116e0 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 13 Dec 2012 12:02:52 +0100 Subject: [PATCH 33/71] Add DeobUtils.sha1Sum() --- de4dot.code/deobfuscators/DeobUtils.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/de4dot.code/deobfuscators/DeobUtils.cs b/de4dot.code/deobfuscators/DeobUtils.cs index 77b8f8b5..b9db95be 100644 --- a/de4dot.code/deobfuscators/DeobUtils.cs +++ b/de4dot.code/deobfuscators/DeobUtils.cs @@ -73,6 +73,10 @@ namespace de4dot.code.deobfuscators { return MD5.Create().ComputeHash(data); } + public static byte[] sha1Sum(byte[] data) { + return SHA1.Create().ComputeHash(data); + } + public static byte[] sha256Sum(byte[] data) { return SHA256.Create().ComputeHash(data); } From 7e9e691ef3cafe3ddb22788227154137912e1fc7 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 13 Dec 2012 12:03:25 +0100 Subject: [PATCH 34/71] Support ILProtector 1.0.6.0 - 1.0.6.7 --- .../deobfuscators/ILProtector/Deobfuscator.cs | 32 +- .../ILProtector/MethodsDecrypter.cs | 298 +++++++++++++----- 2 files changed, 231 insertions(+), 99 deletions(-) diff --git a/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs b/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs index 45593e47..db55962a 100644 --- a/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs @@ -86,30 +86,30 @@ namespace de4dot.code.deobfuscators.ILProtector { mainType = new MainType(module); mainType.find(); methodsDecrypter = new MethodsDecrypter(module, mainType); - methodsDecrypter.find(); + if (mainType.Detected) + methodsDecrypter.find(); if (mainType.Detected && methodsDecrypter.Detected && methodsDecrypter.Version != null) - obfuscatorName += " " + getVersion(methodsDecrypter.Version); - } - - static string getVersion(Version version) { - if (version.Revision == 0) - return string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); - return version.ToString(); + obfuscatorName += " " + methodsDecrypter.Version; } public override void deobfuscateBegin() { base.deobfuscateBegin(); - methodsDecrypter.decrypt(); - addTypesToBeRemoved(methodsDecrypter.DelegateTypes, "Obfuscator method delegate type"); - addResourceToBeRemoved(methodsDecrypter.Resource, "Encrypted methods resource"); - addTypeToBeRemoved(mainType.InvokerDelegate, "Invoker delegate type"); - addFieldToBeRemoved(mainType.InvokerInstanceField, "Invoker delegate instance field"); - foreach (var pm in mainType.ProtectMethods) { - addMethodToBeRemoved(pm, "Obfuscator 'Protect' init method"); + if (mainType.Detected) { + if (methodsDecrypter.Detected) { + methodsDecrypter.decrypt(); + addTypesToBeRemoved(methodsDecrypter.DelegateTypes, "Obfuscator method delegate type"); + addResourceToBeRemoved(methodsDecrypter.Resource, "Encrypted methods resource"); + addTypeToBeRemoved(mainType.InvokerDelegate, "Invoker delegate type"); + addFieldToBeRemoved(mainType.InvokerInstanceField, "Invoker delegate instance field"); + foreach (var pm in mainType.ProtectMethods) + addMethodToBeRemoved(pm, "Obfuscator 'Protect' init method"); + mainType.cleanUp(); + } + else + Logger.w("New ILProtector version. Can't decrypt methods (yet)"); } - mainType.cleanUp(); } public override IEnumerable getStringDecrypterMethods() { diff --git a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs index 7fc944c9..aaefcd51 100644 --- a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; +using System.IO; using dot10.IO; using dot10.DotNet; using dot10.DotNet.Emit; @@ -26,20 +27,210 @@ using de4dot.blocks; namespace de4dot.code.deobfuscators.ILProtector { class MethodsDecrypter { - public static readonly byte[] ilpPublicKeyToken = new byte[8] { 0x20, 0x12, 0xD3, 0xC0, 0x55, 0x1F, 0xE0, 0x3D }; - - // This is the first four bytes of ILProtector's public key token - const uint RESOURCE_MAGIC = 0xC0D31220; - ModuleDefMD module; MainType mainType; EmbeddedResource methodsResource; - Version ilpVersion; Dictionary methodInfos = new Dictionary(); List delegateTypes = new List(); - int startOffset; - byte[] decryptionKey; - int decryptionKeyMod; + IDecrypter decrypter; + + interface IDecrypter { + string Version { get; } + byte[] getMethodsData(EmbeddedResource resource); + } + + class DecrypterBase : IDecrypter { + protected static readonly byte[] ilpPublicKeyToken = new byte[8] { 0x20, 0x12, 0xD3, 0xC0, 0x55, 0x1F, 0xE0, 0x3D }; + + protected string ilpVersion; + protected int startOffset; + protected byte[] decryptionKey; + protected int decryptionKeyMod; + + public string Version { + get { return ilpVersion; } + } + + protected void setVersion(Version version) { + if (version.Revision == 0) + ilpVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); + else + ilpVersion = version.ToString(); + } + + public virtual byte[] getMethodsData(EmbeddedResource resource) { + var reader = resource.Data; + reader.Position = startOffset; + if ((reader.ReadInt32() & 1) != 0) + return decompress(reader); + else + return reader.ReadRemainingBytes(); + } + + byte[] decompress(IBinaryReader reader) { + return decompress(reader, decryptionKey, decryptionKeyMod); + } + + static void copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int size) { + for (int i = 0; i < size; i++) + dst[dstIndex++] = src[srcIndex++]; + } + + static byte[] decompress(IBinaryReader reader, byte[] key, int keyMod) { + return decompress(new byte[reader.Read7BitEncodedUInt32()], reader, key, keyMod); + } + + protected static byte[] decompress(byte[] decrypted, IBinaryReader reader, byte[] key, int keyMod) { + int destIndex = 0; + while (reader.Position < reader.Length) { + if (destIndex >= decrypted.Length) + break; + byte flags = reader.ReadByte(); + for (int mask = 1; mask != 0x100; mask <<= 1) { + if (reader.Position >= reader.Length) + break; + if (destIndex >= decrypted.Length) + break; + if ((flags & mask) != 0) { + int displ = (int)reader.Read7BitEncodedUInt32(); + int size = (int)reader.Read7BitEncodedUInt32(); + copy(decrypted, destIndex - displ, decrypted, destIndex, size); + destIndex += size; + } + else { + byte b = reader.ReadByte(); + if (key != null) + b ^= key[destIndex % keyMod]; + decrypted[destIndex++] = b; + } + } + } + + return decrypted; + } + } + + // 1.0.0 - 1.0.4 + class DecrypterV100 : DecrypterBase { + // This is the first four bytes of ILProtector's public key token + const uint RESOURCE_MAGIC = 0xC0D31220; + + DecrypterV100(Version ilpVersion) { + setVersion(ilpVersion); + this.startOffset = 8; + this.decryptionKey = ilpPublicKeyToken; + this.decryptionKeyMod = 8; + } + + public static DecrypterV100 create(IBinaryReader reader) { + reader.Position = 0; + if (reader.Length < 12) + return null; + if (reader.ReadUInt32() != RESOURCE_MAGIC) + return null; + + return new DecrypterV100(new Version(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte())); + } + } + + // 1.0.5 + class DecrypterV105 : DecrypterBase { + DecrypterV105(Version ilpVersion, byte[] key) { + setVersion(ilpVersion); + this.startOffset = 0xA0; + this.decryptionKey = key; + this.decryptionKeyMod = key.Length - 1; + } + + public static DecrypterV105 create(IBinaryReader reader) { + reader.Position = 0; + if (reader.Length < 0xA4) + return null; + var key = reader.ReadBytes(0x94); + if (!Utils.compare(reader.ReadBytes(8), ilpPublicKeyToken)) + return null; + return new DecrypterV105(new Version(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()), key); + } + } + + // 1.0.6 + class DecrypterV106 : DecrypterBase { + byte[] decryptionKey6; + byte[] decryptionKey7; + + DecrypterV106(byte[] key0, byte[] key6, byte[] key7, int startOffset) { + this.ilpVersion = "1.0.6"; + this.startOffset = startOffset; + this.decryptionKey = key0; + this.decryptionKey6 = key6; + this.decryptionKey7 = key7; + this.decryptionKeyMod = key0.Length - 1; + } + + public static DecrypterV106 create(IBinaryReader reader) { + try { + int keyXorOffs2 = (ReadByteAt(reader, 0) ^ ReadByteAt(reader, 2)) + 2; + reader.Position = keyXorOffs2 + (ReadByteAt(reader, 1) ^ ReadByteAt(reader, keyXorOffs2)); + + int sha1DataLen = reader.Read7BitEncodedInt32() + 0x80; + int keyXorOffs1 = (int)reader.Position; + int encryptedOffs = (int)reader.Position + sha1DataLen; + var sha1Data = reader.ReadBytes(sha1DataLen); + uint crc32 = CRC32.checksum(sha1Data); + + reader.Position = reader.Length - 0x18; + uint origCrc32 = reader.ReadUInt32(); + if (crc32 != origCrc32) + return null; + + var key0 = DeobUtils.sha1Sum(sha1Data); // 1.0.6.0 + var key6 = getKey(reader, key0, keyXorOffs1); // 1.0.6.6 + var key7 = getKey(reader, key0, keyXorOffs2); // 1.0.6.7 + return new DecrypterV106(key0, key6, key7, encryptedOffs); + } + catch (IOException) { + return null; + } + } + + static byte[] getKey(IBinaryReader reader, byte[] sha1Sum, int offs) { + var key = (byte[])sha1Sum.Clone(); + reader.Position = offs; + for (int i = 0; i < key.Length; i++) { + byte b = reader.ReadByte(); + key[i] ^= b; + } + return key; + } + + static byte ReadByteAt(IBinaryReader reader, int offs) { + reader.Position = offs; + byte b = reader.ReadByte(); + return b; + } + + public override byte[] getMethodsData(EmbeddedResource resource) { + var reader = resource.Data; + var keys = new byte[][] { decryptionKey, decryptionKey6, decryptionKey7 }; + foreach (var key in keys) { + try { + reader.Position = startOffset; + var decrypted = new byte[reader.Read7BitEncodedUInt32()]; + uint origCrc32 = reader.ReadUInt32(); + decompress(decrypted, reader, key, decryptionKeyMod); + uint crc32 = CRC32.checksum(decrypted); + if (crc32 == origCrc32) + return decrypted; + } + catch (OutOfMemoryException) { + } + catch (IOException) { + } + } + + throw new ApplicationException("Could not decrypt methods data"); + } + } class MethodInfo2 { public int id; @@ -64,8 +255,8 @@ namespace de4dot.code.deobfuscators.ILProtector { get { return delegateTypes; } } - public Version Version { - get { return ilpVersion; } + public string Version { + get { return decrypter == null ? null : decrypter.Version; } } public bool Detected { @@ -85,7 +276,8 @@ namespace de4dot.code.deobfuscators.ILProtector { var reader = resource.Data; reader.Position = 0; if (!checkResourceV100(reader) && - !checkResourceV105(reader)) + !checkResourceV105(reader) && + !checkResourceV106(reader)) continue; methodsResource = resource; @@ -93,91 +285,31 @@ namespace de4dot.code.deobfuscators.ILProtector { } } - // 1.0.0 - 1.0.4 bool checkResourceV100(IBinaryReader reader) { - reader.Position = 0; - if (reader.Length < 12) - return false; - if (reader.ReadUInt32() != RESOURCE_MAGIC) - return false; - ilpVersion = new Version(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); - startOffset = 8; - decryptionKey = ilpPublicKeyToken; - decryptionKeyMod = 8; - return true; + decrypter = DecrypterV100.create(reader); + return decrypter != null; } - // 1.0.5+ bool checkResourceV105(IBinaryReader reader) { - reader.Position = 0; - if (reader.Length < 0xA4) - return false; - var key = reader.ReadBytes(0x94); - if (!Utils.compare(reader.ReadBytes(8), ilpPublicKeyToken)) - return false; - ilpVersion = new Version(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); - startOffset = 0xA0; - decryptionKey = key; - decryptionKeyMod = key.Length - 1; - return true; + decrypter = DecrypterV105.create(reader); + return decrypter != null; + } + + bool checkResourceV106(IBinaryReader reader) { + decrypter = DecrypterV106.create(reader); + return decrypter != null; } public void decrypt() { - if (methodsResource == null) + if (methodsResource == null || decrypter == null) return; - foreach (var info in readMethodInfos(getMethodsData(methodsResource))) + foreach (var info in readMethodInfos(decrypter.getMethodsData(methodsResource))) methodInfos[info.id] = info; restoreMethods(); } - byte[] getMethodsData(EmbeddedResource resource) { - var reader = resource.Data; - reader.Position = 0; - reader.Position = startOffset; - if ((reader.ReadInt32() & 1) != 0) - return decompress(reader); - else - return reader.ReadRemainingBytes(); - } - - byte[] decompress(IBinaryReader reader) { - return decompress(reader, decryptionKey, decryptionKeyMod); - } - - static void copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int size) { - for (int i = 0; i < size; i++) - dst[dstIndex++] = src[srcIndex++]; - } - - static byte[] decompress(IBinaryReader reader, byte[] key, int keyMod) { - var decrypted = new byte[reader.Read7BitEncodedUInt32()]; - - int destIndex = 0; - while (reader.Position < reader.Length) { - byte flags = reader.ReadByte(); - for (int mask = 1; mask != 0x100; mask <<= 1) { - if (reader.Position >= reader.Length) - break; - if ((flags & mask) != 0) { - int displ = (int)reader.Read7BitEncodedUInt32(); - int size = (int)reader.Read7BitEncodedUInt32(); - copy(decrypted, destIndex - displ, decrypted, destIndex, size); - destIndex += size; - } - else { - byte b = reader.ReadByte(); - if (key != null) - b ^= key[destIndex % keyMod]; - decrypted[destIndex++] = b; - } - } - } - - return decrypted; - } - static MethodInfo2[] readMethodInfos(byte[] data) { var reader = MemoryImageStream.Create(data); int numMethods = (int)reader.Read7BitEncodedUInt32(); From bf7c0d58d2fa4c2e25fe498e7962b793d25afa9e Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 13 Dec 2012 14:03:31 +0100 Subject: [PATCH 35/71] Some fixes - Rename offset variables - Alloc buffer outside the loop - Read CRC32 checksum outside the loop - Get rid of a local variable --- .../ILProtector/MethodsDecrypter.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs index aaefcd51..b75d0af5 100644 --- a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs @@ -169,11 +169,11 @@ namespace de4dot.code.deobfuscators.ILProtector { public static DecrypterV106 create(IBinaryReader reader) { try { - int keyXorOffs2 = (ReadByteAt(reader, 0) ^ ReadByteAt(reader, 2)) + 2; - reader.Position = keyXorOffs2 + (ReadByteAt(reader, 1) ^ ReadByteAt(reader, keyXorOffs2)); + int keyXorOffs7 = (ReadByteAt(reader, 0) ^ ReadByteAt(reader, 2)) + 2; + reader.Position = keyXorOffs7 + (ReadByteAt(reader, 1) ^ ReadByteAt(reader, keyXorOffs7)); int sha1DataLen = reader.Read7BitEncodedInt32() + 0x80; - int keyXorOffs1 = (int)reader.Position; + int keyXorOffs6 = (int)reader.Position; int encryptedOffs = (int)reader.Position + sha1DataLen; var sha1Data = reader.ReadBytes(sha1DataLen); uint crc32 = CRC32.checksum(sha1Data); @@ -184,8 +184,8 @@ namespace de4dot.code.deobfuscators.ILProtector { return null; var key0 = DeobUtils.sha1Sum(sha1Data); // 1.0.6.0 - var key6 = getKey(reader, key0, keyXorOffs1); // 1.0.6.6 - var key7 = getKey(reader, key0, keyXorOffs2); // 1.0.6.7 + var key6 = getKey(reader, key0, keyXorOffs6); // 1.0.6.6 + var key7 = getKey(reader, key0, keyXorOffs7); // 1.0.6.7 return new DecrypterV106(key0, key6, key7, encryptedOffs); } catch (IOException) { @@ -196,27 +196,27 @@ namespace de4dot.code.deobfuscators.ILProtector { static byte[] getKey(IBinaryReader reader, byte[] sha1Sum, int offs) { var key = (byte[])sha1Sum.Clone(); reader.Position = offs; - for (int i = 0; i < key.Length; i++) { - byte b = reader.ReadByte(); - key[i] ^= b; - } + for (int i = 0; i < key.Length; i++) + key[i] ^= reader.ReadByte(); return key; } static byte ReadByteAt(IBinaryReader reader, int offs) { reader.Position = offs; - byte b = reader.ReadByte(); - return b; + return reader.ReadByte(); } public override byte[] getMethodsData(EmbeddedResource resource) { var reader = resource.Data; + reader.Position = startOffset; + var decrypted = new byte[reader.Read7BitEncodedUInt32()]; + uint origCrc32 = reader.ReadUInt32(); + long pos = reader.Position; + var keys = new byte[][] { decryptionKey, decryptionKey6, decryptionKey7 }; foreach (var key in keys) { try { - reader.Position = startOffset; - var decrypted = new byte[reader.Read7BitEncodedUInt32()]; - uint origCrc32 = reader.ReadUInt32(); + reader.Position = pos; decompress(decrypted, reader, key, decryptionKeyMod); uint crc32 = CRC32.checksum(decrypted); if (crc32 == origCrc32) From 7bcf5b471050e6e4e8af2c54566390283a5dfc88 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 13 Dec 2012 16:19:34 +0100 Subject: [PATCH 36/71] Make sure lastOffset <= fileData.Length (could be a bad dump) --- de4dot.code/deobfuscators/CodeVeil/MethodsDecrypter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.code/deobfuscators/CodeVeil/MethodsDecrypter.cs b/de4dot.code/deobfuscators/CodeVeil/MethodsDecrypter.cs index 07513cf1..d2c90e8f 100644 --- a/de4dot.code/deobfuscators/CodeVeil/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/CodeVeil/MethodsDecrypter.cs @@ -199,7 +199,7 @@ namespace de4dot.code.deobfuscators.CodeVeil { const int RVA_EXECUTIVE_OFFSET = 1 * 4; const int ENC_CODE_OFFSET = 6 * 4; - int lastOffset = (int)(section.PointerToRawData + section.SizeOfRawData); + int lastOffset = Math.Min(fileData.Length, (int)(section.PointerToRawData + section.SizeOfRawData)); for (int offset = getStartOffset(peImage); offset < lastOffset; ) { offset = findSig(fileData, offset, lastOffset, initializeMethodEnd); if (offset < 0) From d62572a124f547be9e6ba688071bdfa39e16aa46 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 13 Dec 2012 16:30:52 +0100 Subject: [PATCH 37/71] Remove ERROR from error string --- de4dot.cui/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs index e6b843ef..e554a06f 100644 --- a/de4dot.cui/Program.cs +++ b/de4dot.cui/Program.cs @@ -156,7 +156,7 @@ namespace de4dot.cui { Logger.Instance.Log(false, null, loggerEvent, "\n\n"); Logger.Instance.Log(false, null, loggerEvent, line); Logger.Instance.Log(false, null, loggerEvent, "Stack trace:\n{0}", ex.StackTrace); - Logger.Instance.Log(false, null, loggerEvent, "\n\nERROR: Caught an exception:\n"); + Logger.Instance.Log(false, null, loggerEvent, "\n\nCaught an exception:\n"); Logger.Instance.Log(false, null, loggerEvent, line); Logger.Instance.Log(false, null, loggerEvent, "Message:"); Logger.Instance.Log(false, null, loggerEvent, " {0}", ex.Message); From 574eb0ee3d8d1741775afe0cb5b7b2cdf200d422 Mon Sep 17 00:00:00 2001 From: de4dot Date: Thu, 13 Dec 2012 16:32:38 +0100 Subject: [PATCH 38/71] Remove comment --- Test.Rename/Program.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Test.Rename/Program.cs b/Test.Rename/Program.cs index 2f4c9b75..c349fe22 100644 --- a/Test.Rename/Program.cs +++ b/Test.Rename/Program.cs @@ -17,20 +17,6 @@ along with de4dot. If not, see . */ - -/* -How to test it: -- Compile this assembly and the other test assembly -- Force rename of everything (set name regex to eg. .*) -- Run peverify /IL /MD on both files -- Decompile them and create a solution for both projects. I recommend using ILSpy. -- Compile with VS. If it fails to build, make sure the decompiler isn't buggy. -- Some manual inspection may be required as well even if it builds 100%. - -Yes, it's not 100% automated. You get what you pay for! Buy something -nice with the money you saved. -*/ - namespace Test.Rename { namespace test1 { public class Class1 : Test.Rename.Dll.test.pub1.Class1 { From bbbdf0b0ffbf9272a60bd00184b8f417a9075448 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 09:18:14 +0100 Subject: [PATCH 39/71] Update array cflow deobfuscator --- .../DeepSea/ArrayBlockDeobfuscator.cs | 52 ++++++++++++++-- .../deobfuscators/DeepSea/ArrayBlockState.cs | 62 ++++++++++++++++--- 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs b/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs index c757bd9e..2d6a6fca 100644 --- a/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs +++ b/de4dot.code/deobfuscators/DeepSea/ArrayBlockDeobfuscator.cs @@ -91,6 +91,48 @@ namespace de4dot.code.deobfuscators.DeepSea { return changed; } + static bool IsLdelem(ArrayBlockState.FieldInfo info, Code code) { + switch (info.elementType) { + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: + return code == Code.Ldelem_I1 || code == Code.Ldelem_U1; + + case ElementType.Char: + case ElementType.I2: + case ElementType.U2: + return code == Code.Ldelem_I2 || code == Code.Ldelem_U2; + + case ElementType.I4: + case ElementType.U4: + return code == Code.Ldelem_I4 || code == Code.Ldelem_U4; + + default: + return false; + } + } + + static bool IsStelem(ArrayBlockState.FieldInfo info, Code code) { + switch (info.elementType) { + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: + return code == Code.Stelem_I1; + + case ElementType.Char: + case ElementType.I2: + case ElementType.U2: + return code == Code.Stelem_I2; + + case ElementType.I4: + case ElementType.U4: + return code == Code.Stelem_I4; + + default: + return false; + } + } + bool deobfuscate1(Block block, int i) { var instrs = block.Instructions; if (i >= instrs.Count - 2) @@ -111,11 +153,11 @@ namespace de4dot.code.deobfuscators.DeepSea { return false; var ldelem = instrs[i + 2]; - if (ldelem.OpCode.Code != Code.Ldelem_U1) + if (!IsLdelem(info, ldelem.OpCode.Code)) return false; block.remove(i, 3 - 1); - instrs[i] = new Instr(Instruction.CreateLdcI4(info.array[ldci4.getLdcI4Value()])); + instrs[i] = new Instr(Instruction.CreateLdcI4((int)info.readArrayElement(ldci4.getLdcI4Value()))); return true; } @@ -136,11 +178,11 @@ namespace de4dot.code.deobfuscators.DeepSea { return false; var ldelem = instrs[i + 2]; - if (ldelem.OpCode.Code != Code.Ldelem_U1) + if (!IsLdelem(info, ldelem.OpCode.Code)) return false; block.remove(i, 3 - 1); - instrs[i] = new Instr(Instruction.CreateLdcI4(info.array[ldci4.getLdcI4Value()])); + instrs[i] = new Instr(Instruction.CreateLdcI4((int)info.readArrayElement(ldci4.getLdcI4Value()))); return true; } @@ -169,7 +211,7 @@ namespace de4dot.code.deobfuscators.DeepSea { if (i >= instrs.Count) return false; var stelem = instrs[i]; - if (stelem.OpCode.Code != Code.Stelem_I1) + if (!IsStelem(info, stelem.OpCode.Code)) return false; block.remove(start, i - start + 1); diff --git a/de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs b/de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs index fb665c2a..7ebef092 100644 --- a/de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs +++ b/de4dot.code/deobfuscators/DeepSea/ArrayBlockState.cs @@ -17,6 +17,7 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; using dot10.DotNet; using dot10.DotNet.Emit; @@ -28,14 +29,62 @@ namespace de4dot.code.deobfuscators.DeepSea { FieldDefAndDeclaringTypeDict fieldToInfo = new FieldDefAndDeclaringTypeDict(); public class FieldInfo { + public readonly ElementType elementType; public readonly FieldDef field; public readonly FieldDef arrayInitField; - public readonly byte[] array; + public readonly Array array; public FieldInfo(FieldDef field, FieldDef arrayInitField) { this.field = field; + this.elementType = ((SZArraySig)field.FieldType).Next.GetElementType(); this.arrayInitField = arrayInitField; - this.array = (byte[])arrayInitField.InitialValue.Clone(); + this.array = createArray(elementType, arrayInitField.InitialValue); + } + + static Array createArray(ElementType etype, byte[] data) { + switch (etype) { + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: + return (byte[])data.Clone(); + + case ElementType.Char: + case ElementType.I2: + case ElementType.U2: + var ary2 = new ushort[data.Length / 2]; + Buffer.BlockCopy(data, 0, ary2, 0, ary2.Length * 2); + return ary2; + + case ElementType.I4: + case ElementType.U4: + var ary4 = new uint[data.Length / 4]; + Buffer.BlockCopy(data, 0, ary4, 0, ary4.Length * 4); + return ary4; + + default: + throw new ApplicationException("Invalid etype"); + } + } + + public uint readArrayElement(int index) { + switch (elementType) { + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: + return ((byte[])array)[index]; + + case ElementType.Char: + case ElementType.I2: + case ElementType.U2: + return ((ushort[])array)[index]; + + case ElementType.I4: + case ElementType.U4: + return ((uint[])array)[index]; + + default: + throw new ApplicationException("Invalid etype"); + } } } @@ -71,10 +120,6 @@ namespace de4dot.code.deobfuscators.DeepSea { if (instrs == null) continue; - var arrayType = instrs[0].Operand as ITypeDefOrRef; - if (arrayType == null || (arrayType.FullName != "System.Byte" && !arrayType.DefinitionAssembly.IsCorLib())) - continue; - var arrayInitField = instrs[2].Operand as FieldDef; if (arrayInitField == null || arrayInitField.InitialValue == null || arrayInitField.InitialValue.Length == 0) continue; @@ -84,7 +129,10 @@ namespace de4dot.code.deobfuscators.DeepSea { continue; var targetField = instrs[4].Operand as FieldDef; - if (targetField == null) + if (targetField == null || targetField.FieldType.GetElementType() != ElementType.SZArray) + continue; + var etype = ((SZArraySig)targetField.FieldType).Next.GetElementType(); + if (etype < ElementType.Boolean || etype > ElementType.U4) continue; if (fieldToInfo.find(targetField) == null) { From bbb715c93c4bdb1ad303db371131871caaa5b4bd Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 09:22:36 +0100 Subject: [PATCH 40/71] Update string decrypter --- .../deobfuscators/DeepSea/StringDecrypter.cs | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs b/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs index f27d0990..18b78b3e 100644 --- a/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/DeepSea/StringDecrypter.cs @@ -158,7 +158,14 @@ namespace de4dot.code.deobfuscators.DeepSea { ushort[] encryptedData; short[] key; int keyShift; - bool isTrial; + DecryptType decryptType; + + // This'll do for now. Code should be added to detect the constants in the code. + enum DecryptType { + Type1, + Type2, + Type3, + } class ArrayInfo { public int sizeInElems; @@ -238,7 +245,7 @@ namespace de4dot.code.deobfuscators.DeepSea { encryptedData = new ushort[arrayInfo.initField.InitialValue.Length / 2]; Buffer.BlockCopy(arrayInfo.initField.InitialValue, 0, encryptedData, 0, arrayInfo.initField.InitialValue.Length); - isTrial = !DeobUtils.hasInteger(Method, 0xFFF0); + decryptType = getDecryptType(Method); keyShift = findKeyShift(cctor); key = findKey(); if (key == null || key.Length == 0) @@ -247,6 +254,14 @@ namespace de4dot.code.deobfuscators.DeepSea { return true; } + static DecryptType getDecryptType(MethodDef method) { + if (DeobUtils.hasInteger(method, 0xFFF0)) + return DecryptType.Type2; + if (DeobUtils.hasInteger(method, 0xFFC0)) + return DecryptType.Type3; + return DecryptType.Type1; // trial + } + int findKeyShift(MethodDef method) { var instrs = method.Body.Instructions; for (int i = 0; i < instrs.Count - 3; i++) { @@ -342,9 +357,19 @@ namespace de4dot.code.deobfuscators.DeepSea { } public string decrypt(object[] args) { - if (isTrial) + switch (decryptType) { + case DecryptType.Type1: return decryptTrial((int)args[arg1], (int)args[arg2]); - return decryptRetail((int)args[arg1], (int)args[arg2]); + + case DecryptType.Type2: + return decryptRetail2((int)args[arg1], (int)args[arg2]); + + case DecryptType.Type3: + return decryptRetail3((int)args[arg1], (int)args[arg2]); + + default: + throw new ApplicationException("Unknown type"); + } } string decryptTrial(int magic2, int magic3) { @@ -359,13 +384,21 @@ namespace de4dot.code.deobfuscators.DeepSea { return sb.ToString(); } - string decryptRetail(int magic2, int magic3) { + string decryptRetail2(int magic2, int magic3) { + return decryptRetail(magic2, magic3, 2, 1, 0, 8, 0); + } + + string decryptRetail3(int magic2, int magic3) { + return decryptRetail(magic2, magic3, 0, 2, 1, 0x20, 17); + } + + string decryptRetail(int magic2, int magic3, int keyCharOffs, int cachedIndexOffs, int flagsOffset, int flag, int keyDispl) { int offset = magic ^ magic2 ^ magic3; - var keyChar = encryptedData[offset + 2]; - int cachedIndex = encryptedData[offset + 1] ^ keyChar; - int flags = encryptedData[offset] ^ keyChar; - int numChars = (flags >> 1) & ~7 | (flags & 7); - if ((flags & 8) != 0) { + var keyChar = encryptedData[offset + keyCharOffs]; + int cachedIndex = encryptedData[offset + cachedIndexOffs] ^ keyChar; + int flags = encryptedData[offset + flagsOffset] ^ keyChar; + int numChars = ((flags >> 1) & ~(flag - 1)) | (flags & (flag - 1)); + if ((flags & flag) != 0) { numChars <<= 15; numChars |= encryptedData[offset + 3] ^ keyChar; offset++; @@ -373,7 +406,7 @@ namespace de4dot.code.deobfuscators.DeepSea { offset += 3; var sb = new StringBuilder(numChars); for (int i = 0; i < numChars; i++) - sb.Append((char)(keyChar ^ encryptedData[offset + numChars - i - 1] ^ key[(i + 1 + offset) % key.Length])); + sb.Append((char)(keyChar ^ encryptedData[offset + numChars - i - 1] ^ key[(i + 1 + keyDispl + offset) % key.Length])); return sb.ToString(); } From 61201b8f7e637761bcc5b4addf996a33908f92b3 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 12:29:38 +0100 Subject: [PATCH 41/71] Bug fix. Use GPCs from the original GP --- blocks/DotNetUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/DotNetUtils.cs b/blocks/DotNetUtils.cs index bb262811..13cc07ae 100644 --- a/blocks/DotNetUtils.cs +++ b/blocks/DotNetUtils.cs @@ -364,7 +364,7 @@ namespace de4dot.blocks { newMethod.ParamList.Add(new ParamDefUser(pd.Name, pd.Sequence, pd.Attributes)); foreach (var gp in method.GenericParameters) { var newGp = new GenericParamUser(gp.Number, gp.Flags, gp.Name); - foreach (var gpc in newGp.GenericParamConstraints) + foreach (var gpc in gp.GenericParamConstraints) newGp.GenericParamConstraints.Add(new GenericParamConstraintUser(gpc.Constraint)); newMethod.GenericParameters.Add(newGp); } From e2016f6b18d8b39c4ba05a4f81a76bba696ff767 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 12:29:52 +0100 Subject: [PATCH 42/71] Add clearStack() and pop(int) --- blocks/cflow/InstructionEmulator.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index b0be6768..b49200ea 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -232,6 +232,17 @@ namespace de4dot.blocks.cflow { valueStack.push(value); } + public void clearStack() { + valueStack.clear(); + } + + public void pop(int num) { + if (num < 0) + valueStack.clear(); + else + valueStack.pop(num); + } + public Value pop() { return valueStack.pop(); } From 88d1a8ab89c44fdc39f5657504e733703e08ab0e Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 12:39:06 +0100 Subject: [PATCH 43/71] Inline generic methods that DS added --- blocks/cflow/MethodCallInlinerBase.cs | 20 ++++++++++++- .../DeepSea/DsMethodCallInliner.cs | 30 +++++++++++++++---- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/blocks/cflow/MethodCallInlinerBase.cs b/blocks/cflow/MethodCallInlinerBase.cs index f81baba2..5f4537a2 100644 --- a/blocks/cflow/MethodCallInlinerBase.cs +++ b/blocks/cflow/MethodCallInlinerBase.cs @@ -104,6 +104,10 @@ namespace de4dot.blocks.cflow { return tryInlineOtherMethod(patchIndex, methodToInline, instr, instrIndex, 0); } + protected virtual Instruction onAfterLoadArg(MethodDef methodToInline, Instruction instr, ref int instrIndex) { + return instr; + } + protected InstructionPatcher tryInlineOtherMethod(int patchIndex, MethodDef methodToInline, Instruction instr, int instrIndex, int popLastArgs) { int loadIndex = 0; int methodArgsCount = DotNetUtils.getArgsCount(methodToInline); @@ -133,6 +137,7 @@ namespace de4dot.blocks.cflow { return null; loadIndex++; instr = DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex); + instr = onAfterLoadArg(methodToInline, instr, ref instrIndex); } if (instr == null || loadIndex != methodArgsCount - popLastArgs) return null; @@ -224,7 +229,7 @@ namespace de4dot.blocks.cflow { return false; for (int i = 0; i < methodArgs.Count; i++) { var methodArg = methodArgs[i]; - var methodToInlineArg = methodToInlineArgs[i].Type; + var methodToInlineArg = getArgType(methodToInline, methodToInlineArgs[i].Type); if (!isCompatibleType(i, methodArg, methodToInlineArg)) { if (i != 0 || !hasImplicitThis) return false; @@ -236,6 +241,19 @@ namespace de4dot.blocks.cflow { return true; } + static TypeSig getArgType(MethodDef method, TypeSig arg) { + if (arg.GetElementType() != ElementType.MVar) + return arg; + var mvar = (GenericMVar)arg; + foreach (var gp in method.GenericParameters) { + if (gp.Number != mvar.Number) + continue; + foreach (var gpc in gp.GenericParamConstraints) + return gpc.Constraint.ToTypeSig(); + } + return arg; + } + protected virtual bool isCompatibleType(int paramIndex, IType origType, IType newType) { return new SigComparer().Equals(origType, newType); } diff --git a/de4dot.code/deobfuscators/DeepSea/DsMethodCallInliner.cs b/de4dot.code/deobfuscators/DeepSea/DsMethodCallInliner.cs index 1fab08f5..3e9ab411 100644 --- a/de4dot.code/deobfuscators/DeepSea/DsMethodCallInliner.cs +++ b/de4dot.code/deobfuscators/DeepSea/DsMethodCallInliner.cs @@ -51,8 +51,13 @@ namespace de4dot.code.deobfuscators.DeepSea { bool inlineMethod(Instruction callInstr, int instrIndex) { var method = callInstr.Operand as MethodDef; - if (method == null) - return false; + if (method == null) { + var ms = callInstr.Operand as MethodSpec; + if (ms != null) + method = ms.Method as MethodDef; + if (method == null) + return false; + } if (!canInline(method)) return false; @@ -69,6 +74,14 @@ namespace de4dot.code.deobfuscators.DeepSea { return true; } + protected override Instruction onAfterLoadArg(MethodDef methodToInline, Instruction instr, ref int instrIndex) { + if (instr.OpCode.Code != Code.Box) + return instr; + if (methodToInline.MethodSig.GetGenParamCount() == 0) + return instr; + return DotNetUtils.getInstruction(methodToInline.Body.Instructions, ref instrIndex); + } + bool inlineMethod(MethodDef methodToInline, int instrIndex, int const1, int const2) { this.methodToInline = methodToInline = cflowDeobfuscator.deobfuscate(methodToInline); @@ -271,8 +284,7 @@ done: bool emulateToReturn(int index, Instruction lastInstr) { int pushes, pops; lastInstr.CalculateStackUsage(false, out pushes, out pops); - for (int i = 0; i < pops; i++) - instructionEmulator.pop(); + instructionEmulator.pop(pops); returnValue = null; if (pushes != 0) { @@ -300,8 +312,6 @@ done: return false; if (method.Attributes != (MethodAttributes.Assembly | MethodAttributes.Static)) return false; - if (method.GenericParameters.Count > 0) - return false; if (method.Body.ExceptionHandlers.Count > 0) return false; @@ -309,6 +319,14 @@ done: int paramCount = parameters.Count; if (paramCount < 2) return false; + + if (method.GenericParameters.Count > 0) { + foreach (var gp in method.GenericParameters) { + if (gp.GenericParamConstraints.Count == 0) + return false; + } + } + var param1 = parameters[paramCount - 1]; var param2 = parameters[paramCount - 2]; if (!isIntType(param1.ElementType)) From 63f1ec4f93d9438d109f807b9175d72f5b342c44 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 12:40:44 +0100 Subject: [PATCH 44/71] Update DS string decrypter --- .../deobfuscators/DeepSea/ResourceResolver.cs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs b/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs index 2b419051..584aecba 100644 --- a/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs +++ b/de4dot.code/deobfuscators/DeepSea/ResourceResolver.cs @@ -140,7 +140,8 @@ namespace de4dot.code.deobfuscators.DeepSea { data41.resourceField = getLdtokenField(info.handler); if (data41.resourceField == null) return false; - int magicArgIndex = getMagicArgIndex41Retail(info.handler); + bool isOtherRetail; + int magicArgIndex = getMagicArgIndex41Retail(info.handler, out isOtherRetail); if (magicArgIndex < 0) { magicArgIndex = getMagicArgIndex41Trial(info.handler); data41.isTrial = true; @@ -153,29 +154,40 @@ namespace de4dot.code.deobfuscators.DeepSea { return false; if (data41.isTrial) data41.magic = (int)val >> 3; + else if (isOtherRetail) + data41.magic = data41.resourceField.InitialValue.Length - (int)val; else data41.magic = ((asmVer.Major << 3) | (asmVer.Minor << 2) | asmVer.Revision) - (int)val; return true; } - static int getMagicArgIndex41Retail(MethodDef method) { + static int getMagicArgIndex41Retail(MethodDef method, out bool isOtherRetail) { + isOtherRetail = false; var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count - 3; i++) { - var add = instrs[i]; + for (int i = 0; i < instrs.Count - 4; i++) { + isOtherRetail = false; + var ld = instrs[i]; + if (ld.IsLdarg()) + isOtherRetail = true; + else if (!ld.IsLdloc()) + continue; + + var add = instrs[i + 1]; if (add.OpCode.Code != Code.Add) continue; - var ldarg = instrs[i + 1]; + var ldarg = instrs[i + 2]; if (!ldarg.IsLdarg()) continue; - var sub = instrs[i + 2]; + var sub = instrs[i + 3]; if (sub.OpCode.Code != Code.Sub) continue; - var ldci4 = instrs[i + 3]; + var ldci4 = instrs[i + 4]; if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 0xFF) continue; return ldarg.GetParameterIndex(); } + return -1; } From e8a9c0675af41fd7fc763cc9db8c259b56365c52 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 16:50:06 +0100 Subject: [PATCH 45/71] Add preserveTokensAndTypes() --- de4dot.code/deobfuscators/DeobfuscatorBase.cs | 18 +++++++++++++++--- .../deobfuscators/Unknown/Deobfuscator.cs | 5 +---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/de4dot.code/deobfuscators/DeobfuscatorBase.cs b/de4dot.code/deobfuscators/DeobfuscatorBase.cs index f52ce371..c53cd3f2 100644 --- a/de4dot.code/deobfuscators/DeobfuscatorBase.cs +++ b/de4dot.code/deobfuscators/DeobfuscatorBase.cs @@ -51,6 +51,8 @@ namespace de4dot.code.deobfuscators { MethodCallRemover methodCallRemover = new MethodCallRemover(); byte[] moduleBytes; protected InitializedDataCreator initializedDataCreator; + bool keepTypes; + MetaDataFlags? mdFlags; protected byte[] ModuleBytes { get { return moduleBytes; } @@ -78,7 +80,7 @@ namespace de4dot.code.deobfuscators { public DecrypterType DefaultDecrypterType { get; set; } public virtual MetaDataFlags MetaDataFlags { - get { return Operations.MetaDataFlags; } + get { return mdFlags ?? Operations.MetaDataFlags; } } public abstract string Type { get; } @@ -89,8 +91,9 @@ namespace de4dot.code.deobfuscators { get { return false; } } - protected virtual bool KeepTypes { - get { return false; } + protected bool KeepTypes { + get { return keepTypes; } + set { keepTypes = value; } } protected bool CanRemoveTypes { @@ -129,6 +132,15 @@ namespace de4dot.code.deobfuscators { initializedDataCreator = new InitializedDataCreator(module); } + protected void preserveTokensAndTypes() { + keepTypes = true; + mdFlags = Operations.MetaDataFlags; + mdFlags |= MetaDataFlags.PreserveRids | + MetaDataFlags.PreserveUSOffsets | + MetaDataFlags.PreserveBlobOffsets | + MetaDataFlags.PreserveExtraSignatureData; + } + protected virtual bool checkValidName(string name) { return optionsBase.ValidNameRegex.isMatch(name); } diff --git a/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs b/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs index 8cf49820..10d31cdd 100644 --- a/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs @@ -63,12 +63,9 @@ namespace de4dot.code.deobfuscators.Unknown { get { return obfuscatorName ?? "Unknown Obfuscator"; } } - protected override bool KeepTypes { - get { return true; } - } - internal Deobfuscator(Options options) : base(options) { + KeepTypes = true; } void setName(string name) { From 6ce3b44de637e8a15648ccdc54a6dbbda69e2016 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 16:51:21 +0100 Subject: [PATCH 46/71] Preserve tokens if VM code couldn't be restored --- de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs | 8 ++++++-- de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs index a3f011fd..011af735 100644 --- a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs @@ -271,8 +271,12 @@ namespace de4dot.code.deobfuscators.Agile_NET { } if (options.RestoreVmCode) { - csvm.restore(); - addResourceToBeRemoved(csvm.Resource, "CSVM data resource"); + if (csvm.restore()) + addResourceToBeRemoved(csvm.Resource, "CSVM data resource"); + else { + Logger.e("Couldn't restore VM methods"); + preserveTokensAndTypes(); + } } } diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs b/de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs index 8699f08f..238a5fd9 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs @@ -78,13 +78,17 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return DotNetUtils.getResource(module, "_CSVM") as EmbeddedResource; } - public void restore() { + public bool restore() { if (!Detected) - return; + return true; int oldIndent = Logger.Instance.IndentLevel; try { restore2(); + return true; + } + catch { + return false; } finally { Logger.Instance.IndentLevel = oldIndent; From ef668d1dcc5630e2e72a402458a56afb5c40eddf Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 19:50:15 +0100 Subject: [PATCH 47/71] Update links from github to bitbucket --- LICENSE.de4dot.txt | 2 +- de4dot.cui/Program.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE.de4dot.txt b/LICENSE.de4dot.txt index 686b098f..83f3d846 100644 --- a/LICENSE.de4dot.txt +++ b/LICENSE.de4dot.txt @@ -17,6 +17,6 @@ along with de4dot. If not, see . -Official site: https://github.com/0xd4d/de4dot +Official site: https://bitbucket.org/0xd4d/de4dot See the file COPYING for more details. diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs index e554a06f..7aa6f305 100644 --- a/de4dot.cui/Program.cs +++ b/de4dot.cui/Program.cs @@ -73,7 +73,7 @@ namespace de4dot.cui { Logger.n(""); Logger.n("de4dot v{0} Copyright (C) 2011-2012 de4dot@gmail.com", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); - Logger.n("Latest version and source code: https://github.com/0xd4d/de4dot"); + Logger.n("Latest version and source code: https://bitbucket.org/0xd4d/de4dot"); Logger.n(""); var options = new FilesDeobfuscator.Options(); @@ -99,7 +99,7 @@ namespace de4dot.cui { Logger.Instance.LogErrorDontIgnore("If it's a supported obfuscator, it could be a bug or a new obfuscator version."); Logger.Instance.LogErrorDontIgnore("If it's an unsupported obfuscator, make sure the methods are decrypted!"); } - Logger.Instance.LogErrorDontIgnore("Send bug reports to de4dot@gmail.com or go to https://github.com/0xd4d/de4dot/issues"); + Logger.Instance.LogErrorDontIgnore("Send bug reports to de4dot@gmail.com or go to https://bitbucket.org/0xd4d/de4dot/issues"); Logger.Instance.LogErrorDontIgnore("I will need the original files, so email me a link to the installer or a zip/rar file."); exitCode = 1; } From d0002f098c35d05c00266f0f672fbecc577c9021 Mon Sep 17 00:00:00 2001 From: de4dot Date: Fri, 14 Dec 2012 19:53:48 +0100 Subject: [PATCH 48/71] Copy license files to a new dir --- de4dot.code/de4dot.code.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 45dfd2bc..23b9cd7e 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -347,8 +347,9 @@ - copy "$(SolutionDir)LICENSE*.txt" "..\$(OutDir).." -copy "$(SolutionDir)COPYING" "..\$(OutDir).." + mkdir "..\$(OutDir)..\LICENSES" +copy "$(SolutionDir)LICENSE*.txt" "..\$(OutDir)..\LICENSES" +copy "$(SolutionDir)COPYING" "..\$(OutDir)..\LICENSES"