diff --git a/Confuser.CLI/Program.cs b/Confuser.CLI/Program.cs index 9d3323c02..b8929c45d 100644 --- a/Confuser.CLI/Program.cs +++ b/Confuser.CLI/Program.cs @@ -28,6 +28,7 @@ static int Main(string[] args) { bool quiet = false; bool dumpRequested = false; string dumpPath = null; + string inputMap = null; int verbosity = 0; string outDir = null; string snKeyPath = null; @@ -65,6 +66,9 @@ static int Main(string[] args) { }, { "dump:", "write a diagnostic report (optionally to the given file).", value => { dumpRequested = true; if (!string.IsNullOrEmpty(value)) dumpPath = value; } + }, { + "map=", "reuse names from a previous symbol map for consistent re-obfuscation.", + value => { inputMap = value; } } }; @@ -147,6 +151,9 @@ static int Main(string[] args) { parameters.Project = proj; } + if (inputMap != null && parameters.Project != null) + parameters.Project.InputSymbolMap = inputMap; + int retVal = RunProject(parameters, quiet, verbosity, dumpRequested, dumpPath); if (NeedPause() && !noPause) { @@ -284,6 +291,7 @@ static void PrintUsage() { WriteLine(" -v|verbose : increase verbosity (-v debug, -vv trace)."); WriteLine(" -q|quiet : only show warnings and errors."); WriteLine(" -dump : write a diagnostic report (-dump= for a custom path)."); + WriteLine(" -map : reuse names from a previous symbol map for consistent re-obfuscation."); } static void WriteLineWithColor(ConsoleColor color, string txt) { diff --git a/Confuser.Core/Project/ConfuserPrj.xsd b/Confuser.Core/Project/ConfuserPrj.xsd index 65c04f6d7..e99703b12 100644 --- a/Confuser.Core/Project/ConfuserPrj.xsd +++ b/Confuser.Core/Project/ConfuserPrj.xsd @@ -75,6 +75,7 @@ + diff --git a/Confuser.Core/Project/ConfuserProject.cs b/Confuser.Core/Project/ConfuserProject.cs index 3c0886a8e..75d2f5361 100644 --- a/Confuser.Core/Project/ConfuserProject.cs +++ b/Confuser.Core/Project/ConfuserProject.cs @@ -528,6 +528,14 @@ public ConfuserProject() { /// true if debug symbols are generated; otherwise, false. public bool Debug { get; set; } + /// + /// Gets or sets the path to a symbol map from a previous obfuscation run. When set, the + /// renamer reuses the obfuscated names recorded in that map, so re-obfuscation produces + /// consistent names across builds (e.g. to patch already-deployed obfuscated assemblies). + /// + /// The path to the input symbol map, or null to generate fresh names. + public string InputSymbolMap { get; set; } + /// /// Gets or sets the output directory. /// @@ -594,6 +602,12 @@ public XmlDocument Save() { elem.Attributes.Append(debugAttr); } + if (InputSymbolMap != null) { + XmlAttribute mapAttr = xmlDoc.CreateAttribute("inputSymbolMap"); + mapAttr.Value = InputSymbolMap; + elem.Attributes.Append(mapAttr); + } + foreach (Rule i in Rules) elem.AppendChild(i.Save(xmlDoc)); @@ -655,6 +669,11 @@ public void Load(XmlDocument doc, string baseDirRoot = null) { else Debug = false; + if (docElem.Attributes["inputSymbolMap"] != null) + InputSymbolMap = docElem.Attributes["inputSymbolMap"].Value.NullIfEmpty(); + else + InputSymbolMap = null; + Packer = null; Clear(); ProbePaths.Clear(); @@ -724,6 +743,7 @@ public ConfuserProject Clone() { var ret = new ConfuserProject(); ret.Seed = Seed; ret.Debug = Debug; + ret.InputSymbolMap = InputSymbolMap; ret.OutputDirectory = OutputDirectory; ret.BaseDirectory = BaseDirectory; ret.Packer = Packer == null ? null : Packer.Clone(); diff --git a/Confuser.Renamer/NameService.cs b/Confuser.Renamer/NameService.cs index ae5a481ec..69bb5aeac 100644 --- a/Confuser.Renamer/NameService.cs +++ b/Confuser.Renamer/NameService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Text; using Confuser.Core; @@ -8,6 +9,7 @@ using Confuser.Renamer.Analyzers; using Confuser.Renamer.Properties; using dnlib.DotNet; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer { public interface INameService { @@ -67,6 +69,7 @@ internal class NameService : INameService { readonly Dictionary _originalToObfuscatedNameMap = new Dictionary(); readonly Dictionary _obfuscatedToOriginalNameMap = new Dictionary(); readonly Dictionary _prefixesMap = new Dictionary(); + readonly bool hasInputMap; internal ReversibleRenamer reversibleRenamer; public NameService(ConfuserContext context) { @@ -74,6 +77,7 @@ public NameService(ConfuserContext context) { storage = new VTableStorage(context.Logger); random = context.Registry.GetService().GetRandomGenerator(NameProtection._FullId); nameSeed = random.NextBytes(20); + hasInputMap = LoadInputSymbolMap(); Renamers = new List { new InterReferenceAnalyzer(), @@ -89,6 +93,57 @@ public NameService(ConfuserContext context) { public IList Renamers { get; private set; } + /// + /// Loads the project's input symbol map (if any) so previously assigned obfuscated names + /// are reused, giving consistent names across re-obfuscation runs. The file uses the same + /// "{obfuscated}\t{original}" format as the exported symbols.map. + /// + /// true if a non-empty map was loaded; otherwise false. + bool LoadInputSymbolMap() { + var mapPath = context.Project.InputSymbolMap; + if (string.IsNullOrEmpty(mapPath)) + return false; + + try { + if (!Path.IsPathRooted(mapPath)) + mapPath = Path.Combine(context.BaseDirectory, mapPath); + + if (!File.Exists(mapPath)) { + context.Logger.LogWarning("Input symbol map not found: '{0}'. Names will be generated fresh.", mapPath); + return false; + } + + int count = 0; + foreach (var line in File.ReadAllLines(mapPath)) { + if (string.IsNullOrWhiteSpace(line)) + continue; + + int tab = line.IndexOf('\t'); + if (tab <= 0) + continue; + + var obfuscated = line.Substring(0, tab); + var original = line.Substring(tab + 1); + + // The map is "{obfuscated}\t{original}"; reuse looks up original -> obfuscated. + if (!_obfuscatedToOriginalNameMap.ContainsKey(obfuscated)) + _obfuscatedToOriginalNameMap[obfuscated] = original; + if (!_originalToObfuscatedNameMap.ContainsKey(original)) { + _originalToObfuscatedNameMap[original] = obfuscated; + count++; + } + } + + context.Logger.LogInformation( + "Loaded {0} name mappings from input symbol map for consistent re-obfuscation.", count); + return count > 0; + } + catch (Exception ex) { + context.Logger.LogWarning(ex, "Failed to load input symbol map. Names will be generated fresh."); + return false; + } + } + public VTableStorage GetVTables() { return storage; } @@ -255,7 +310,10 @@ public string ObfuscateName(string format, string name, RenameMode mode, bool pr hash = Utils.SHA1(hash); } - if (mode == RenameMode.Decodable || mode == RenameMode.Sequential) { + // Decodable/Sequential always record the mapping (they need it to be reversible). + // When an input symbol map is in use, record every mode so newly-generated names are + // reused on the next run and the exported map stays complete and chainable. + if (mode == RenameMode.Decodable || mode == RenameMode.Sequential || hasInputMap) { _obfuscatedToOriginalNameMap.Add(newName, name); _originalToObfuscatedNameMap.Add(name, newName); } diff --git a/Confuser2.sln b/Confuser2.sln index 47acc3a6c..bec181e0c 100644 --- a/Confuser2.sln +++ b/Confuser2.sln @@ -211,6 +211,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Confuser.Analyzers.Test", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntiDebug.Test", "Tests\AntiDebug.Test\AntiDebug.Test.csproj", "{47197200-B8CB-400A-B1BD-84975FEC8C28}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SymbolMapReuse.Test", "Tests\SymbolMapReuse.Test\SymbolMapReuse.Test.csproj", "{591069EF-617C-4284-A968-5DB55BE1E1EF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1385,6 +1387,18 @@ Global {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x64.Build.0 = Release|Any CPU {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x86.ActiveCfg = Release|Any CPU {47197200-B8CB-400A-B1BD-84975FEC8C28}.Release|x86.Build.0 = Release|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Debug|x64.ActiveCfg = Debug|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Debug|x64.Build.0 = Debug|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Debug|x86.ActiveCfg = Debug|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Debug|x86.Build.0 = Debug|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|Any CPU.Build.0 = Release|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|x64.ActiveCfg = Release|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|x64.Build.0 = Release|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|x86.ActiveCfg = Release|Any CPU + {591069EF-617C-4284-A968-5DB55BE1E1EF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1478,6 +1492,7 @@ Global {4458415A-0F5E-4136-B723-7A67955D6047} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} {1B22CAAE-FC4A-478D-BD68-D29A3081F938} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} {47197200-B8CB-400A-B1BD-84975FEC8C28} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} + {591069EF-617C-4284-A968-5DB55BE1E1EF} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0D937D9E-E04B-4A68-B639-D4260473A388} diff --git a/ConfuserEx/ViewModel/Project/ProjectVM.cs b/ConfuserEx/ViewModel/Project/ProjectVM.cs index 2063a166c..fe849d730 100644 --- a/ConfuserEx/ViewModel/Project/ProjectVM.cs +++ b/ConfuserEx/ViewModel/Project/ProjectVM.cs @@ -57,6 +57,11 @@ public bool Debug { set { SetProperty(proj.Debug != value, val => proj.Debug = val, value, "Debug"); } } + public string InputSymbolMap { + get { return proj.InputSymbolMap; } + set { SetProperty(proj.InputSymbolMap != value, val => proj.InputSymbolMap = val, value, "InputSymbolMap"); } + } + public string BaseDirectory { get { return proj.BaseDirectory; } set { SetProperty(proj.BaseDirectory != value, val => proj.BaseDirectory = val, value, "BaseDirectory"); } diff --git a/ConfuserEx/Views/ProjectTabAdvancedView.xaml b/ConfuserEx/Views/ProjectTabAdvancedView.xaml index bfb94b0a5..38e5b5413 100644 --- a/ConfuserEx/Views/ProjectTabAdvancedView.xaml +++ b/ConfuserEx/Views/ProjectTabAdvancedView.xaml @@ -12,15 +12,34 @@ + -