diff --git a/de4dot.code/IObfuscatedFile.cs b/de4dot.code/IObfuscatedFile.cs index 39e728b4..f5720a94 100644 --- a/de4dot.code/IObfuscatedFile.cs +++ b/de4dot.code/IObfuscatedFile.cs @@ -30,6 +30,7 @@ namespace de4dot { Func IsValidName { get; } bool RenameResourcesInCode { get; } bool RenameSymbols { get; } + bool RemoveNamespaceWithOneType { get; } void deobfuscateBegin(); void deobfuscate(); diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 967eec82..1fb81bdd 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -118,6 +118,10 @@ namespace de4dot { get { return options.RenameSymbols; } } + public bool RemoveNamespaceWithOneType { + get { return (deob.RenamingOptions & RenamingOptions.RemoveNamespaceIfOneType) != 0; } + } + public IDeobfuscator Deobfuscator { get { return deob; } } diff --git a/de4dot.code/deobfuscators/DeobfuscatorBase.cs b/de4dot.code/deobfuscators/DeobfuscatorBase.cs index 26fd8c34..4b44e47e 100644 --- a/de4dot.code/deobfuscators/DeobfuscatorBase.cs +++ b/de4dot.code/deobfuscators/DeobfuscatorBase.cs @@ -66,6 +66,7 @@ namespace de4dot.deobfuscators { public IOperations Operations { get; set; } public IDeobfuscatedFile DeobfuscatedFile { get; set; } public virtual StringFeatures StringFeatures { get; set; } + public virtual RenamingOptions RenamingOptions { get; set; } public DecrypterType DefaultDecrypterType { get; set; } public abstract string Type { get; } diff --git a/de4dot.code/deobfuscators/IDeobfuscator.cs b/de4dot.code/deobfuscators/IDeobfuscator.cs index 9ae356f6..4c475e1a 100644 --- a/de4dot.code/deobfuscators/IDeobfuscator.cs +++ b/de4dot.code/deobfuscators/IDeobfuscator.cs @@ -44,6 +44,11 @@ namespace de4dot.deobfuscators { AllowAll = AllowNoDecryption | AllowStaticDecryption | AllowDynamicDecryption, } + [Flags] + enum RenamingOptions { + RemoveNamespaceIfOneType = 1, + } + interface IDeobfuscator { string Type { get; } string Name { get; } @@ -51,6 +56,7 @@ namespace de4dot.deobfuscators { IDeobfuscatorOptions TheOptions { get; } IOperations Operations { get; set; } StringFeatures StringFeatures { get; } + RenamingOptions RenamingOptions { get; } DecrypterType DefaultDecrypterType { get; } // This is non-null only in detect() and deobfuscateBegin(). diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs index 0c599240..52a2a904 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs @@ -17,6 +17,7 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; @@ -34,6 +35,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { BoolOption removeInlinedMethods; BoolOption dumpEmbeddedAssemblies; BoolOption decryptResources; + BoolOption removeNamespaces; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { @@ -44,6 +46,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { removeInlinedMethods = new BoolOption(null, makeArgName("remove-inlined"), "Remove inlined methods", true); dumpEmbeddedAssemblies = new BoolOption(null, makeArgName("embedded"), "Dump embedded assemblies", true); decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true); + removeNamespaces = new BoolOption(null, makeArgName("ns1"), "Clear namespace if there's only one class in it", true); } public override string Name { @@ -64,6 +67,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { RemoveInlinedMethods = removeInlinedMethods.get(), DumpEmbeddedAssemblies = dumpEmbeddedAssemblies.get(), DecryptResources = decryptResources.get(), + RemoveNamespaces = removeNamespaces.get(), }); } @@ -76,6 +80,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { removeInlinedMethods, dumpEmbeddedAssemblies, decryptResources, + removeNamespaces, }; } } @@ -105,6 +110,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { public bool RemoveInlinedMethods { get; set; } public bool DumpEmbeddedAssemblies { get; set; } public bool DecryptResources { get; set; } + public bool RemoveNamespaces { get; set; } } public override string Type { @@ -122,6 +128,11 @@ namespace de4dot.deobfuscators.dotNET_Reactor { public Deobfuscator(Options options) : base(options) { this.options = options; + + if (options.RemoveNamespaces) + this.RenamingOptions |= RenamingOptions.RemoveNamespaceIfOneType; + else + this.RenamingOptions &= ~RenamingOptions.RemoveNamespaceIfOneType; } public override void init(ModuleDefinition module) { diff --git a/de4dot.code/renamer/DefinitionsRenamer.cs b/de4dot.code/renamer/DefinitionsRenamer.cs index c52d3b13..0c2a61e7 100644 --- a/de4dot.code/renamer/DefinitionsRenamer.cs +++ b/de4dot.code/renamer/DefinitionsRenamer.cs @@ -210,6 +210,10 @@ namespace de4dot.renamer { void renameTypeDefinitions() { Log.v("Renaming obfuscated type definitions"); + + foreach (var module in modules) + module.onBeforeRenamingTypeDefinitions(); + typeNameState = new TypeNameState(); foreach (var typeDef in allTypes) typeNameState.currentNames.add(typeDef.OldName); diff --git a/de4dot.code/renamer/MemberRefs.cs b/de4dot.code/renamer/MemberRefs.cs index 8aa004fe..136241e0 100644 --- a/de4dot.code/renamer/MemberRefs.cs +++ b/de4dot.code/renamer/MemberRefs.cs @@ -421,6 +421,11 @@ namespace de4dot.renamer { bool IsDelegate { get; set; } + public string NewNamespace { + get { return newNamespace; } + set { newNamespace = value; } + } + public TypeDef(TypeDefinition typeDefinition) : this(typeDefinition, null) { } @@ -607,7 +612,7 @@ namespace de4dot.renamer { rename(nameCreator.newName(typeDefinition, newBaseType)); } - if (typeDefinition.Namespace != "" && !typeNameState.isValidNamespace(typeDefinition.Namespace)) + if (newNamespace == null && typeDefinition.Namespace != "" && !typeNameState.isValidNamespace(typeDefinition.Namespace)) newNamespace = typeNameState.newNamespace(typeDefinition.Namespace); prepareRenameGenericParams(genericParams, typeNameState.IsValidName); diff --git a/de4dot.code/renamer/Module.cs b/de4dot.code/renamer/Module.cs index 145972be..858a32d1 100644 --- a/de4dot.code/renamer/Module.cs +++ b/de4dot.code/renamer/Module.cs @@ -77,6 +77,37 @@ namespace de4dot.renamer { } } + public void onBeforeRenamingTypeDefinitions() { + if (obfuscatedFile.RemoveNamespaceWithOneType) + removeOneClassNamespaces(); + } + + void removeOneClassNamespaces() { + var nsToTypes = new Dictionary>(StringComparer.Ordinal); + + foreach (var typeDef in allTypes.getAll()) { + List list; + var ns = typeDef.TypeDefinition.Namespace; + if (string.IsNullOrEmpty(ns)) + continue; + if (IsValidName(ns)) + continue; + if (!nsToTypes.TryGetValue(ns, out list)) + nsToTypes[ns] = list = new List(); + list.Add(typeDef); + } + + foreach (var list in nsToTypes.Values) { + const int maxClasses = 1; + if (list.Count != maxClasses) + continue; + var ns = list[0].TypeDefinition.Namespace; + Log.v("Removing namespace: {0}", ns); + foreach (var type in list) + type.NewNamespace = ""; + } + } + static string renameResourceString(string s, string oldTypeName, string newTypeName) { if (!Utils.StartsWith(s, oldTypeName, StringComparison.Ordinal)) return s;