/* 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 de4dot.renamer.asmmodules; namespace de4dot.renamer { class Renamer { public bool RenameNamespaces { get; set; } public bool RenameTypes { get; set; } public bool RenameGenericParams { 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; } Modules modules = new Modules(); MemberInfos memberInfos = new MemberInfos(); 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)); } public void rename() { if (modules.Empty) return; Log.n("Renaming all obfuscated symbols"); modules.initialize(); modules.initializeVirtualMembers(); memberInfos.initialize(modules); renameTypeDefinitions(); renameTypeReferences(); modules.onTypesRenamed(); prepareRenameMemberDefinitions(); modules.cleanUp(); } void renameTypeDefinitions() { Log.v("Renaming obfuscated type definitions"); prepareRenameTypes(modules.BaseTypes, new TypeRenamerState()); 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 = memberInfos.type(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 = memberInfos.gparam(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 = memberInfos.type(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) { memberInfos.type(typeDef).prepareRenameTypes(state); prepareRenameTypes(typeDef.derivedTypes, state); } } void renameTypeReferences() { Log.v("Renaming references to type definitions"); var theModules = modules.TheModules; foreach (var module in theModules) { if (theModules.Count > 1) Log.v("Renaming references to type definitions ({0})", module.Filename); Log.indent(); foreach (var refToDef in module.TypeRefsToRename) { refToDef.reference.Name = refToDef.definition.Name; refToDef.reference.Namespace = refToDef.definition.Namespace; } Log.deIndent(); } } void prepareRenameMemberDefinitions() { Log.v("Renaming member definitions #1"); prepareRenameEntryPoints(); foreach (var typeDef in modules.BaseTypes) memberInfos.type(typeDef).variableNameState = new VariableNameState(); foreach (var typeDef in modules.AllTypes) prepareRenameMembers(typeDef); } Dictionary prepareRenameMembersCalled = new Dictionary(); void prepareRenameMembers(TypeDef type) { if (prepareRenameMembersCalled.ContainsKey(type)) return; prepareRenameMembersCalled[type] = true; foreach (var ifaceInfo in type.interfaces) prepareRenameMembers(ifaceInfo.typeDef); if (type.baseType != null) prepareRenameMembers(type.baseType.typeDef); TypeInfo info; if (memberInfos.tryGetType(type, out info)) info.prepareRenameMembers(); } void prepareRenameEntryPoints() { foreach (var module in modules.TheModules) { var entryPoint = module.ModuleDefinition.EntryPoint; if (entryPoint == null) continue; var methodDef = modules.resolve(entryPoint); if (methodDef == null) { Log.w(string.Format("Could not find entry point. Module: {0}, Method: {1}", module.ModuleDefinition.FullyQualifiedName, entryPoint)); continue; } if (!methodDef.isStatic()) continue; memberInfos.method(methodDef).suggestedName = "Main"; if (methodDef.ParamDefs.Count == 1) { var paramDef = methodDef.ParamDefs[0]; var type = paramDef.ParameterDefinition.ParameterType; if (type.FullName == "System.String[]") memberInfos.param(paramDef).newName = "args"; } } } } }