diff --git a/Editor/GarbageCodeGeneration.meta b/Editor/GarbageCodeGeneration.meta new file mode 100644 index 0000000..29fa133 --- /dev/null +++ b/Editor/GarbageCodeGeneration.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f47f2abd9eb7ba8469ba5cb1bb085d33 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/GarbageCodeGeneration/ConfigGarbageCodeGenerator.cs b/Editor/GarbageCodeGeneration/ConfigGarbageCodeGenerator.cs new file mode 100644 index 0000000..5d0eb57 --- /dev/null +++ b/Editor/GarbageCodeGeneration/ConfigGarbageCodeGenerator.cs @@ -0,0 +1,193 @@ +using dnlib.DotNet; +using NUnit.Framework; +using Obfuz.Settings; +using Obfuz.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using UnityEngine; + +namespace Obfuz.GarbageCodeGeneration +{ + public abstract class SpecificGarbageCodeGeneratorBase : ISpecificGarbageCodeGenerator + { + protected interface IClassGenerationInfo + { + string Namespace { get; set; } + + string Name { get; set; } + + IList Fields { get; set; } + + IList Methods { get; set; } + } + + protected class ClassGenerationInfo : IClassGenerationInfo + { + public string Namespace { get; set; } + public string Name { get; set; } + public IList Fields { get; set; } = new List(); + public IList Methods { get; set; } = new List(); + } + + public virtual void Generate(GenerationParameters parameters) + { + FileUtil.RecreateDir(parameters.outputPath); + + for (int i = 0; i < parameters.classCount; i++) + { + Debug.Log($"[{GetType().Name}] Generating class {i}"); + var localRandom = new RandomWithKey(((RandomWithKey)parameters.random).Key, parameters.random.NextInt()); + string outputFile = $"{parameters.outputPath}/__GeneratedGarbageClass_{i}.cs"; + var result = new StringBuilder(64 * 1024); + GenerateClass(i, localRandom, result, parameters); + File.WriteAllText(outputFile, result.ToString(), Encoding.UTF8); + Debug.Log($"[{GetType().Name}] Generated class {i} to {outputFile}"); + } + } + + protected virtual void GenerateClass(int classIndex, IRandom random, StringBuilder result, GenerationParameters parameters) + { + IClassGenerationInfo cgi = CreateClassGenerationInfo(parameters.classNamespace, $"{parameters.classNamePrefix}{classIndex}", random, parameters); + result.AppendLine("using System;"); + result.AppendLine("using System.Collections.Generic;"); + result.AppendLine("using System.Linq;"); + result.AppendLine("using System.IO;"); + result.AppendLine("using UnityEngine;"); + + GenerateUsings(result, cgi); + + result.AppendLine($"namespace {cgi.Namespace}"); + result.AppendLine("{"); + result.AppendLine($" public class {cgi.Name}"); + result.AppendLine(" {"); + + string indent = " "; + foreach (object field in cgi.Fields) + { + GenerateField(result, cgi, field, indent); + } + foreach (object method in cgi.Methods) + { + GenerateMethod(result, cgi, method, indent); + } + result.AppendLine(" }"); + result.AppendLine("}"); + } + + protected abstract IClassGenerationInfo CreateClassGenerationInfo(string classNamespace, string className, IRandom random, GenerationParameters parameters); + + protected abstract void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi); + + protected abstract void GenerateField(StringBuilder result, IClassGenerationInfo cgi, object field, string indent); + + protected abstract void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, object method, string indent); + } + + public class ConfigGarbageCodeGenerator : SpecificGarbageCodeGeneratorBase + { + class FieldGenerationInfo + { + public int index; + public string name; + public string type; + } + + class MethodGenerationInfo + { + public int index; + public string name; + } + + + protected override IClassGenerationInfo CreateClassGenerationInfo(string classNamespace, string className, IRandom random, GenerationParameters parameters) + { + var cgi = new ClassGenerationInfo + { + Namespace = classNamespace, + Name = className, + }; + + for (int i = 0; i < parameters.fieldCountPerClass; i++) + { + var fieldInfo = new FieldGenerationInfo + { + index = i, + name = $"x{i}", + type = CreateRandomType(random), + }; + cgi.Fields.Add(fieldInfo); + } + + for (int i = 0; i < parameters.methodCountPerClass; i++) + { + var methodInfo = new MethodGenerationInfo + { + index = i, + name = $"Load{i}", + }; + cgi.Methods.Add(methodInfo); + } + + return cgi; + } + + private readonly string[] _types = new string[] + { + "bool", + "byte", + "short", + "int", + "long", + "float", + "double", + }; + + private string CreateRandomType(IRandom random) + { + return _types[random.NextInt(_types.Length)]; + } + + + protected override void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi) + { + } + + protected override void GenerateField(StringBuilder result, IClassGenerationInfo cgi, object field, string indent) + { + var fgi = (FieldGenerationInfo)field; + result.AppendLine($"{indent}public {fgi.type} {fgi.name};"); + } + + private string GetReadMethodNameOfType(string type) + { + switch (type) + { + case "bool": return "ReadBoolean"; + case "byte": return "ReadByte"; + case "short": return "ReadInt16"; + case "int": return "ReadInt32"; + case "long": return "ReadInt64"; + case "float": return "ReadSingle"; + case "double": return "ReadDouble"; + default: throw new ArgumentException($"Unsupported type: {type}"); + } + } + + protected override void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, object method, string indent) + { + result.AppendLine($"{indent}void Load(BinaryReader reader)"); + result.AppendLine($"{indent}{{"); + + string indent2 = indent + " "; + foreach (FieldGenerationInfo fgi in cgi.Fields) + { + result.AppendLine($"{indent2}this.{fgi.name} = reader.{GetReadMethodNameOfType(fgi.type)}();"); + } + + result.AppendLine($"{indent}}}"); + } + } +} diff --git a/Editor/GarbageCodeGeneration/ConfigGarbageCodeGenerator.cs.meta b/Editor/GarbageCodeGeneration/ConfigGarbageCodeGenerator.cs.meta new file mode 100644 index 0000000..c47c040 --- /dev/null +++ b/Editor/GarbageCodeGeneration/ConfigGarbageCodeGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 327cb4a465ff23944a5fea30bf3beeeb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/GarbageCodeGeneration/GarbageCodeGenerator.cs b/Editor/GarbageCodeGeneration/GarbageCodeGenerator.cs new file mode 100644 index 0000000..de1944f --- /dev/null +++ b/Editor/GarbageCodeGeneration/GarbageCodeGenerator.cs @@ -0,0 +1,73 @@ +using Obfuz.Settings; +using Obfuz.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace Obfuz.GarbageCodeGeneration +{ + + public class GarbageCodeGenerator + { + private const int CodeGenerationSecretKeyLength = 1024; + + private readonly GarbageCodeGeneratorSettings _settings; + private readonly int[] _intGenerationSecretKey; + + public GarbageCodeGenerator(GarbageCodeGeneratorSettings settings) + { + _settings = settings; + + byte[] byteGenerationSecretKey = KeyGenerator.GenerateKey(settings.codeGenerationSecret, CodeGenerationSecretKeyLength); + _intGenerationSecretKey = KeyGenerator.ConvertToIntKey(byteGenerationSecretKey); + } + + public void Generate() + { + GenerateTask(_settings.defaultTask); + if (_settings.additionalTasks != null && _settings.additionalTasks.Length > 0) + { + foreach (var task in _settings.additionalTasks) + { + GenerateTask(task); + } + } + } + + private void GenerateTask(GarbageCodeGenerationTask task) + { + Debug.Log($"Generating garbage code with seed: {task.codeGenerationRandomSeed}, class count: {task.classCount}, method count per class: {task.methodCountPerClass}, types: {task.garbageCodeTypes}, output path: {task.outputPath}"); + + var generator = CreateSpecificCodeGenerator(task.garbageCodeTypes); + + var parameters = new GenerationParameters + { + random = new RandomWithKey(_intGenerationSecretKey, task.codeGenerationRandomSeed), + classNamespace = task.classNamespace, + classNamePrefix = task.classNamePrefix, + classCount = task.classCount, + methodCountPerClass = task.methodCountPerClass, + fieldCountPerClass = task.fieldCountPerClass, + outputPath = task.outputPath + }; + generator.Generate(parameters); + + Debug.Log($"Generate garbage code end."); + } + + private ISpecificGarbageCodeGenerator CreateSpecificCodeGenerator(GarbageCodeType type) + { + switch (type) + { + case GarbageCodeType.Config: + return new ConfigGarbageCodeGenerator(); + // Add cases for other types as needed + default: + throw new NotSupportedException($"Garbage code type {type} is not supported."); + } + } + } +} diff --git a/Editor/GarbageCodeGeneration/GarbageCodeGenerator.cs.meta b/Editor/GarbageCodeGeneration/GarbageCodeGenerator.cs.meta new file mode 100644 index 0000000..4939c45 --- /dev/null +++ b/Editor/GarbageCodeGeneration/GarbageCodeGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff64fd1e6f7b8874db5a5228fab159f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/GarbageCodeGeneration/ISpecificGarbageCodeGenerator.cs b/Editor/GarbageCodeGeneration/ISpecificGarbageCodeGenerator.cs new file mode 100644 index 0000000..9deda5c --- /dev/null +++ b/Editor/GarbageCodeGeneration/ISpecificGarbageCodeGenerator.cs @@ -0,0 +1,22 @@ +using Obfuz.Settings; +using Obfuz.Utils; + +namespace Obfuz.GarbageCodeGeneration +{ + public class GenerationParameters + { + public IRandom random; + + public string classNamespace; + public string classNamePrefix; + public int classCount; + public int methodCountPerClass; + public int fieldCountPerClass; + public string outputPath; + } + + public interface ISpecificGarbageCodeGenerator + { + void Generate(GenerationParameters parameters); + } +} diff --git a/Editor/GarbageCodeGeneration/ISpecificGarbageCodeGenerator.cs.meta b/Editor/GarbageCodeGeneration/ISpecificGarbageCodeGenerator.cs.meta new file mode 100644 index 0000000..9cb265c --- /dev/null +++ b/Editor/GarbageCodeGeneration/ISpecificGarbageCodeGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 74a17802b5aab2e40a3c89e0ddbcec0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Settings/AssemblySettings.cs b/Editor/Settings/AssemblySettings.cs index 9792351..fb0c6b6 100644 --- a/Editor/Settings/AssemblySettings.cs +++ b/Editor/Settings/AssemblySettings.cs @@ -5,6 +5,7 @@ using UnityEngine; namespace Obfuz.Settings { + [Serializable] public class AssemblySettings { diff --git a/Editor/Settings/GarbageCodeGeneratorSettings.cs b/Editor/Settings/GarbageCodeGeneratorSettings.cs new file mode 100644 index 0000000..a8194fb --- /dev/null +++ b/Editor/Settings/GarbageCodeGeneratorSettings.cs @@ -0,0 +1,41 @@ +using System; + +namespace Obfuz.Settings +{ + public enum GarbageCodeType + { + None, + Config, + UI, + } + + [Serializable] + public class GarbageCodeGenerationTask + { + public int codeGenerationRandomSeed; + + public string classNamespace = "__GarbageCode"; + + public string classNamePrefix = "__GeneratedGarbageClass"; + + public int classCount = 100; + + public int methodCountPerClass = 30; + + public int fieldCountPerClass = 50; + + public GarbageCodeType garbageCodeTypes = GarbageCodeType.Config; + + public string outputPath = "Assets/Obfuz/GarbageCode"; + } + + [Serializable] + public class GarbageCodeGeneratorSettings + { + public string codeGenerationSecret = "Garbage Code"; + + public GarbageCodeGenerationTask defaultTask; + + public GarbageCodeGenerationTask[] additionalTasks; + } +} diff --git a/Editor/Settings/GarbageCodeGeneratorSettings.cs.meta b/Editor/Settings/GarbageCodeGeneratorSettings.cs.meta new file mode 100644 index 0000000..326444e --- /dev/null +++ b/Editor/Settings/GarbageCodeGeneratorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 220b44e477cfa2848bd287c38db4fd21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Settings/ObfuzSettings.cs b/Editor/Settings/ObfuzSettings.cs index a58a0e2..491d225 100644 --- a/Editor/Settings/ObfuzSettings.cs +++ b/Editor/Settings/ObfuzSettings.cs @@ -43,6 +43,9 @@ namespace Obfuz.Settings [Tooltip("control flow obfuscation settings")] public ControlFlowObfuscationSettings controlFlowObfuscationSettings; + [Tooltip("garbage code generator settings")] + public GarbageCodeGeneratorSettings garbageCodeGeneratorSettings; + public string ObfuzRootDir => $"Library/Obfuz"; public string GetObfuscatedAssemblyOutputPath(BuildTarget target) diff --git a/Editor/Settings/ObfuzSettingsProvider.cs b/Editor/Settings/ObfuzSettingsProvider.cs index ba6eede..8dbf04c 100644 --- a/Editor/Settings/ObfuzSettingsProvider.cs +++ b/Editor/Settings/ObfuzSettingsProvider.cs @@ -38,6 +38,8 @@ namespace Obfuz.Settings private SerializedProperty _exprObfusSettings; private SerializedProperty _controlFlowObfusSettings; + private SerializedProperty _garbageCodeGeneratorSettings; + public ObfuzSettingsProvider() : base("Project/Obfuz", SettingsScope.Project) { } @@ -72,6 +74,8 @@ namespace Obfuz.Settings _fieldEncryptSettings = _serializedObject.FindProperty("fieldEncryptSettings"); _callObfusSettings = _serializedObject.FindProperty("callObfusSettings"); _controlFlowObfusSettings = _serializedObject.FindProperty("controlFlowObfuscationSettings"); + + _garbageCodeGeneratorSettings = _serializedObject.FindProperty("garbageCodeGeneratorSettings"); } public override void OnGUI(string searchContext) @@ -98,6 +102,7 @@ namespace Obfuz.Settings EditorGUILayout.PropertyField(_callObfusSettings); EditorGUILayout.PropertyField(_controlFlowObfusSettings); + EditorGUILayout.PropertyField(_garbageCodeGeneratorSettings); if (EditorGUI.EndChangeCheck()) { diff --git a/Editor/Unity/ObfuzMenu.cs b/Editor/Unity/ObfuzMenu.cs index fc578df..17b32ea 100644 --- a/Editor/Unity/ObfuzMenu.cs +++ b/Editor/Unity/ObfuzMenu.cs @@ -1,4 +1,5 @@ using Obfuz.EncryptionVM; +using Obfuz.GarbageCodeGeneration; using Obfuz.Settings; using Obfuz.Utils; using System.IO; @@ -36,6 +37,16 @@ namespace Obfuz.Unity AssetDatabase.Refresh(); } + [MenuItem("Obfuz/GenerateGarbageCodes", priority = 100)] + public static void GenerateGarbageCodes() + { + Debug.Log($"Generating garbage codes begin."); + GarbageCodeGeneratorSettings settings = ObfuzSettings.Instance.garbageCodeGeneratorSettings; + var generator = new GarbageCodeGenerator(settings); + generator.Generate(); + Debug.Log($"Generating garbage codes end."); + } + private static void SaveKey(byte[] secret, string secretOutputPath) { FileUtil.CreateParentDir(secretOutputPath); diff --git a/Editor/Utils/RandomWithKey.cs b/Editor/Utils/RandomWithKey.cs index e7ae157..e66bfc6 100644 --- a/Editor/Utils/RandomWithKey.cs +++ b/Editor/Utils/RandomWithKey.cs @@ -18,6 +18,8 @@ _seed = seed; } + public int[] Key => _key; + public int NextInt(int min, int max) { return min + NextInt(max - min);