2025-04-23 13:01:13 +08:00
|
|
|
|
using dnlib.DotNet;
|
|
|
|
|
using dnlib.DotNet.Emit;
|
2025-05-14 11:18:07 +08:00
|
|
|
|
using Obfuz.Editor;
|
2025-05-08 09:33:35 +08:00
|
|
|
|
using Obfuz.Emit;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
using Obfuz.Utils;
|
|
|
|
|
using System;
|
2025-05-14 14:36:25 +08:00
|
|
|
|
using System.Collections;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2025-05-14 10:46:42 +08:00
|
|
|
|
using System.Net.NetworkInformation;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2025-05-08 09:33:35 +08:00
|
|
|
|
using UnityEngine;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
using UnityEngine.Assertions;
|
|
|
|
|
|
2025-05-08 09:33:35 +08:00
|
|
|
|
namespace Obfuz.Data
|
2025-04-23 13:01:13 +08:00
|
|
|
|
{
|
2025-05-11 08:46:01 +08:00
|
|
|
|
public class ModuleConstFieldAllocator : IGroupByModuleEntity
|
2025-04-23 13:01:13 +08:00
|
|
|
|
{
|
2025-05-08 09:33:35 +08:00
|
|
|
|
private ModuleDef _module;
|
2025-05-16 11:33:03 +08:00
|
|
|
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
2025-05-08 09:33:35 +08:00
|
|
|
|
private readonly RvaDataAllocator _rvaDataAllocator;
|
2025-05-13 08:49:57 +08:00
|
|
|
|
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
2025-05-16 11:33:03 +08:00
|
|
|
|
private EncryptionScopeInfo _encryptionScope;
|
|
|
|
|
private RandomCreator _randomCreator;
|
|
|
|
|
private IEncryptor _encryptor;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
|
|
|
|
|
private TypeDef _holderTypeDef;
|
|
|
|
|
|
|
|
|
|
class ConstFieldInfo
|
|
|
|
|
{
|
|
|
|
|
public FieldDef field;
|
|
|
|
|
public object value;
|
|
|
|
|
}
|
2025-05-14 14:36:25 +08:00
|
|
|
|
|
|
|
|
|
class AnyComparer : IEqualityComparer<object>
|
|
|
|
|
{
|
|
|
|
|
public new bool Equals(object x, object y)
|
|
|
|
|
{
|
|
|
|
|
if (x is byte[] xBytes && y is byte[] yBytes)
|
|
|
|
|
{
|
|
|
|
|
return StructuralComparisons.StructuralEqualityComparer.Equals(xBytes, yBytes);
|
|
|
|
|
}
|
|
|
|
|
return x.Equals(y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int ComputeHashCode(object obj)
|
|
|
|
|
{
|
|
|
|
|
return HashUtil.ComputePrimitiveOrStringOrBytesHashCode(obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetHashCode(object obj)
|
|
|
|
|
{
|
|
|
|
|
return ComputeHashCode(obj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly Dictionary<object, ConstFieldInfo> _allocatedFields = new Dictionary<object, ConstFieldInfo>(new AnyComparer());
|
2025-04-23 13:01:13 +08:00
|
|
|
|
private readonly Dictionary<FieldDef, ConstFieldInfo> _field2Fields = new Dictionary<FieldDef, ConstFieldInfo>();
|
|
|
|
|
|
|
|
|
|
private readonly List<TypeDef> _holderTypeDefs = new List<TypeDef>();
|
2025-05-14 10:46:42 +08:00
|
|
|
|
private bool _done;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
|
|
|
|
|
|
2025-05-16 11:33:03 +08:00
|
|
|
|
public ModuleConstFieldAllocator(EncryptionScopeProvider encryptionScopeProvider, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager)
|
2025-04-23 13:01:13 +08:00
|
|
|
|
{
|
2025-05-16 11:33:03 +08:00
|
|
|
|
_encryptionScopeProvider = encryptionScopeProvider;
|
2025-05-08 09:33:35 +08:00
|
|
|
|
_rvaDataAllocator = rvaDataAllocator;
|
2025-05-13 08:49:57 +08:00
|
|
|
|
_moduleEntityManager = moduleEntityManager;
|
2025-05-08 09:33:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Init(ModuleDef mod)
|
|
|
|
|
{
|
|
|
|
|
_module = mod;
|
2025-05-16 11:33:03 +08:00
|
|
|
|
_encryptionScope = _encryptionScopeProvider.GetScope(mod);
|
|
|
|
|
_randomCreator = _encryptionScope.localRandomCreator;
|
|
|
|
|
_encryptor = _encryptionScope.encryptor;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int maxFieldCount = 1000;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private TypeSig GetTypeSigOfValue(object value)
|
|
|
|
|
{
|
|
|
|
|
if (value is int)
|
|
|
|
|
return _module.CorLibTypes.Int32;
|
|
|
|
|
if (value is long)
|
|
|
|
|
return _module.CorLibTypes.Int64;
|
|
|
|
|
if (value is float)
|
|
|
|
|
return _module.CorLibTypes.Single;
|
|
|
|
|
if (value is double)
|
|
|
|
|
return _module.CorLibTypes.Double;
|
|
|
|
|
if (value is string)
|
|
|
|
|
return _module.CorLibTypes.String;
|
|
|
|
|
if (value is byte[])
|
|
|
|
|
return new SZArraySig(_module.CorLibTypes.Byte);
|
|
|
|
|
throw new NotSupportedException($"Unsupported type: {value.GetType()}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ConstFieldInfo CreateConstFieldInfo(object value)
|
|
|
|
|
{
|
|
|
|
|
if (_holderTypeDef == null || _holderTypeDef.Fields.Count >= maxFieldCount)
|
|
|
|
|
{
|
|
|
|
|
_module.EnableTypeDefFindCache = false;
|
|
|
|
|
ITypeDefOrRef objectTypeRef = _module.Import(typeof(object));
|
2025-05-14 11:18:07 +08:00
|
|
|
|
_holderTypeDef = new TypeDefUser($"{ConstValues.ObfuzMetadataNamePrefix}ConstFieldHolder${_holderTypeDefs.Count}", objectTypeRef);
|
2025-04-23 13:01:13 +08:00
|
|
|
|
_module.Types.Add(_holderTypeDef);
|
|
|
|
|
_holderTypeDefs.Add(_holderTypeDef);
|
|
|
|
|
_module.EnableTypeDefFindCache = true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-14 11:18:07 +08:00
|
|
|
|
var field = new FieldDefUser($"{ConstValues.ObfuzMetadataNamePrefix}RVA_Value{_holderTypeDef.Fields.Count}", new FieldSig(GetTypeSigOfValue(value)), FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly);
|
2025-04-23 13:01:13 +08:00
|
|
|
|
field.DeclaringType = _holderTypeDef;
|
|
|
|
|
return new ConstFieldInfo
|
|
|
|
|
{
|
|
|
|
|
field = field,
|
|
|
|
|
value = value,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private FieldDef AllocateAny(object value)
|
|
|
|
|
{
|
2025-05-14 10:46:42 +08:00
|
|
|
|
if (_done)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("can't Allocate after done");
|
|
|
|
|
}
|
2025-04-23 13:01:13 +08:00
|
|
|
|
if (!_allocatedFields.TryGetValue(value, out var field))
|
|
|
|
|
{
|
|
|
|
|
field = CreateConstFieldInfo(value);
|
|
|
|
|
_allocatedFields.Add(value, field);
|
|
|
|
|
_field2Fields.Add(field.field, field);
|
|
|
|
|
}
|
|
|
|
|
return field.field;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(int value)
|
|
|
|
|
{
|
|
|
|
|
return AllocateAny(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(long value)
|
|
|
|
|
{
|
|
|
|
|
return AllocateAny(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(float value)
|
|
|
|
|
{
|
|
|
|
|
return AllocateAny(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(double value)
|
|
|
|
|
{
|
|
|
|
|
return AllocateAny(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(string value)
|
|
|
|
|
{
|
|
|
|
|
return AllocateAny(value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-09 11:28:17 +08:00
|
|
|
|
public FieldDef Allocate(byte[] value)
|
|
|
|
|
{
|
|
|
|
|
return AllocateAny(value);
|
|
|
|
|
}
|
2025-04-23 13:01:13 +08:00
|
|
|
|
|
2025-05-11 08:53:48 +08:00
|
|
|
|
private DefaultMetadataImporter GetModuleMetadataImporter()
|
2025-05-08 09:33:35 +08:00
|
|
|
|
{
|
2025-05-13 08:49:57 +08:00
|
|
|
|
return _moduleEntityManager.GetDefaultModuleMetadataImporter(_module);
|
2025-05-08 09:33:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-04-23 13:01:13 +08:00
|
|
|
|
private void CreateCCtorOfRvaTypeDef(TypeDef type)
|
|
|
|
|
{
|
|
|
|
|
var cctor = new MethodDefUser(".cctor",
|
|
|
|
|
MethodSig.CreateStatic(_module.CorLibTypes.Void),
|
|
|
|
|
MethodImplAttributes.IL | MethodImplAttributes.Managed,
|
|
|
|
|
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
|
|
|
|
|
cctor.DeclaringType = type;
|
|
|
|
|
//_rvaTypeDef.Methods.Add(cctor);
|
|
|
|
|
var body = new CilBody();
|
|
|
|
|
cctor.Body = body;
|
|
|
|
|
var ins = body.Instructions;
|
|
|
|
|
|
|
|
|
|
//IMethod method = _module.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new[] { typeof(Array), typeof(RuntimeFieldHandle) }));
|
|
|
|
|
//Assert.IsNotNull(method);
|
|
|
|
|
|
2025-05-08 09:33:35 +08:00
|
|
|
|
|
2025-05-11 08:53:48 +08:00
|
|
|
|
DefaultMetadataImporter importer = GetModuleMetadataImporter();
|
2025-04-23 13:01:13 +08:00
|
|
|
|
// TODO. obfuscate init codes
|
|
|
|
|
foreach (var field in type.Fields)
|
|
|
|
|
{
|
|
|
|
|
ConstFieldInfo constInfo = _field2Fields[field];
|
2025-05-14 14:36:25 +08:00
|
|
|
|
IRandom localRandom = _randomCreator(HashUtil.ComputePrimitiveOrStringOrBytesHashCode(constInfo.value));
|
|
|
|
|
int ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, _encryptor, 4);
|
|
|
|
|
int salt = localRandom.NextInt();
|
2025-04-23 13:01:13 +08:00
|
|
|
|
switch (constInfo.value)
|
|
|
|
|
{
|
|
|
|
|
case int i:
|
2025-05-08 09:33:35 +08:00
|
|
|
|
{
|
|
|
|
|
int encryptedValue = _encryptor.Encrypt(i, ops, salt);
|
|
|
|
|
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(ops));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(salt));
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-04-23 13:01:13 +08:00
|
|
|
|
case long l:
|
2025-05-08 09:33:35 +08:00
|
|
|
|
{
|
|
|
|
|
long encryptedValue = _encryptor.Encrypt(l, ops, salt);
|
|
|
|
|
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(ops));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(salt));
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaLong));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-04-23 13:01:13 +08:00
|
|
|
|
case float f:
|
2025-05-08 09:33:35 +08:00
|
|
|
|
{
|
|
|
|
|
float encryptedValue = _encryptor.Encrypt(f, ops, salt);
|
|
|
|
|
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(ops));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(salt));
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaFloat));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-04-23 13:01:13 +08:00
|
|
|
|
case double d:
|
2025-05-08 09:33:35 +08:00
|
|
|
|
{
|
|
|
|
|
double encryptedValue = _encryptor.Encrypt(d, ops, salt);
|
|
|
|
|
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(ops));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(salt));
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaDouble));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-04-23 13:01:13 +08:00
|
|
|
|
case string s:
|
2025-05-08 09:33:35 +08:00
|
|
|
|
{
|
|
|
|
|
byte[] encryptedValue = _encryptor.Encrypt(s, ops, salt);
|
|
|
|
|
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
2025-05-14 14:36:25 +08:00
|
|
|
|
Assert.AreEqual(encryptedValue.Length, rvaData.size);
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(encryptedValue.Length));
|
2025-05-08 09:33:35 +08:00
|
|
|
|
ins.Add(Instruction.CreateLdcI4(ops));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(salt));
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaString));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-05-09 11:28:17 +08:00
|
|
|
|
case byte[] bs:
|
|
|
|
|
{
|
|
|
|
|
byte[] encryptedValue = _encryptor.Encrypt(bs, 0, bs.Length, ops, salt);
|
2025-05-14 14:36:25 +08:00
|
|
|
|
Assert.AreEqual(encryptedValue.Length, bs.Length);
|
2025-05-09 11:28:17 +08:00
|
|
|
|
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(bs.Length));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(ops));
|
|
|
|
|
ins.Add(Instruction.CreateLdcI4(salt));
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaBytes));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-04-23 13:01:13 +08:00
|
|
|
|
default: throw new NotSupportedException($"Unsupported type: {constInfo.value.GetType()}");
|
|
|
|
|
}
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Stsfld, field));
|
|
|
|
|
}
|
|
|
|
|
ins.Add(Instruction.Create(OpCodes.Ret));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Done()
|
|
|
|
|
{
|
2025-05-14 10:46:42 +08:00
|
|
|
|
if (_done)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("Already done");
|
|
|
|
|
}
|
|
|
|
|
_done = true;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
foreach (var typeDef in _holderTypeDefs)
|
|
|
|
|
{
|
|
|
|
|
CreateCCtorOfRvaTypeDef(typeDef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ConstFieldAllocator
|
|
|
|
|
{
|
2025-05-16 11:33:03 +08:00
|
|
|
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
2025-05-08 09:33:35 +08:00
|
|
|
|
private readonly RvaDataAllocator _rvaDataAllocator;
|
2025-05-13 08:49:57 +08:00
|
|
|
|
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
|
2025-05-16 11:33:03 +08:00
|
|
|
|
public ConstFieldAllocator(EncryptionScopeProvider encryptionScopeProvider, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager)
|
2025-04-23 13:01:13 +08:00
|
|
|
|
{
|
2025-05-16 11:33:03 +08:00
|
|
|
|
_encryptionScopeProvider = encryptionScopeProvider;
|
2025-05-08 09:33:35 +08:00
|
|
|
|
_rvaDataAllocator = rvaDataAllocator;
|
2025-05-13 08:49:57 +08:00
|
|
|
|
_moduleEntityManager = moduleEntityManager;
|
2025-04-23 13:01:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ModuleConstFieldAllocator GetModuleAllocator(ModuleDef mod)
|
|
|
|
|
{
|
2025-05-16 11:33:03 +08:00
|
|
|
|
return _moduleEntityManager.GetEntity<ModuleConstFieldAllocator>(mod, () => new ModuleConstFieldAllocator(_encryptionScopeProvider, _rvaDataAllocator, _moduleEntityManager));
|
2025-04-23 13:01:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(ModuleDef mod, int value)
|
|
|
|
|
{
|
|
|
|
|
return GetModuleAllocator(mod).Allocate(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(ModuleDef mod, long value)
|
|
|
|
|
{
|
|
|
|
|
return GetModuleAllocator(mod).Allocate(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(ModuleDef mod, float value)
|
|
|
|
|
{
|
|
|
|
|
return GetModuleAllocator(mod).Allocate(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public FieldDef Allocate(ModuleDef mod, double value)
|
|
|
|
|
{
|
|
|
|
|
return GetModuleAllocator(mod).Allocate(value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-09 11:28:17 +08:00
|
|
|
|
public FieldDef Allocate(ModuleDef mod, byte[] value)
|
|
|
|
|
{
|
|
|
|
|
return GetModuleAllocator(mod).Allocate(value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-23 13:01:13 +08:00
|
|
|
|
public FieldDef Allocate(ModuleDef mod, string value)
|
|
|
|
|
{
|
|
|
|
|
return GetModuleAllocator(mod).Allocate(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Done()
|
|
|
|
|
{
|
2025-05-13 08:49:57 +08:00
|
|
|
|
foreach (var moduleAllocator in _moduleEntityManager.GetEntities<ModuleConstFieldAllocator>())
|
2025-04-23 13:01:13 +08:00
|
|
|
|
{
|
|
|
|
|
moduleAllocator.Done();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|