diff --git a/de4dot.code/IObfuscatedFile.cs b/de4dot.code/IObfuscatedFile.cs index f5720a94..2f27bcc2 100644 --- a/de4dot.code/IObfuscatedFile.cs +++ b/de4dot.code/IObfuscatedFile.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using de4dot.deobfuscators; using Mono.Cecil; +using de4dot.renamer; namespace de4dot { interface IObfuscatedFile { @@ -27,7 +28,7 @@ namespace de4dot { IDeobfuscator Deobfuscator { get; } string Filename { get; } string NewFilename { get; } - Func IsValidName { get; } + INameChecker NameChecker { get; } bool RenameResourcesInCode { get; } bool RenameSymbols { get; } bool RemoveNamespaceWithOneType { get; } diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 590a2be3..9706e44a 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -29,6 +29,7 @@ using de4dot.deobfuscators; using de4dot.blocks; using de4dot.blocks.cflow; using de4dot.AssemblyClient; +using de4dot.renamer; namespace de4dot { class ObfuscatedFile : IObfuscatedFile, IDeobfuscatedFile { @@ -106,8 +107,8 @@ namespace de4dot { get { return module; } } - public Func IsValidName { - get { return deob.IsValidName; } + public INameChecker NameChecker { + get { return deob; } } public bool RenameResourcesInCode { diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 4a828d1f..e7fc1379 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -156,6 +156,7 @@ + @@ -166,7 +167,12 @@ + + + + + diff --git a/de4dot.code/deobfuscators/DeobfuscatorBase.cs b/de4dot.code/deobfuscators/DeobfuscatorBase.cs index 2723b450..b17e5ecb 100644 --- a/de4dot.code/deobfuscators/DeobfuscatorBase.cs +++ b/de4dot.code/deobfuscators/DeobfuscatorBase.cs @@ -77,10 +77,6 @@ namespace de4dot.deobfuscators { get { return false; } } - public Func IsValidName { - get { return (name) => checkValidName(name); } - } - public DeobfuscatorBase(OptionsBase optionsBase) { this.optionsBase = optionsBase; StringFeatures = StringFeatures.AllowAll; @@ -502,5 +498,37 @@ namespace de4dot.deobfuscators { var list = new List(DotNetUtils.findAttributes(module.Assembly, attr)); return list.Count == 0 ? null : list[0]; } + + public bool isValidNamespaceName(string ns) { + return checkValidName(ns); + } + + public bool isValidTypeName(string name) { + return checkValidName(name); + } + + public bool isValidMethodName(string name) { + return checkValidName(name); + } + + public bool isValidPropertyName(string name) { + return checkValidName(name); + } + + public bool isValidEventName(string name) { + return checkValidName(name); + } + + public bool isValidFieldName(string name) { + return checkValidName(name); + } + + public bool isValidGenericParamName(string name) { + return checkValidName(name); + } + + public bool isValidMethodArgName(string name) { + return checkValidName(name); + } } } diff --git a/de4dot.code/deobfuscators/IDeobfuscator.cs b/de4dot.code/deobfuscators/IDeobfuscator.cs index 857ae52d..72bb8e61 100644 --- a/de4dot.code/deobfuscators/IDeobfuscator.cs +++ b/de4dot.code/deobfuscators/IDeobfuscator.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using Mono.Cecil; using Mono.MyStuff; using de4dot.blocks; +using de4dot.renamer; namespace de4dot.deobfuscators { interface IDeobfuscatorOptions { @@ -49,11 +50,10 @@ namespace de4dot.deobfuscators { RemoveNamespaceIfOneType = 1, } - interface IDeobfuscator { + interface IDeobfuscator : INameChecker { string Type { get; } string TypeLong { get; } string Name { get; } - Func IsValidName { get; } IDeobfuscatorOptions TheOptions { get; } IOperations Operations { get; set; } StringFeatures StringFeatures { get; } diff --git a/de4dot.code/old_renamer/Module.cs b/de4dot.code/old_renamer/Module.cs index 322b8b22..5062530c 100644 --- a/de4dot.code/old_renamer/Module.cs +++ b/de4dot.code/old_renamer/Module.cs @@ -34,7 +34,7 @@ namespace de4dot.old_renamer { List allMethods; public Func IsValidName { - get { return obfuscatedFile.IsValidName; } + get { return null; } } class RefToDef where R : MemberReference where D : R { diff --git a/de4dot.code/renamer/DerivedFrom.cs b/de4dot.code/renamer/DerivedFrom.cs new file mode 100644 index 00000000..68b7db46 --- /dev/null +++ b/de4dot.code/renamer/DerivedFrom.cs @@ -0,0 +1,62 @@ +/* + Copyright (C) 2011 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; +using de4dot.renamer.asmmodules; + +namespace de4dot.renamer { + class DerivedFrom { + Dictionary classNames = new Dictionary(StringComparer.Ordinal); + Dictionary results = new Dictionary(); + + public DerivedFrom(string className) { + addName(className); + } + + public DerivedFrom(string[] classNames) { + foreach (var className in classNames) + addName(className); + } + + void addName(string className) { + classNames[className] = true; + } + + public bool check(TypeDef type) { + if (results.ContainsKey(type)) + return results[type]; + + bool val; + if (classNames.ContainsKey(type.TypeDefinition.FullName)) + val = true; + else if (type.baseType == null) { + if (type.TypeDefinition.BaseType != null) + val = classNames.ContainsKey(type.TypeDefinition.BaseType.FullName); + else + val = false; + } + else + val = check(type.baseType.typeDef); + + results[type] = val; + return val; + } + } +} diff --git a/de4dot.code/renamer/ExistingNames.cs b/de4dot.code/renamer/ExistingNames.cs new file mode 100644 index 00000000..af809c2f --- /dev/null +++ b/de4dot.code/renamer/ExistingNames.cs @@ -0,0 +1,62 @@ +/* + Copyright (C) 2011 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; + +namespace de4dot.renamer { + class ExistingNames { + Dictionary allNames = new Dictionary(StringComparer.Ordinal); + + public void add(string name) { + allNames[name] = true; + } + + bool exists(string name) { + return allNames.ContainsKey(name); + } + + public string getName(string oldName, INameCreator nameCreator) { + return getName(oldName, () => nameCreator.create()); + } + + public string getName(string oldName, Func createNewName) { + string prevName = null; + while (true) { + var name = createNewName(); + if (name == prevName) + throw new ApplicationException(string.Format("Could not rename symbol to {0}", Utils.toCsharpString(name))); + + if (!exists(name) || name == oldName) { + allNames[name] = true; + return name; + } + + prevName = name; + } + } + + public ExistingNames clone() { + var cn = new ExistingNames(); + foreach (var key in allNames.Keys) + cn.allNames[key] = true; + return cn; + } + } +} diff --git a/de4dot.code/renamer/INameChecker.cs b/de4dot.code/renamer/INameChecker.cs new file mode 100644 index 00000000..d0c2499e --- /dev/null +++ b/de4dot.code/renamer/INameChecker.cs @@ -0,0 +1,31 @@ +/* + Copyright (C) 2011 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.renamer { + interface INameChecker { + bool isValidNamespaceName(string ns); + bool isValidTypeName(string name); + bool isValidMethodName(string name); + bool isValidPropertyName(string name); + bool isValidEventName(string name); + bool isValidFieldName(string name); + bool isValidGenericParamName(string name); + bool isValidMethodArgName(string name); + } +} diff --git a/de4dot.code/renamer/NameCreators.cs b/de4dot.code/renamer/NameCreators.cs new file mode 100644 index 00000000..7ce9df81 --- /dev/null +++ b/de4dot.code/renamer/NameCreators.cs @@ -0,0 +1,229 @@ +/* + Copyright (C) 2011 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 Mono.Cecil; +using de4dot.blocks; + +namespace de4dot.renamer { + interface INameCreator { + INameCreator clone(); + string create(); + } + + class OneNameCreator : INameCreator { + string name; + + public OneNameCreator(string name) { + this.name = name; + } + + public INameCreator clone() { + return this; + } + + public string create() { + return name; + } + } + + class GlobalNameCreator : INameCreator { + INameCreator other; + + public GlobalNameCreator(INameCreator other) { + this.other = other; + } + + public INameCreator clone() { + return this; + } + + public string create() { + return other.create(); + } + } + + class GenericParamNameCreator : INameCreator { + static string[] names = new string[] { "T", "U", "V", "W", "X", "Y", "Z" }; + int index = 0; + + public string create() { + if (index < names.Length) + return names[index++]; + return string.Format("T{0}", index++); + } + + public INameCreator clone() { + var rv = new GenericParamNameCreator(); + rv.index = index; + return rv; + } + } + + class NameCreator : INameCreator { + string prefix; + int num; + + public NameCreator(string prefix, int num = 0) { + this.prefix = prefix; + this.num = num; + } + + public INameCreator clone() { + return new NameCreator(prefix, num); + } + + public string create() { + return prefix + num++; + } + } + + // Like NameCreator but don't add the counter the first time + class NameCreator2 : INameCreator { + string prefix; + int num; + const string separator = "_"; + + public NameCreator2(string prefix, int num = 0) { + this.prefix = prefix; + this.num = num; + } + + public INameCreator clone() { + return new NameCreator2(prefix, num); + } + + public string create() { + string rv; + if (num == 0) + rv = prefix; + else + rv = prefix + separator + num; + num++; + return rv; + } + } + + interface ITypeNameCreator { + string create(TypeDefinition typeDefinition, string newBaseTypeName = null); + } + + class NameInfos { + IList nameInfos = new List(); + + class NameInfo { + public string name; + public INameCreator nameCreator; + public NameInfo(string name, INameCreator nameCreator) { + this.name = name; + this.nameCreator = nameCreator; + } + } + + public void add(string name, INameCreator nameCreator) { + nameInfos.Add(new NameInfo(name, nameCreator)); + } + + public INameCreator find(string typeName) { + foreach (var nameInfo in nameInfos) { + if (typeName.Contains(nameInfo.name)) + return nameInfo.nameCreator; + } + + return null; + } + } + + class TypeNameCreator : ITypeNameCreator { + ExistingNames existingNames; + INameCreator createUnknownTypeName; + INameCreator createEnumName; + INameCreator createStructName; + INameCreator createDelegateName; + INameCreator createClassName; + INameCreator createInterfaceName; + NameInfos nameInfos = new NameInfos(); + + public TypeNameCreator(ExistingNames existingNames) { + this.existingNames = existingNames; + createUnknownTypeName = createNameCreator("Type"); + createEnumName = createNameCreator("Enum"); + createStructName = createNameCreator("Struct"); + createDelegateName = createNameCreator("Delegate"); + createClassName = createNameCreator("Class"); + createInterfaceName = createNameCreator("Interface"); + + var names = new string[] { + "Exception", + "EventArgs", + "Attribute", + "Form", + "Dialog", + "Control", + }; + foreach (var name in names) + nameInfos.add(name, createNameCreator(name)); + } + + protected virtual INameCreator createNameCreator(string prefix) { + return new NameCreator(prefix); + } + + public string create(TypeDefinition typeDefinition, string newBaseTypeName = null) { + var nameCreator = getNameCreator(typeDefinition, newBaseTypeName); + return existingNames.getName(typeDefinition.Name, nameCreator); + } + + INameCreator getNameCreator(TypeDefinition typeDefinition, string newBaseTypeName) { + var nameCreator = createUnknownTypeName; + if (typeDefinition.IsEnum) + nameCreator = createEnumName; + else if (typeDefinition.IsValueType) + nameCreator = createStructName; + else if (typeDefinition.IsClass) { + if (typeDefinition.BaseType != null) { + if (MemberReferenceHelper.verifyType(typeDefinition.BaseType, "mscorlib", "System.Delegate")) + nameCreator = createDelegateName; + else if (MemberReferenceHelper.verifyType(typeDefinition.BaseType, "mscorlib", "System.MulticastDelegate")) + nameCreator = createDelegateName; + else { + nameCreator = nameInfos.find(newBaseTypeName ?? typeDefinition.BaseType.Name); + if (nameCreator == null) + nameCreator = createClassName; + } + } + else + nameCreator = createClassName; + } + else if (typeDefinition.IsInterface) + nameCreator = createInterfaceName; + return nameCreator; + } + } + + class GlobalTypeNameCreator : TypeNameCreator { + public GlobalTypeNameCreator(ExistingNames existingNames) + : base(existingNames) { + } + + protected override INameCreator createNameCreator(string prefix) { + return new GlobalNameCreator(base.createNameCreator("G" + prefix)); + } + } +} diff --git a/de4dot.code/renamer/Renamer.cs b/de4dot.code/renamer/Renamer.cs index 71e60b25..23e9ba38 100644 --- a/de4dot.code/renamer/Renamer.cs +++ b/de4dot.code/renamer/Renamer.cs @@ -17,21 +17,177 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; using de4dot.renamer.asmmodules; namespace de4dot.renamer { + class MemberInfo { + Ref memberRef; + public string oldFullName; + public string oldName; + public string newName; + public bool renamed; + + public MemberInfo(Ref memberRef) { + this.memberRef = memberRef; + oldFullName = memberRef.memberReference.FullName; + oldName = memberRef.memberReference.Name; + newName = memberRef.memberReference.Name; + } + + public void rename(string newTypeName) { + renamed = true; + newName = newTypeName; + } + + public bool gotNewName() { + return oldName != newName; + } + } + + class TypeInfo : MemberInfo { + public string oldNamespace; + public string newNamespace; + + public TypeInfo(TypeDef type) + : base(type) { + oldNamespace = type.TypeDefinition.Namespace; + } + } + + class GenericParamInfo : MemberInfo { + public GenericParamInfo(GenericParamDef genericParamDef) + : base(genericParamDef) { + } + } + class Renamer { - bool renameSymbols = true; - bool renameFields = true; - bool renameProperties = true; - bool renameEvents = true; - bool renameMethods = true; + 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 RenameGenericParams { get; set; } + public bool RenameMethodArgs { get; set; } Modules modules = new Modules(); + DerivedFrom isWinFormsClass; + Dictionary allTypeInfos = new Dictionary(); + Dictionary allGenericParamInfos = new Dictionary(); + Dictionary suggestedMethodNames = new Dictionary(); + + static string[] WINFORMS_CLASSES = new string[] { +#region Win Forms class names + "System.Windows.Forms.Control", + "System.Windows.Forms.AxHost", + "System.Windows.Forms.ButtonBase", + "System.Windows.Forms.Button", + "System.Windows.Forms.CheckBox", + "System.Windows.Forms.RadioButton", + "System.Windows.Forms.DataGrid", + "System.Windows.Forms.DataGridView", + "System.Windows.Forms.DataVisualization.Charting.Chart", + "System.Windows.Forms.DateTimePicker", + "System.Windows.Forms.GroupBox", + "System.Windows.Forms.Integration.ElementHost", + "System.Windows.Forms.Label", + "System.Windows.Forms.LinkLabel", + "System.Windows.Forms.ListControl", + "System.Windows.Forms.ComboBox", + "Microsoft.VisualBasic.Compatibility.VB6.DriveListBox", + "System.Windows.Forms.DataGridViewComboBoxEditingControl", + "System.Windows.Forms.ListBox", + "Microsoft.VisualBasic.Compatibility.VB6.DirListBox", + "Microsoft.VisualBasic.Compatibility.VB6.FileListBox", + "System.Windows.Forms.CheckedListBox", + "System.Windows.Forms.ListView", + "System.Windows.Forms.MdiClient", + "System.Windows.Forms.MonthCalendar", + "System.Windows.Forms.PictureBox", + "System.Windows.Forms.PrintPreviewControl", + "System.Windows.Forms.ProgressBar", + "System.Windows.Forms.ScrollableControl", + "System.Windows.Forms.ContainerControl", + "System.Windows.Forms.Form", + "System.ComponentModel.Design.CollectionEditor.CollectionForm", + "System.Messaging.Design.QueuePathDialog", + "System.ServiceProcess.Design.ServiceInstallerDialog", + "System.Web.UI.Design.WebControls.CalendarAutoFormatDialog", + "System.Web.UI.Design.WebControls.RegexEditorDialog", + "System.Windows.Forms.Design.ComponentEditorForm", + "System.Windows.Forms.PrintPreviewDialog", + "System.Windows.Forms.ThreadExceptionDialog", + "System.Workflow.Activities.Rules.Design.RuleConditionDialog", + "System.Workflow.Activities.Rules.Design.RuleSetDialog", + "System.Workflow.ComponentModel.Design.ThemeConfigurationDialog", + "System.Workflow.ComponentModel.Design.TypeBrowserDialog", + "System.Workflow.ComponentModel.Design.WorkflowPageSetupDialog", + "System.Windows.Forms.PropertyGrid", + "System.Windows.Forms.SplitContainer", + "System.Windows.Forms.ToolStripContainer", + "System.Windows.Forms.ToolStripPanel", + "System.Windows.Forms.UpDownBase", + "System.Windows.Forms.DomainUpDown", + "System.Windows.Forms.NumericUpDown", + "System.Windows.Forms.UserControl", + "Microsoft.VisualBasic.Compatibility.VB6.ADODC", + "System.Web.UI.Design.WebControls.ParameterEditorUserControl", + "System.Workflow.ComponentModel.Design.WorkflowOutline", + "System.Workflow.ComponentModel.Design.WorkflowView", + "System.Windows.Forms.Design.ComponentTray", + "System.Windows.Forms.Panel", + "System.Windows.Forms.Design.ComponentEditorPage", + "System.Windows.Forms.FlowLayoutPanel", + "System.Windows.Forms.SplitterPanel", + "System.Windows.Forms.TableLayoutPanel", + "System.ComponentModel.Design.ByteViewer", + "System.Windows.Forms.TabPage", + "System.Windows.Forms.ToolStripContentPanel", + "System.Windows.Forms.ToolStrip", + "System.Windows.Forms.BindingNavigator", + "System.Windows.Forms.MenuStrip", + "System.Windows.Forms.StatusStrip", + "System.Windows.Forms.ToolStripDropDown", + "System.Windows.Forms.ToolStripDropDownMenu", + "System.Windows.Forms.ContextMenuStrip", + "System.Windows.Forms.ToolStripOverflow", + "System.Windows.Forms.ScrollBar", + "System.Windows.Forms.HScrollBar", + "System.Windows.Forms.VScrollBar", + "System.Windows.Forms.Splitter", + "System.Windows.Forms.StatusBar", + "System.Windows.Forms.TabControl", + "System.Windows.Forms.TextBoxBase", + "System.Windows.Forms.MaskedTextBox", + "System.Windows.Forms.RichTextBox", + "System.Windows.Forms.TextBox", + "System.Windows.Forms.DataGridTextBox", + "System.Windows.Forms.DataGridViewTextBoxEditingControl", + "System.Windows.Forms.ToolBar", + "System.Windows.Forms.TrackBar", + "System.Windows.Forms.TreeView", + "System.ComponentModel.Design.ObjectSelectorEditor.Selector", + "System.Windows.Forms.WebBrowserBase", + "System.Windows.Forms.WebBrowser", +#endregion + }; public Renamer(IEnumerable files) { + RenameNamespaces = true; + RenameTypes = true; + RenameProperties = true; + RenameEvents = true; + RenameFields = true; + RenameGenericParams = true; + RenameMethodArgs = true; + foreach (var file in files) modules.add(new Module(file)); + + isWinFormsClass = new DerivedFrom(WINFORMS_CLASSES); } public void rename() { @@ -41,7 +197,224 @@ namespace de4dot.renamer { modules.initialize(); modules.initializeVirtualMembers(); + renameTypeDefinitions(); modules.cleanUp(); } + + void renameTypeDefinitions() { + Log.v("Renaming obfuscated type definitions"); + + prepareRenameTypes(); + } + + void prepareRenameTypes() { + foreach (var type in modules.AllTypes) + allTypeInfos[type] = new TypeInfo(type); + + var state = new TypeRenamerState(); + prepareRenameTypes(modules.BaseTypes, state); + fixClsTypeNames(); + renameTypeDefinitions(modules.NonNestedTypes); + } + + void renameTypeDefinitions(IEnumerable typeDefs) { + Log.indent(); + foreach (var typeDef in typeDefs) { + rename(typeDef); + renameTypeDefinitions(typeDef.NestedTypes); + } + Log.deIndent(); + } + + void rename(TypeDef type) { + var typeDefinition = type.TypeDefinition; + var info = allTypeInfos[type]; + + Log.v("Type: {0} ({1:X8})", typeDefinition.FullName, typeDefinition.MetadataToken.ToUInt32()); + Log.indent(); + + renameGenericParams(type.GenericParams); + + if (RenameTypes && info.gotNewName()) { + var old = typeDefinition.Name; + typeDefinition.Name = info.newName; + Log.v("Name: {0} => {1}", old, typeDefinition.Name); + } + + if (RenameNamespaces && info.newNamespace != null) { + var old = typeDefinition.Namespace; + typeDefinition.Namespace = info.newNamespace; + Log.v("Namespace: {0} => {1}", old, typeDefinition.Namespace); + } + + Log.deIndent(); + } + + void renameGenericParams(IEnumerable genericParams) { + if (!RenameGenericParams) + return; + foreach (var param in genericParams) { + var info = allGenericParamInfos[param]; + if (!info.gotNewName()) + continue; + param.GenericParameter.Name = info.newName; + Log.v("GenParam: {0} => {1}", info.oldFullName, param.GenericParameter.FullName); + } + } + + // Make sure the renamed types are using valid CLS names. That means renaming all + // generic types from eg. Class1 to Class1`2. If we don't do this, some decompilers + // (eg. ILSpy v1.0) won't produce correct output. + void fixClsTypeNames() { + foreach (var type in modules.NonNestedTypes) + fixClsTypeNames(null, type); + } + + void fixClsTypeNames(TypeDef nesting, TypeDef nested) { + int nestingCount = nesting == null ? 0 : nesting.GenericParams.Count; + int arity = nested.GenericParams.Count - nestingCount; + var nestedInfo = allTypeInfos[nested]; + if (nestedInfo.renamed && arity > 0) + nestedInfo.newName += "`" + arity; + foreach (var nestedType in nested.NestedTypes) + fixClsTypeNames(nested, nestedType); + } + + void prepareRenameTypes(IEnumerable types, TypeRenamerState state) { + foreach (var typeDef in types) { + prepareRenameTypes(typeDef, state); + prepareRenameTypes(typeDef.derivedTypes, state); + } + } + + void prepareRenameTypes(TypeDef type, TypeRenamerState state) { + var info = allTypeInfos[type]; + var checker = type.Module.ObfuscatedFile.NameChecker; + + if (RenameNamespaces) { + if (info.newNamespace == null && info.oldNamespace != "") { + if (!checker.isValidNamespaceName(info.oldNamespace)) { + info.newNamespace = state.createNamespace(info.oldNamespace); + } + } + } + + if (RenameTypes) { + if (info.oldFullName != "" && !checker.isValidTypeName(info.oldName)) { + string origClassName = null; + if (isWinFormsClass.check(type)) + origClassName = findWindowsFormsClassName(type); + if (origClassName != null && checker.isValidTypeName(origClassName)) + info.rename(state.getTypeName(info.oldName, origClassName)); + else { + ITypeNameCreator nameCreator = type.isGlobalType() ? + state.globalTypeNameCreator : + state.internalTypeNameCreator; + string newBaseType = null; + TypeInfo baseInfo; + if (type.baseType != null && allTypeInfos.TryGetValue(type.baseType.typeDef, out baseInfo)) { + if (baseInfo.renamed) + newBaseType = baseInfo.newName; + } + info.rename(nameCreator.create(type.TypeDefinition, newBaseType)); + } + } + } + + if (RenameGenericParams) + prepareRenameGenericParams(type.GenericParams, checker); + } + + void prepareRenameGenericParams(IEnumerable genericParams, INameChecker checker, IEnumerable otherGenericParams = null) { + var usedNames = new Dictionary(StringComparer.Ordinal); + var nameCreator = new GenericParamNameCreator(); + + foreach (var gp in genericParams) + allGenericParamInfos[gp] = new GenericParamInfo(gp); + + if (otherGenericParams != null) { + foreach (var param in otherGenericParams) { + var gpInfo = allGenericParamInfos[param]; + usedNames[gpInfo.newName] = true; + } + } + + foreach (var param in genericParams) { + var gpInfo = allGenericParamInfos[param]; + if (!checker.isValidGenericParamName(gpInfo.oldName) || usedNames.ContainsKey(gpInfo.oldName)) { + string newName; + do { + newName = nameCreator.create(); + } while (usedNames.ContainsKey(newName)); + usedNames[newName] = true; + gpInfo.rename(newName); + } + } + } + + string findWindowsFormsClassName(TypeDef type) { + foreach (var methodDef in type.getAllMethods()) { + if (methodDef.MethodDefinition.Body == null) + continue; + if (methodDef.MethodDefinition.IsStatic || methodDef.MethodDefinition.IsVirtual) + continue; + var instructions = methodDef.MethodDefinition.Body.Instructions; + for (int i = 2; i < instructions.Count; i++) { + var call = instructions[i]; + if (call.OpCode.Code != Code.Call && call.OpCode.Code != Code.Callvirt) + continue; + if (!isWindowsFormsSetNameMethod(call.Operand as MethodReference)) + continue; + + var ldstr = instructions[i - 1]; + if (ldstr.OpCode.Code != Code.Ldstr) + continue; + var className = ldstr.Operand as string; + if (className == null) + continue; + + if (DotNetUtils.getArgIndex(methodDef.MethodDefinition, instructions[i - 2]) != 0) + continue; + + findInitializeComponentMethod(type, methodDef); + return className; + } + } + return null; + } + + void findInitializeComponentMethod(TypeDef type, MethodDef possibleInitMethod) { + foreach (var methodDef in type.getAllMethods()) { + if (methodDef.MethodDefinition.Name != ".ctor") + continue; + if (methodDef.MethodDefinition.Body == null) + continue; + foreach (var instr in methodDef.MethodDefinition.Body.Instructions) { + if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt) + continue; + if (!MemberReferenceHelper.compareMethodReferenceAndDeclaringType(possibleInitMethod.MethodDefinition, instr.Operand as MethodReference)) + continue; + + suggestedMethodNames[possibleInitMethod] = "InitializeComponent"; + return; + } + } + } + + static bool isWindowsFormsSetNameMethod(MethodReference method) { + if (method == null) + return false; + if (method.Name != "set_Name") + return false; + if (method.MethodReturnType.ReturnType.FullName != "System.Void") + return false; + if (method.Parameters.Count != 1) + return false; + if (method.Parameters[0].ParameterType.FullName != "System.String") + return false; + if (!method.DeclaringType.FullName.StartsWith("System.Windows.Forms.", StringComparison.Ordinal)) + return false; + return true; + } } } diff --git a/de4dot.code/renamer/TypeRenamerState.cs b/de4dot.code/renamer/TypeRenamerState.cs new file mode 100644 index 00000000..8da9671b --- /dev/null +++ b/de4dot.code/renamer/TypeRenamerState.cs @@ -0,0 +1,50 @@ +/* + Copyright (C) 2011 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; +using System.Collections.Generic; + +namespace de4dot.renamer { + class TypeRenamerState { + ExistingNames existingNames; + Dictionary namespaceToNewName; + INameCreator createNamespaceName; + public ITypeNameCreator globalTypeNameCreator; + public ITypeNameCreator internalTypeNameCreator; + + public TypeRenamerState() { + existingNames = new ExistingNames(); + namespaceToNewName = new Dictionary(StringComparer.Ordinal); + createNamespaceName = new GlobalNameCreator(new NameCreator("ns")); + globalTypeNameCreator = new GlobalTypeNameCreator(existingNames); + internalTypeNameCreator = new TypeNameCreator(existingNames); + } + + public string getTypeName(string oldName, string newName) { + return existingNames.getName(oldName, new NameCreator2(newName)); + } + + public string createNamespace(string ns) { + string newName; + if (namespaceToNewName.TryGetValue(ns, out newName)) + return newName; + return namespaceToNewName[ns] = createNamespaceName.create(); + } + } +} diff --git a/de4dot.code/renamer/asmmodules/GenericParamDef.cs b/de4dot.code/renamer/asmmodules/GenericParamDef.cs new file mode 100644 index 00000000..ef448c25 --- /dev/null +++ b/de4dot.code/renamer/asmmodules/GenericParamDef.cs @@ -0,0 +1,43 @@ +/* + Copyright (C) 2011 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 Mono.Cecil; + +namespace de4dot.renamer.asmmodules { + class GenericParamDef : Ref { + public GenericParameter GenericParameter { + get { return (GenericParameter)memberReference; } + } + + public GenericParamDef(GenericParameter genericParameter, int index) + : base(genericParameter, null, index) { + } + + public static List createGenericParamDefList(IEnumerable parameters) { + var list = new List(); + if (parameters == null) + return list; + int i = 0; + foreach (var param in parameters) + list.Add(new GenericParamDef(param, i++)); + return list; + } + } +} diff --git a/de4dot.code/renamer/asmmodules/MethodDef.cs b/de4dot.code/renamer/asmmodules/MethodDef.cs index 8c396d57..1a4cad1b 100644 --- a/de4dot.code/renamer/asmmodules/MethodDef.cs +++ b/de4dot.code/renamer/asmmodules/MethodDef.cs @@ -17,16 +17,20 @@ along with de4dot. If not, see . */ +using System.Collections.Generic; using Mono.Cecil; namespace de4dot.renamer.asmmodules { class MethodDef : Ref { + IList genericParams; + public MethodDefinition MethodDefinition { get { return (MethodDefinition)memberReference; } } public MethodDef(MethodDefinition methodDefinition, TypeDef owner, int index) : base(methodDefinition, owner, index) { + genericParams = GenericParamDef.createGenericParamDefList(MethodDefinition.GenericParameters); } public bool isPublic() { diff --git a/de4dot.code/renamer/asmmodules/Module.cs b/de4dot.code/renamer/asmmodules/Module.cs index 9f3580af..006a01b6 100644 --- a/de4dot.code/renamer/asmmodules/Module.cs +++ b/de4dot.code/renamer/asmmodules/Module.cs @@ -40,6 +40,10 @@ namespace de4dot.renamer.asmmodules { } } + public IObfuscatedFile ObfuscatedFile { + get { return obfuscatedFile; } + } + public string Filename { get { return obfuscatedFile.Filename; } } diff --git a/de4dot.code/renamer/asmmodules/Modules.cs b/de4dot.code/renamer/asmmodules/Modules.cs index 934e6aa3..40749571 100644 --- a/de4dot.code/renamer/asmmodules/Modules.cs +++ b/de4dot.code/renamer/asmmodules/Modules.cs @@ -30,8 +30,20 @@ namespace de4dot.renamer.asmmodules { AssemblyHash assemblyHash = new AssemblyHash(); List allTypes = new List(); - List baseTypes = new List(); //TODO: Do we need this? - List nonNestedTypes; //TODO: Do we need this? + List baseTypes = new List(); + List nonNestedTypes; + + public IEnumerable AllTypes { + get { return allTypes; } + } + + public IEnumerable BaseTypes { + get { return baseTypes; } + } + + public List NonNestedTypes { + get { return nonNestedTypes; } + } class AssemblyHash { IDictionary assemblyHash = new Dictionary(StringComparer.Ordinal); diff --git a/de4dot.code/renamer/asmmodules/Ref.cs b/de4dot.code/renamer/asmmodules/Ref.cs index 12e325d0..5c02a696 100644 --- a/de4dot.code/renamer/asmmodules/Ref.cs +++ b/de4dot.code/renamer/asmmodules/Ref.cs @@ -21,7 +21,7 @@ using Mono.Cecil; namespace de4dot.renamer.asmmodules { abstract class Ref { - protected readonly MemberReference memberReference; + public readonly MemberReference memberReference; public int Index { get; set; } public TypeDef Owner { get; set; } diff --git a/de4dot.code/renamer/asmmodules/TypeDef.cs b/de4dot.code/renamer/asmmodules/TypeDef.cs index e67d8294..0bc63318 100644 --- a/de4dot.code/renamer/asmmodules/TypeDef.cs +++ b/de4dot.code/renamer/asmmodules/TypeDef.cs @@ -204,6 +204,7 @@ namespace de4dot.renamer.asmmodules { MethodDefDict methods = new MethodDefDict(); PropertyDefDict properties = new PropertyDefDict(); TypeDefDict types = new TypeDefDict(); + List genericParams; internal TypeInfo baseType = null; internal IList interfaces = new List(); // directly implemented interfaces internal IList derivedTypes = new List(); @@ -214,10 +215,18 @@ namespace de4dot.renamer.asmmodules { Dictionary allImplementedInterfaces = new Dictionary(); InterfaceMethodInfos interfaceMethodInfos = new InterfaceMethodInfos(); + public Module Module { + get { return module; } + } + public bool HasModule { get { return module != null; } } + public IList GenericParams { + get { return genericParams; } + } + public IEnumerable NestedTypes { get { return types.getSorted(); } } @@ -231,6 +240,7 @@ namespace de4dot.renamer.asmmodules { public TypeDef(TypeDefinition typeDefinition, Module module, int index) : base(typeDefinition, null, index) { this.module = module; + genericParams = GenericParamDef.createGenericParamDefList(TypeDefinition.GenericParameters); } public void addInterface(TypeDef ifaceDef, TypeReference iface) { @@ -290,6 +300,28 @@ namespace de4dot.renamer.asmmodules { add(new PropertyDef(type.Properties[i], this, i)); } + public bool isNested() { + return NestingType != null; + } + + public bool isGlobalType() { + if (!isNested()) + return TypeDefinition.IsPublic; + var mask = TypeDefinition.Attributes & TypeAttributes.VisibilityMask; + switch (mask) { + case TypeAttributes.NestedPrivate: + case TypeAttributes.NestedAssembly: + case TypeAttributes.NestedFamANDAssem: + return false; + case TypeAttributes.NestedPublic: + case TypeAttributes.NestedFamily: + case TypeAttributes.NestedFamORAssem: + return NestingType.isGlobalType(); + default: + return false; + } + } + public void initializeVirtualMembers(MethodNameScopes scopes, IResolver resolver) { if (initializeVirtualMembersCalled) return;