重构 MetadataImporter

backup
walon 2025-05-07 19:39:09 +08:00
parent 3ee44663fb
commit 0ca1f8fe41
11 changed files with 194 additions and 81 deletions

View File

@ -2,6 +2,7 @@
using dnlib.DotNet.Emit; using dnlib.DotNet.Emit;
using NUnit.Framework; using NUnit.Framework;
using Obfuz.Emit; using Obfuz.Emit;
using Obfuz.Encryption;
using System.Collections.Generic; using System.Collections.Generic;
namespace Obfuz.Emit namespace Obfuz.Emit

View File

@ -9,6 +9,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using static UnityEngine.Networking.UnityWebRequest; using static UnityEngine.Networking.UnityWebRequest;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using Obfuz.Encryption;
namespace Obfuz.Emit namespace Obfuz.Emit
{ {

View File

@ -9,6 +9,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using static UnityEngine.Networking.UnityWebRequest; using static UnityEngine.Networking.UnityWebRequest;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using Obfuz.Encryption;
namespace Obfuz.Emit namespace Obfuz.Emit
{ {

View File

@ -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<T>(ModuleDef mod, Func<ModuleDef, T> 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<T> GetEmitManagers<T>() where T: IModuleEmitManager
{
var managers = new List<T>();
foreach (var kv in _moduleEmitManagers)
{
if (kv.Key.Item2 == typeof(T))
{
managers.Add((T)kv.Value);
}
}
return managers;
}
}
}

View File

@ -8,64 +8,67 @@ using UnityEngine.Assertions;
namespace Obfuz.Emit namespace Obfuz.Emit
{ {
public class ModuleMetadataImporter public interface IModuleMetadataImporter
{ {
private readonly ModuleDef _module; void Init(ModuleDef mod);
public ModuleMetadataImporter(ModuleDef module) }
{
_module = module;
_module = module;
InitMetadatas(module);
}
private static IMethod s_castIntAsFloat; public abstract class ModuleMetadataImporterBase : IModuleMetadataImporter
private static IMethod s_castLongAsDouble; {
private static IMethod s_castFloatAsInt; public abstract void Init(ModuleDef mod);
private static IMethod s_castDoubleAsLong; }
private void InitMetadatas(ModuleDef mod) public class DefaultModuleMetadataImporter : ModuleMetadataImporterBase
{
public override void Init(ModuleDef mod)
{ {
if (s_castFloatAsInt != null) _module = mod;
{
return;
}
var constUtilityType = typeof(ConstUtility); var constUtilityType = typeof(ConstUtility);
s_castIntAsFloat = mod.Import(constUtilityType.GetMethod("CastIntAsFloat")); _castIntAsFloat = mod.Import(constUtilityType.GetMethod("CastIntAsFloat"));
Assert.IsNotNull(s_castIntAsFloat, "CastIntAsFloat not found"); Assert.IsNotNull(_castIntAsFloat, "CastIntAsFloat not found");
s_castLongAsDouble = mod.Import(constUtilityType.GetMethod("CastLongAsDouble")); _castLongAsDouble = mod.Import(constUtilityType.GetMethod("CastLongAsDouble"));
Assert.IsNotNull(s_castLongAsDouble, "CastLongAsDouble not found"); Assert.IsNotNull(_castLongAsDouble, "CastLongAsDouble not found");
s_castFloatAsInt = mod.Import(constUtilityType.GetMethod("CastFloatAsInt")); _castFloatAsInt = mod.Import(constUtilityType.GetMethod("CastFloatAsInt"));
Assert.IsNotNull(s_castFloatAsInt, "CastFloatAsInt not found"); Assert.IsNotNull(_castFloatAsInt, "CastFloatAsInt not found");
s_castDoubleAsLong = mod.Import(constUtilityType.GetMethod("CastDoubleAsLong")); _castDoubleAsLong = mod.Import(constUtilityType.GetMethod("CastDoubleAsLong"));
Assert.IsNotNull(s_castDoubleAsLong, "CastDoubleAsLong not found"); 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() private ModuleDef _module;
{ private IMethod _castIntAsFloat;
return s_castIntAsFloat; private IMethod _castLongAsDouble;
} private IMethod _castFloatAsInt;
private IMethod _castDoubleAsLong;
private IMethod _initializeArray;
private IMethod _encryptBlock;
private IMethod _decryptBlock;
public IMethod GetCastLongAsDouble() public IMethod CastIntAsFloat => _castIntAsFloat;
{
return s_castLongAsDouble;
}
public IMethod GetCastFloatAsInt() public IMethod CastLongAsDouble => _castLongAsDouble;
{
return s_castFloatAsInt;
}
public IMethod GetCastDoubleAsLong() public IMethod CastFloatAsInt => _castFloatAsInt;
{
return s_castDoubleAsLong; public IMethod CastDoubleAsLong => _castDoubleAsLong;
}
public IMethod InitializedArrayMethod => _initializeArray;
public IMethod EncryptBlock => _encryptBlock;
public IMethod DecryptBlock => _decryptBlock;
} }
public class MetadataImporter public class MetadataImporter
{ {
private readonly Dictionary<(ModuleDef, Type), IModuleMetadataImporter> _customModuleMetadataImporters = new Dictionary<(ModuleDef, Type), IModuleMetadataImporter>();
private readonly Dictionary<ModuleDef, ModuleMetadataImporter> _moduleMetadataImporters = new Dictionary<ModuleDef, ModuleMetadataImporter>();
public static MetadataImporter Instance { get; private set; } public static MetadataImporter Instance { get; private set; }
@ -74,14 +77,46 @@ namespace Obfuz.Emit
Instance = new MetadataImporter(); Instance = new MetadataImporter();
} }
public ModuleMetadataImporter GetModuleMetadataImporter(ModuleDef module) public DefaultModuleMetadataImporter GetDefaultModuleMetadataImporter(ModuleDef module)
{ {
if (!_moduleMetadataImporters.TryGetValue(module, out var importer)) return GetCustomModuleMetadataImporter<DefaultModuleMetadataImporter>(module);
}
public List<DefaultModuleMetadataImporter> GetDefaultModuleMetadataImporters()
{
return GetCustomModuleMetadataImporters<DefaultModuleMetadataImporter>();
}
public T GetCustomModuleMetadataImporter<T>(ModuleDef module, Func<ModuleDef, T> creator = null) where T : IModuleMetadataImporter
{
var key = (module, typeof(T));
if (!_customModuleMetadataImporters.TryGetValue(key, out var importer))
{ {
importer = new ModuleMetadataImporter(module); if (creator != null)
_moduleMetadataImporters[module] = importer; {
importer = creator(module);
}
else
{
importer = (IModuleMetadataImporter)Activator.CreateInstance(typeof(T), module);
}
importer.Init(module);
_customModuleMetadataImporters[key] = importer;
} }
return importer; return (T)importer;
}
public List<T> GetCustomModuleMetadataImporters<T>()
{
var result = new List<T>();
foreach (var kvp in _customModuleMetadataImporters)
{
if (kvp.Key.Item2 == typeof(T))
{
result.Add((T)kvp.Value);
}
}
return result;
} }
} }
} }

View File

@ -13,6 +13,8 @@ namespace Obfuz.Emit
public class EncryptionCompileContext public class EncryptionCompileContext
{ {
public ModuleDef module; public ModuleDef module;
public DefaultModuleMetadataImporter DefaultModuleMetadataImporter => MetadataImporter.Instance.GetDefaultModuleMetadataImporter(module);
} }
public interface IVariableTransformer public interface IVariableTransformer
@ -234,10 +236,10 @@ namespace Obfuz.Emit
switch (_outputType) switch (_outputType)
{ {
case DataNodeType.Int32: 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; break;
case DataNodeType.Int64: 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; break;
default: throw new NotSupportedException($"Unsupported type: {_outputType}"); default: throw new NotSupportedException($"Unsupported type: {_outputType}");
} }
@ -248,10 +250,10 @@ namespace Obfuz.Emit
switch (_outputType) switch (_outputType)
{ {
case DataNodeType.Int32: 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; break;
case DataNodeType.Int64: 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; break;
default: throw new NotSupportedException($"Unsupported type: {_outputType}"); default: throw new NotSupportedException($"Unsupported type: {_outputType}");
} }
@ -312,10 +314,10 @@ namespace Obfuz.Emit
switch (_outputType) switch (_outputType)
{ {
case DataNodeType.Float32: 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; break;
case DataNodeType.Float64: 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; break;
default: throw new NotSupportedException($"Unsupported type: {_outputType}"); default: throw new NotSupportedException($"Unsupported type: {_outputType}");
} }
@ -326,10 +328,10 @@ namespace Obfuz.Emit
switch (_outputType) switch (_outputType)
{ {
case DataNodeType.Float32: 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; break;
case DataNodeType.Float64: 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; break;
default: throw new NotSupportedException($"Unsupported type: {_outputType}"); default: throw new NotSupportedException($"Unsupported type: {_outputType}");
} }

View File

@ -1,5 +1,6 @@
using dnlib.DotNet; using dnlib.DotNet;
using dnlib.DotNet.Emit; using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Utils; using Obfuz.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -8,7 +9,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine.Assertions; using UnityEngine.Assertions;
namespace Obfuz.Emit namespace Obfuz.Encryption
{ {
public struct RvaData public struct RvaData
{ {
@ -24,7 +25,7 @@ namespace Obfuz.Emit
} }
} }
public class ModuleRvaDataAllocator public class ModuleRvaDataAllocator : ModuleEmitManagerBase
{ {
// randomized // randomized
const int maxRvaDataSize = 0x100; const int maxRvaDataSize = 0x100;
@ -33,7 +34,6 @@ namespace Obfuz.Emit
private readonly IRandom _random; private readonly IRandom _random;
private readonly IEncryptor _encryptor; private readonly IEncryptor _encryptor;
class RvaField class RvaField
{ {
public FieldDef holderDataField; public FieldDef holderDataField;
@ -76,6 +76,11 @@ namespace Obfuz.Emit
_encryptor = encryptor; _encryptor = encryptor;
} }
public override void Init(ModuleDef mod)
{
}
private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType) private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType)
{ {
if (_rvaTypeDef == null) if (_rvaTypeDef == null)
@ -230,10 +235,7 @@ namespace Obfuz.Emit
cctorMethod.Body = body; cctorMethod.Body = body;
var ins = body.Instructions; var ins = body.Instructions;
IMethod initializeArrayMethod = mod.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new[] { typeof(Array), typeof(RuntimeFieldHandle) })); DefaultModuleMetadataImporter importer = MetadataImporter.Instance.GetDefaultModuleMetadataImporter(mod);
IMethod decryptArrayMethod = mod.Import(typeof(EncryptionService).GetMethod("DecryptBlock", new[] { typeof(byte[]), typeof(long), typeof(int) }));
Assert.IsNotNull(initializeArrayMethod);
foreach (var field in _rvaFields) foreach (var field in _rvaFields)
{ {
// ldc // ldc
@ -248,12 +250,12 @@ namespace Obfuz.Emit
ins.Add(Instruction.Create(OpCodes.Dup)); ins.Add(Instruction.Create(OpCodes.Dup));
ins.Add(Instruction.Create(OpCodes.Stsfld, field.runtimeValueField)); ins.Add(Instruction.Create(OpCodes.Stsfld, field.runtimeValueField));
ins.Add(Instruction.Create(OpCodes.Ldtoken, field.holderDataField)); 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); // EncryptionService.DecryptBlock(array, field.encryptionOps, field.salt);
ins.Add(Instruction.Create(OpCodes.Ldc_I8, field.encryptionOps)); ins.Add(Instruction.Create(OpCodes.Ldc_I8, field.encryptionOps));
ins.Add(Instruction.Create(OpCodes.Ldc_I4, field.salt)); 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)); ins.Add(Instruction.Create(OpCodes.Ret));
@ -286,7 +288,6 @@ namespace Obfuz.Emit
private readonly IRandom _random; private readonly IRandom _random;
private readonly IEncryptor _encryptor; private readonly IEncryptor _encryptor;
private readonly Dictionary<ModuleDef, ModuleRvaDataAllocator> _modules = new Dictionary<ModuleDef, ModuleRvaDataAllocator>();
public RvaDataAllocator(IRandom random, IEncryptor encryptor) public RvaDataAllocator(IRandom random, IEncryptor encryptor)
{ {
@ -296,12 +297,7 @@ namespace Obfuz.Emit
private ModuleRvaDataAllocator GetModuleRvaDataAllocator(ModuleDef mod) private ModuleRvaDataAllocator GetModuleRvaDataAllocator(ModuleDef mod)
{ {
if (!_modules.TryGetValue(mod, out var allocator)) return EmitManager.Ins.GetEmitManager<ModuleRvaDataAllocator>(mod, mod => new ModuleRvaDataAllocator(mod, _random, _encryptor));
{
allocator = new ModuleRvaDataAllocator(mod, _random, _encryptor);
_modules.Add(mod, allocator);
}
return allocator;
} }
public RvaData Allocate(ModuleDef mod, int value) public RvaData Allocate(ModuleDef mod, int value)
@ -336,7 +332,7 @@ namespace Obfuz.Emit
public void Done() public void Done()
{ {
foreach (var allocator in _modules.Values) foreach (var allocator in EmitManager.Ins.GetEmitManagers<ModuleRvaDataAllocator>())
{ {
allocator.Done(); allocator.Done();
} }

View File

@ -9,12 +9,18 @@ using System.Threading.Tasks;
using MethodImplAttributes = dnlib.DotNet.MethodImplAttributes; using MethodImplAttributes = dnlib.DotNet.MethodImplAttributes;
using TypeAttributes = dnlib.DotNet.TypeAttributes; using TypeAttributes = dnlib.DotNet.TypeAttributes;
namespace Obfuz.Emit namespace Obfuz.ObfusPasses.CallObfus
{ {
public struct ProxyCallMethodData public struct ProxyCallMethodData
{ {
public MethodDef proxyMethod; public readonly MethodDef proxyMethod;
public int secret; public readonly int secret;
public ProxyCallMethodData(MethodDef proxyMethod, int secret)
{
this.proxyMethod = proxyMethod;
this.secret = secret;
}
} }
class ModuleDynamicProxyMethodAllocator class ModuleDynamicProxyMethodAllocator
@ -161,7 +167,7 @@ namespace Obfuz.Emit
methodDispatcher.methods.Add(new CallInfo { method = method, callVir = callVir}); methodDispatcher.methods.Add(new CallInfo { method = method, callVir = callVir});
_methodProxys.Add(key, proxyInfo); _methodProxys.Add(key, proxyInfo);
} }
return new ProxyCallMethodData { proxyMethod = proxyInfo.proxyMethod, secret = proxyInfo.secret }; return new ProxyCallMethodData(proxyInfo.proxyMethod, proxyInfo.secret);
} }
public void Done() public void Done()

View File

@ -1,6 +1,7 @@
using dnlib.DotNet; using dnlib.DotNet;
using dnlib.DotNet.Emit; using dnlib.DotNet.Emit;
using Obfuz.Emit; using Obfuz.Emit;
using Obfuz.Encryption;
using Obfuz.Utils; using Obfuz.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -38,6 +38,7 @@ namespace Obfuz
_obfuscatedAssemblyOutputDir = obfuscatedAssemblyOutputDir; _obfuscatedAssemblyOutputDir = obfuscatedAssemblyOutputDir;
MetadataImporter.Reset(); MetadataImporter.Reset();
EmitManager.Reset();
_assemblyCache = new AssemblyCache(new PathAssemblyResolver(assemblySearchDirs.ToArray())); _assemblyCache = new AssemblyCache(new PathAssemblyResolver(assemblySearchDirs.ToArray()));
foreach (var pass in obfuscationPasses) foreach (var pass in obfuscationPasses)
{ {

View File

@ -10,14 +10,14 @@ namespace Obfuz
{ {
private static readonly IEncryptor _encryptor = new DefaultEncryptor(new byte[] { 0x1A, 0x2B, 0x3C, 0x4D }); 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);
} }
} }
} }