From 0ca1f8fe41ca09785936288cf3339f966d014af7 Mon Sep 17 00:00:00 2001 From: walon Date: Wed, 7 May 2025 19:39:09 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=20MetadataImporter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Editor/Emit/CompileContext.cs | 1 + .../BytesInitializeFromFieldRvaDataNode.cs | 1 + .../DataNodes/ConstFromFieldRvaDataNode.cs | 1 + Editor/Emit/EmitManager.cs | 69 +++++++++ Editor/Emit/MetadataImporter.cs | 131 +++++++++++------- Editor/Emit/VariableEncryption.cs | 18 +-- .../{Emit => Encryption}/RvaDataAllocator.cs | 30 ++-- .../CallObfus}/ProxyCallAllocator.cs | 14 +- .../ConstObfus/DefaultDataObfuscator.cs | 1 + Editor/Obfuscator.cs | 1 + Runtime/EncryptionService.cs | 8 +- 11 files changed, 194 insertions(+), 81 deletions(-) create mode 100644 Editor/Emit/EmitManager.cs rename Editor/{Emit => Encryption}/RvaDataAllocator.cs (91%) rename Editor/{Emit => ObfusPasses/CallObfus}/ProxyCallAllocator.cs (95%) diff --git a/Editor/Emit/CompileContext.cs b/Editor/Emit/CompileContext.cs index 5b2d790..be87221 100644 --- a/Editor/Emit/CompileContext.cs +++ b/Editor/Emit/CompileContext.cs @@ -2,6 +2,7 @@ using dnlib.DotNet.Emit; using NUnit.Framework; using Obfuz.Emit; +using Obfuz.Encryption; using System.Collections.Generic; namespace Obfuz.Emit diff --git a/Editor/Emit/DataNodes/BytesInitializeFromFieldRvaDataNode.cs b/Editor/Emit/DataNodes/BytesInitializeFromFieldRvaDataNode.cs index 472c532..d1e25a4 100644 --- a/Editor/Emit/DataNodes/BytesInitializeFromFieldRvaDataNode.cs +++ b/Editor/Emit/DataNodes/BytesInitializeFromFieldRvaDataNode.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading.Tasks; using static UnityEngine.Networking.UnityWebRequest; using UnityEngine.UIElements; +using Obfuz.Encryption; namespace Obfuz.Emit { diff --git a/Editor/Emit/DataNodes/ConstFromFieldRvaDataNode.cs b/Editor/Emit/DataNodes/ConstFromFieldRvaDataNode.cs index b1908ec..d1a40da 100644 --- a/Editor/Emit/DataNodes/ConstFromFieldRvaDataNode.cs +++ b/Editor/Emit/DataNodes/ConstFromFieldRvaDataNode.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading.Tasks; using static UnityEngine.Networking.UnityWebRequest; using UnityEngine.UIElements; +using Obfuz.Encryption; namespace Obfuz.Emit { diff --git a/Editor/Emit/EmitManager.cs b/Editor/Emit/EmitManager.cs new file mode 100644 index 0000000..278a0df --- /dev/null +++ b/Editor/Emit/EmitManager.cs @@ -0,0 +1,69 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Obfuz.Emit +{ + public interface IModuleEmitManager + { + void Init(ModuleDef mod); + } + + public abstract class ModuleEmitManagerBase : IModuleEmitManager + { + public abstract void Init(ModuleDef mod); + } + + public class EmitManager + { + public static EmitManager Ins { get; private set; } + + + private readonly Dictionary<(ModuleDef, Type), IModuleEmitManager> _moduleEmitManagers = new System.Collections.Generic.Dictionary<(ModuleDef, Type), IModuleEmitManager>(); + + public static void Reset() + { + Ins = new EmitManager(); + } + + public T GetEmitManager(ModuleDef mod, Func creator = null) where T : IModuleEmitManager + { + var key = (mod, typeof(T)); + if (_moduleEmitManagers.TryGetValue(key, out var emitManager)) + { + return (T)emitManager; + } + else + { + T newEmitManager; + if (creator != null) + { + newEmitManager = creator(mod); + } + else + { + newEmitManager = (T)Activator.CreateInstance(typeof(T)); + } + newEmitManager.Init(mod); + _moduleEmitManagers[key] = newEmitManager; + return newEmitManager; + } + } + + public List GetEmitManagers() where T: IModuleEmitManager + { + var managers = new List(); + foreach (var kv in _moduleEmitManagers) + { + if (kv.Key.Item2 == typeof(T)) + { + managers.Add((T)kv.Value); + } + } + return managers; + } + } +} diff --git a/Editor/Emit/MetadataImporter.cs b/Editor/Emit/MetadataImporter.cs index b1ef16a..ae0ee7f 100644 --- a/Editor/Emit/MetadataImporter.cs +++ b/Editor/Emit/MetadataImporter.cs @@ -8,64 +8,67 @@ using UnityEngine.Assertions; namespace Obfuz.Emit { - public class ModuleMetadataImporter + public interface IModuleMetadataImporter { - private readonly ModuleDef _module; - public ModuleMetadataImporter(ModuleDef module) - { - _module = module; - _module = module; - InitMetadatas(module); - } + void Init(ModuleDef mod); + } - private static IMethod s_castIntAsFloat; - private static IMethod s_castLongAsDouble; - private static IMethod s_castFloatAsInt; - private static IMethod s_castDoubleAsLong; + public abstract class ModuleMetadataImporterBase : IModuleMetadataImporter + { + public abstract void Init(ModuleDef mod); + } - private void InitMetadatas(ModuleDef mod) + public class DefaultModuleMetadataImporter : ModuleMetadataImporterBase + { + public override void Init(ModuleDef mod) { - if (s_castFloatAsInt != null) - { - return; - } + _module = mod; var constUtilityType = typeof(ConstUtility); - s_castIntAsFloat = mod.Import(constUtilityType.GetMethod("CastIntAsFloat")); - Assert.IsNotNull(s_castIntAsFloat, "CastIntAsFloat not found"); - s_castLongAsDouble = mod.Import(constUtilityType.GetMethod("CastLongAsDouble")); - Assert.IsNotNull(s_castLongAsDouble, "CastLongAsDouble not found"); - s_castFloatAsInt = mod.Import(constUtilityType.GetMethod("CastFloatAsInt")); - Assert.IsNotNull(s_castFloatAsInt, "CastFloatAsInt not found"); - s_castDoubleAsLong = mod.Import(constUtilityType.GetMethod("CastDoubleAsLong")); - Assert.IsNotNull(s_castDoubleAsLong, "CastDoubleAsLong not found"); + _castIntAsFloat = mod.Import(constUtilityType.GetMethod("CastIntAsFloat")); + Assert.IsNotNull(_castIntAsFloat, "CastIntAsFloat not found"); + _castLongAsDouble = mod.Import(constUtilityType.GetMethod("CastLongAsDouble")); + Assert.IsNotNull(_castLongAsDouble, "CastLongAsDouble not found"); + _castFloatAsInt = mod.Import(constUtilityType.GetMethod("CastFloatAsInt")); + Assert.IsNotNull(_castFloatAsInt, "CastFloatAsInt not found"); + _castDoubleAsLong = mod.Import(constUtilityType.GetMethod("CastDoubleAsLong")); + Assert.IsNotNull(_castDoubleAsLong, "CastDoubleAsLong not found"); + + _initializeArray = mod.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new[] { typeof(Array), typeof(RuntimeFieldHandle) })); + Assert.IsNotNull(_initializeArray); + _encryptBlock = mod.Import(typeof(EncryptionService).GetMethod("EncryptBlock", new[] { typeof(byte[]), typeof(long), typeof(int) })); + Assert.IsNotNull(_encryptBlock); + _decryptBlock = mod.Import(typeof(EncryptionService).GetMethod("DecryptBlock", new[] { typeof(byte[]), typeof(long), typeof(int) })); + Assert.IsNotNull(_decryptBlock); } - public IMethod GetCastIntAsFloat() - { - return s_castIntAsFloat; - } + private ModuleDef _module; + private IMethod _castIntAsFloat; + private IMethod _castLongAsDouble; + private IMethod _castFloatAsInt; + private IMethod _castDoubleAsLong; + private IMethod _initializeArray; + private IMethod _encryptBlock; + private IMethod _decryptBlock; - public IMethod GetCastLongAsDouble() - { - return s_castLongAsDouble; - } + public IMethod CastIntAsFloat => _castIntAsFloat; - public IMethod GetCastFloatAsInt() - { - return s_castFloatAsInt; - } + public IMethod CastLongAsDouble => _castLongAsDouble; - public IMethod GetCastDoubleAsLong() - { - return s_castDoubleAsLong; - } + public IMethod CastFloatAsInt => _castFloatAsInt; + + public IMethod CastDoubleAsLong => _castDoubleAsLong; + + public IMethod InitializedArrayMethod => _initializeArray; + + public IMethod EncryptBlock => _encryptBlock; + + public IMethod DecryptBlock => _decryptBlock; } public class MetadataImporter { - - private readonly Dictionary _moduleMetadataImporters = new Dictionary(); + private readonly Dictionary<(ModuleDef, Type), IModuleMetadataImporter> _customModuleMetadataImporters = new Dictionary<(ModuleDef, Type), IModuleMetadataImporter>(); public static MetadataImporter Instance { get; private set; } @@ -74,14 +77,46 @@ namespace Obfuz.Emit Instance = new MetadataImporter(); } - public ModuleMetadataImporter GetModuleMetadataImporter(ModuleDef module) + public DefaultModuleMetadataImporter GetDefaultModuleMetadataImporter(ModuleDef module) { - if (!_moduleMetadataImporters.TryGetValue(module, out var importer)) + return GetCustomModuleMetadataImporter(module); + } + + public List GetDefaultModuleMetadataImporters() + { + return GetCustomModuleMetadataImporters(); + } + + public T GetCustomModuleMetadataImporter(ModuleDef module, Func creator = null) where T : IModuleMetadataImporter + { + var key = (module, typeof(T)); + if (!_customModuleMetadataImporters.TryGetValue(key, out var importer)) { - importer = new ModuleMetadataImporter(module); - _moduleMetadataImporters[module] = importer; + if (creator != null) + { + importer = creator(module); + } + else + { + importer = (IModuleMetadataImporter)Activator.CreateInstance(typeof(T), module); + } + importer.Init(module); + _customModuleMetadataImporters[key] = importer; } - return importer; + return (T)importer; + } + + public List GetCustomModuleMetadataImporters() + { + var result = new List(); + foreach (var kvp in _customModuleMetadataImporters) + { + if (kvp.Key.Item2 == typeof(T)) + { + result.Add((T)kvp.Value); + } + } + return result; } } } diff --git a/Editor/Emit/VariableEncryption.cs b/Editor/Emit/VariableEncryption.cs index 7210ea2..791faba 100644 --- a/Editor/Emit/VariableEncryption.cs +++ b/Editor/Emit/VariableEncryption.cs @@ -13,6 +13,8 @@ namespace Obfuz.Emit public class EncryptionCompileContext { public ModuleDef module; + + public DefaultModuleMetadataImporter DefaultModuleMetadataImporter => MetadataImporter.Instance.GetDefaultModuleMetadataImporter(module); } public interface IVariableTransformer @@ -234,10 +236,10 @@ namespace Obfuz.Emit switch (_outputType) { case DataNodeType.Int32: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastFloatAsInt())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastFloatAsInt)); break; case DataNodeType.Int64: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastDoubleAsLong())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastDoubleAsLong)); break; default: throw new NotSupportedException($"Unsupported type: {_outputType}"); } @@ -248,10 +250,10 @@ namespace Obfuz.Emit switch (_outputType) { case DataNodeType.Int32: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastIntAsFloat())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastIntAsFloat)); break; case DataNodeType.Int64: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastLongAsDouble())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastLongAsDouble)); break; default: throw new NotSupportedException($"Unsupported type: {_outputType}"); } @@ -312,10 +314,10 @@ namespace Obfuz.Emit switch (_outputType) { case DataNodeType.Float32: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastIntAsFloat())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastIntAsFloat)); break; case DataNodeType.Float64: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastLongAsDouble())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastLongAsDouble)); break; default: throw new NotSupportedException($"Unsupported type: {_outputType}"); } @@ -326,10 +328,10 @@ namespace Obfuz.Emit switch (_outputType) { case DataNodeType.Float32: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastFloatAsInt())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastFloatAsInt)); break; case DataNodeType.Float64: - output.Add(Instruction.Create(OpCodes.Call, MetadataImporter.Instance.GetModuleMetadataImporter(ctx.module).GetCastDoubleAsLong())); + output.Add(Instruction.Create(OpCodes.Call, ctx.DefaultModuleMetadataImporter.CastDoubleAsLong)); break; default: throw new NotSupportedException($"Unsupported type: {_outputType}"); } diff --git a/Editor/Emit/RvaDataAllocator.cs b/Editor/Encryption/RvaDataAllocator.cs similarity index 91% rename from Editor/Emit/RvaDataAllocator.cs rename to Editor/Encryption/RvaDataAllocator.cs index 00fdf2a..da1959d 100644 --- a/Editor/Emit/RvaDataAllocator.cs +++ b/Editor/Encryption/RvaDataAllocator.cs @@ -1,5 +1,6 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; +using Obfuz.Emit; using Obfuz.Utils; using System; using System.Collections.Generic; @@ -8,7 +9,7 @@ using System.Text; using System.Threading.Tasks; using UnityEngine.Assertions; -namespace Obfuz.Emit +namespace Obfuz.Encryption { public struct RvaData { @@ -24,7 +25,7 @@ namespace Obfuz.Emit } } - public class ModuleRvaDataAllocator + public class ModuleRvaDataAllocator : ModuleEmitManagerBase { // randomized const int maxRvaDataSize = 0x100; @@ -33,7 +34,6 @@ namespace Obfuz.Emit private readonly IRandom _random; private readonly IEncryptor _encryptor; - class RvaField { public FieldDef holderDataField; @@ -76,6 +76,11 @@ namespace Obfuz.Emit _encryptor = encryptor; } + public override void Init(ModuleDef mod) + { + + } + private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType) { if (_rvaTypeDef == null) @@ -230,10 +235,7 @@ namespace Obfuz.Emit cctorMethod.Body = body; var ins = body.Instructions; - IMethod initializeArrayMethod = mod.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new[] { typeof(Array), typeof(RuntimeFieldHandle) })); - IMethod decryptArrayMethod = mod.Import(typeof(EncryptionService).GetMethod("DecryptBlock", new[] { typeof(byte[]), typeof(long), typeof(int) })); - - Assert.IsNotNull(initializeArrayMethod); + DefaultModuleMetadataImporter importer = MetadataImporter.Instance.GetDefaultModuleMetadataImporter(mod); foreach (var field in _rvaFields) { // ldc @@ -248,12 +250,12 @@ namespace Obfuz.Emit ins.Add(Instruction.Create(OpCodes.Dup)); ins.Add(Instruction.Create(OpCodes.Stsfld, field.runtimeValueField)); ins.Add(Instruction.Create(OpCodes.Ldtoken, field.holderDataField)); - ins.Add(Instruction.Create(OpCodes.Call, initializeArrayMethod)); + ins.Add(Instruction.Create(OpCodes.Call, importer.InitializedArrayMethod)); // EncryptionService.DecryptBlock(array, field.encryptionOps, field.salt); ins.Add(Instruction.Create(OpCodes.Ldc_I8, field.encryptionOps)); ins.Add(Instruction.Create(OpCodes.Ldc_I4, field.salt)); - ins.Add(Instruction.Create(OpCodes.Call, decryptArrayMethod)); + ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptBlock)); } ins.Add(Instruction.Create(OpCodes.Ret)); @@ -286,7 +288,6 @@ namespace Obfuz.Emit private readonly IRandom _random; private readonly IEncryptor _encryptor; - private readonly Dictionary _modules = new Dictionary(); public RvaDataAllocator(IRandom random, IEncryptor encryptor) { @@ -296,12 +297,7 @@ namespace Obfuz.Emit private ModuleRvaDataAllocator GetModuleRvaDataAllocator(ModuleDef mod) { - if (!_modules.TryGetValue(mod, out var allocator)) - { - allocator = new ModuleRvaDataAllocator(mod, _random, _encryptor); - _modules.Add(mod, allocator); - } - return allocator; + return EmitManager.Ins.GetEmitManager(mod, mod => new ModuleRvaDataAllocator(mod, _random, _encryptor)); } public RvaData Allocate(ModuleDef mod, int value) @@ -336,7 +332,7 @@ namespace Obfuz.Emit public void Done() { - foreach (var allocator in _modules.Values) + foreach (var allocator in EmitManager.Ins.GetEmitManagers()) { allocator.Done(); } diff --git a/Editor/Emit/ProxyCallAllocator.cs b/Editor/ObfusPasses/CallObfus/ProxyCallAllocator.cs similarity index 95% rename from Editor/Emit/ProxyCallAllocator.cs rename to Editor/ObfusPasses/CallObfus/ProxyCallAllocator.cs index da48a7a..ca09e1f 100644 --- a/Editor/Emit/ProxyCallAllocator.cs +++ b/Editor/ObfusPasses/CallObfus/ProxyCallAllocator.cs @@ -9,12 +9,18 @@ using System.Threading.Tasks; using MethodImplAttributes = dnlib.DotNet.MethodImplAttributes; using TypeAttributes = dnlib.DotNet.TypeAttributes; -namespace Obfuz.Emit +namespace Obfuz.ObfusPasses.CallObfus { public struct ProxyCallMethodData { - public MethodDef proxyMethod; - public int secret; + public readonly MethodDef proxyMethod; + public readonly int secret; + + public ProxyCallMethodData(MethodDef proxyMethod, int secret) + { + this.proxyMethod = proxyMethod; + this.secret = secret; + } } class ModuleDynamicProxyMethodAllocator @@ -161,7 +167,7 @@ namespace Obfuz.Emit methodDispatcher.methods.Add(new CallInfo { method = method, callVir = callVir}); _methodProxys.Add(key, proxyInfo); } - return new ProxyCallMethodData { proxyMethod = proxyInfo.proxyMethod, secret = proxyInfo.secret }; + return new ProxyCallMethodData(proxyInfo.proxyMethod, proxyInfo.secret); } public void Done() diff --git a/Editor/ObfusPasses/ConstObfus/DefaultDataObfuscator.cs b/Editor/ObfusPasses/ConstObfus/DefaultDataObfuscator.cs index 92b45e6..5b7a208 100644 --- a/Editor/ObfusPasses/ConstObfus/DefaultDataObfuscator.cs +++ b/Editor/ObfusPasses/ConstObfus/DefaultDataObfuscator.cs @@ -1,6 +1,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using Obfuz.Emit; +using Obfuz.Encryption; using Obfuz.Utils; using System; using System.Collections.Generic; diff --git a/Editor/Obfuscator.cs b/Editor/Obfuscator.cs index cac03f8..511c07e 100644 --- a/Editor/Obfuscator.cs +++ b/Editor/Obfuscator.cs @@ -38,6 +38,7 @@ namespace Obfuz _obfuscatedAssemblyOutputDir = obfuscatedAssemblyOutputDir; MetadataImporter.Reset(); + EmitManager.Reset(); _assemblyCache = new AssemblyCache(new PathAssemblyResolver(assemblySearchDirs.ToArray())); foreach (var pass in obfuscationPasses) { diff --git a/Runtime/EncryptionService.cs b/Runtime/EncryptionService.cs index 088cccc..a0167e4 100644 --- a/Runtime/EncryptionService.cs +++ b/Runtime/EncryptionService.cs @@ -10,14 +10,14 @@ namespace Obfuz { private static readonly IEncryptor _encryptor = new DefaultEncryptor(new byte[] { 0x1A, 0x2B, 0x3C, 0x4D }); - public static void EncryptBlock(byte[] data, int salt) + public static void EncryptBlock(byte[] data, long ops, int salt) { - _encryptor.EncryptBlock(data, salt); + _encryptor.EncryptBlock(data, ops, salt); } - public static void DecryptBlock(byte[] data, int salt) + public static void DecryptBlock(byte[] data, long ops, int salt) { - _encryptor.DecryptBlock(data, salt); + _encryptor.DecryptBlock(data, ops, salt); } } }