From dcf38b3f0feadfa748c6f71cd2fe9f724db6d860 Mon Sep 17 00:00:00 2001 From: Gabriel Olivato Date: Wed, 19 Aug 2026 17:53:01 +0100 Subject: [PATCH] Add symbolObfusSettings.nameRandomSeed to randomise generated symbol names Upstream assigns obfuscated names from a monotonic base-52 counter, so two builds of the same source produce an identical mapping and a build with a small source change keeps almost every previous name. An attacker who reverses build N therefore carries that work forward to build N+1. nameRandomSeed = 0 keeps the original counter, so this is opt-in and the default behaviour is unchanged. --- .../NameMakers/NameMakerFactory.cs | 9 ++++++-- .../SymbolObfus/NameMakers/NameScope.cs | 22 ++++++++++++++++--- .../NameMakers/WordSetNameMaker.cs | 20 ++++++++++++++--- .../ObfusPasses/SymbolObfus/SymbolRename.cs | 4 ++-- Editor/Settings/SymbolObfuscationSettings.cs | 7 +++++- 5 files changed, 51 insertions(+), 11 deletions(-) diff --git a/Editor/ObfusPasses/SymbolObfus/NameMakers/NameMakerFactory.cs b/Editor/ObfusPasses/SymbolObfus/NameMakers/NameMakerFactory.cs index 2b631e4..a57731c 100644 --- a/Editor/ObfusPasses/SymbolObfus/NameMakers/NameMakerFactory.cs +++ b/Editor/ObfusPasses/SymbolObfus/NameMakers/NameMakerFactory.cs @@ -1,4 +1,4 @@ -// Copyright 2025 Code Philosophy +// Copyright 2025 Code Philosophy // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -30,6 +30,11 @@ namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers } public static INameMaker CreateNameMakerBaseASCIICharSet(string namePrefix) + { + return CreateNameMakerBaseASCIICharSet(namePrefix, 0); + } + + public static INameMaker CreateNameMakerBaseASCIICharSet(string namePrefix, int seed) { var words = new List(); for (int i = 0; i < 26; i++) @@ -37,7 +42,7 @@ namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers words.Add(((char)('a' + i)).ToString()); words.Add(((char)('A' + i)).ToString()); } - return new WordSetNameMaker(namePrefix, words); + return new WordSetNameMaker(namePrefix, words, seed); } } } diff --git a/Editor/ObfusPasses/SymbolObfus/NameMakers/NameScope.cs b/Editor/ObfusPasses/SymbolObfus/NameMakers/NameScope.cs index 2227f71..c31d7f1 100644 --- a/Editor/ObfusPasses/SymbolObfus/NameMakers/NameScope.cs +++ b/Editor/ObfusPasses/SymbolObfus/NameMakers/NameScope.cs @@ -1,4 +1,4 @@ -// Copyright 2025 Code Philosophy +// Copyright 2025 Code Philosophy // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,19 +28,35 @@ namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers { private readonly string _namePrefix; private readonly List _wordSet; + private readonly System.Random _random; + private readonly int _nameSpace; private int _nextIndex; - public NameScope(string namePrefix, List wordSet) + public NameScope(string namePrefix, List wordSet) : this(namePrefix, wordSet, 0) + { + } + + // A zero seed keeps the original deterministic base-N counter. Any other seed draws + // names at random from a space wide enough that collisions are rare; NameScopeBase + // retries on collision, so correctness does not depend on the space being large. + public NameScope(string namePrefix, List wordSet, int seed) { _namePrefix = namePrefix; _wordSet = wordSet; _nextIndex = 0; + _random = seed != 0 ? new System.Random(seed) : null; + int space = 1; + for (int i = 0; i < 4; i++) + { + space *= wordSet.Count; + } + _nameSpace = space; } protected override void BuildNewName(StringBuilder nameBuilder, string originalName, string lastName) { nameBuilder.Append(_namePrefix); - for (int i = _nextIndex++; ;) + for (int i = _random != null ? _random.Next(_nameSpace) : _nextIndex++; ;) { nameBuilder.Append(_wordSet[i % _wordSet.Count]); i = i / _wordSet.Count; diff --git a/Editor/ObfusPasses/SymbolObfus/NameMakers/WordSetNameMaker.cs b/Editor/ObfusPasses/SymbolObfus/NameMakers/WordSetNameMaker.cs index f44a08d..e56f92c 100644 --- a/Editor/ObfusPasses/SymbolObfus/NameMakers/WordSetNameMaker.cs +++ b/Editor/ObfusPasses/SymbolObfus/NameMakers/WordSetNameMaker.cs @@ -1,4 +1,4 @@ -// Copyright 2025 Code Philosophy +// Copyright 2025 Code Philosophy // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,16 +27,30 @@ namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers { private readonly string _namePrefix; private readonly List _wordSet; + private readonly int _seed; + private int _scopeIndex; - public WordSetNameMaker(string namePrefix, List wordSet) + public WordSetNameMaker(string namePrefix, List wordSet) : this(namePrefix, wordSet, 0) + { + } + + public WordSetNameMaker(string namePrefix, List wordSet, int seed) { _namePrefix = namePrefix; _wordSet = wordSet; + _seed = seed; } protected override INameScope CreateNameScope() { - return new NameScope(_namePrefix, _wordSet); + if (_seed == 0) + { + return new NameScope(_namePrefix, _wordSet); + } + + // Distinct per scope, otherwise every scope would emit the same sequence. + int scopeSeed = unchecked(_seed + _scopeIndex++ * (int)0x9E3779B9); + return new NameScope(_namePrefix, _wordSet, scopeSeed != 0 ? scopeSeed : 1); } } } diff --git a/Editor/ObfusPasses/SymbolObfus/SymbolRename.cs b/Editor/ObfusPasses/SymbolObfus/SymbolRename.cs index 3f1ecb4..3fa9bfa 100644 --- a/Editor/ObfusPasses/SymbolObfus/SymbolRename.cs +++ b/Editor/ObfusPasses/SymbolObfus/SymbolRename.cs @@ -1,4 +1,4 @@ -// Copyright 2025 Code Philosophy +// Copyright 2025 Code Philosophy // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,7 @@ namespace Obfuz.ObfusPasses.SymbolObfus _obfuscationRuleFiles = settings.ruleFiles.ToList(); _renameRecordMap = new RenameRecordMap(settings.symbolMappingFile, settings.debug, settings.keepUnknownSymbolInSymbolMappingFile); _virtualMethodGroupCalculator = new VirtualMethodGroupCalculator(); - _nameMaker = settings.debug ? NameMakerFactory.CreateDebugNameMaker() : NameMakerFactory.CreateNameMakerBaseASCIICharSet(settings.obfuscatedNamePrefix); + _nameMaker = settings.debug ? NameMakerFactory.CreateDebugNameMaker() : NameMakerFactory.CreateNameMakerBaseASCIICharSet(settings.obfuscatedNamePrefix, settings.nameRandomSeed); _customPolicyTypes = settings.customRenamePolicyTypes; } diff --git a/Editor/Settings/SymbolObfuscationSettings.cs b/Editor/Settings/SymbolObfuscationSettings.cs index e2c7690..59d0e45 100644 --- a/Editor/Settings/SymbolObfuscationSettings.cs +++ b/Editor/Settings/SymbolObfuscationSettings.cs @@ -1,4 +1,4 @@ -// Copyright 2025 Code Philosophy +// Copyright 2025 Code Philosophy // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -34,6 +34,7 @@ namespace Obfuz.Settings public bool detectReflectionCompatibility; public bool keepUnknownSymbolInSymbolMappingFile; public string symbolMappingFile; + public int nameRandomSeed; public List ruleFiles; public List customRenamePolicyTypes; } @@ -58,6 +59,9 @@ namespace Obfuz.Settings [Tooltip("symbol mapping file path")] public string symbolMappingFile = "Assets/Obfuz/SymbolObfus/symbol-mapping.xml"; + [Tooltip("random seed for generated names. 0 keeps the original deterministic sequence; any other value randomises which name each symbol gets, so the mapping differs between builds")] + public int nameRandomSeed = 0; + [Tooltip("debug symbol mapping file path, used for debugging purposes")] public string debugSymbolMappingFile = "Assets/Obfuz/SymbolObfus/symbol-mapping-debug.xml"; @@ -82,6 +86,7 @@ namespace Obfuz.Settings detectReflectionCompatibility = detectReflectionCompatibility, keepUnknownSymbolInSymbolMappingFile = keepUnknownSymbolInSymbolMappingFile, symbolMappingFile = GetSymbolMappingFile(), + nameRandomSeed = nameRandomSeed, ruleFiles = ruleFiles?.ToList() ?? new List(), customRenamePolicyTypes = customRenamePolicyTypes?.Select(typeName => ReflectionUtil.FindUniqueTypeInCurrentAppDomain(typeName)).ToList() ?? new List(), };