obfuz/Editor/ObfusPasses/FieldEncrypt/DefaultFieldEncryptor.cs

183 lines
8.0 KiB
C#
Raw Normal View History

2025-05-10 19:09:44 +08:00
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEngine.Assertions;
namespace Obfuz.ObfusPasses.FieldEncrypt
{
public class DefaultFieldEncryptor : FieldEncryptorBase
{
2025-05-16 11:33:03 +08:00
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly GroupByModuleEntityManager _moduleEntityManager;
private readonly int _encryptionLevel;
2025-05-10 19:09:44 +08:00
2025-05-16 11:33:03 +08:00
public DefaultFieldEncryptor(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, int encryptionLevel)
2025-05-10 19:09:44 +08:00
{
2025-05-16 11:33:03 +08:00
_encryptionScopeProvider = encryptionScopeProvider;
_moduleEntityManager = moduleEntityManager;
_encryptionLevel = encryptionLevel;
2025-05-10 19:09:44 +08:00
}
2025-05-11 08:53:48 +08:00
private DefaultMetadataImporter GetMetadataImporter(MethodDef method)
2025-05-10 19:09:44 +08:00
{
return _moduleEntityManager.GetDefaultModuleMetadataImporter(method.Module, _encryptionScopeProvider);
2025-05-10 19:09:44 +08:00
}
class FieldEncryptInfo
{
public int encryptOps;
public int salt;
public ElementType fieldType;
public long xorValueForZero;
}
private readonly Dictionary<FieldDef, FieldEncryptInfo> _fieldEncryptInfoCache = new Dictionary<FieldDef, FieldEncryptInfo>();
2025-05-16 11:33:03 +08:00
private long CalcXorValueForZero(IEncryptor encryptor, ElementType type, int encryptOps, int salt)
2025-05-10 19:09:44 +08:00
{
switch (type)
{
case ElementType.I4:
case ElementType.U4:
case ElementType.R4:
2025-05-16 11:33:03 +08:00
return encryptor.Encrypt(0, encryptOps, salt);
2025-05-10 19:09:44 +08:00
case ElementType.I8:
case ElementType.U8:
case ElementType.R8:
2025-05-16 11:33:03 +08:00
return encryptor.Encrypt(0L, encryptOps, salt);
2025-05-10 19:09:44 +08:00
default:
throw new NotSupportedException($"Unsupported field type: {type} for encryption");
}
}
2025-05-16 11:33:03 +08:00
private IRandom CreateRandomForField(RandomCreator randomCreator, FieldDef field)
{
2025-05-16 11:33:03 +08:00
return randomCreator(FieldEqualityComparer.CompareDeclaringTypes.GetHashCode(field));
}
2025-05-16 11:33:03 +08:00
private int GenerateEncryptionOperations(IRandom random, IEncryptor encryptor)
{
2025-05-16 11:33:03 +08:00
return EncryptionUtil.GenerateEncryptionOpCodes(random, encryptor, _encryptionLevel);
2025-05-13 09:27:44 +08:00
}
public int GenerateSalt(IRandom random)
{
return random.NextInt();
}
2025-05-10 19:09:44 +08:00
private FieldEncryptInfo GetFieldEncryptInfo(FieldDef field)
{
if (_fieldEncryptInfoCache.TryGetValue(field, out var info))
{
return info;
}
2025-05-16 11:33:03 +08:00
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(field.Module);
2025-05-10 19:09:44 +08:00
2025-05-16 11:33:03 +08:00
IRandom random = CreateRandomForField(encryptionScope.localRandomCreator, field);
IEncryptor encryptor = encryptionScope.encryptor;
int encryptOps = GenerateEncryptionOperations(random, encryptor);
2025-05-13 09:27:44 +08:00
int salt = GenerateSalt(random);
2025-05-10 19:09:44 +08:00
ElementType fieldType = field.FieldSig.Type.RemovePinnedAndModifiers().ElementType;
2025-05-16 11:33:03 +08:00
long xorValueForZero = CalcXorValueForZero(encryptor, fieldType, encryptOps, salt);
2025-05-10 19:09:44 +08:00
info = new FieldEncryptInfo
{
encryptOps = encryptOps,
salt = salt,
fieldType = fieldType,
xorValueForZero = xorValueForZero,
};
_fieldEncryptInfoCache[field] = info;
return info;
}
public override void Encrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction)
{
2025-05-11 08:53:48 +08:00
DefaultMetadataImporter importer = GetMetadataImporter(method);
EncryptionServiceMetadataImporter encryptionServiceMetadataImporter = importer.GetEncryptionServiceMetadataImporterOfModule(field.Module);
2025-05-10 19:09:44 +08:00
FieldEncryptInfo fei = GetFieldEncryptInfo(field);
if (fei.fieldType == ElementType.I4 || fei.fieldType == ElementType.U4 || fei.fieldType == ElementType.R4)
{
// value has been put on stack
// encrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.EncryptInt));
2025-05-10 19:09:44 +08:00
// xor
outputInstructions.Add(Instruction.CreateLdcI4((int)fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
}
else if (fei.fieldType == ElementType.I8 || fei.fieldType == ElementType.U8 || fei.fieldType == ElementType.R8)
{
// value has been put on stack
// encrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.EncryptLong));
2025-05-10 19:09:44 +08:00
// xor
outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
}
else
{
Assert.IsTrue(false, $"Unsupported field type: {fei.fieldType} for encryption");
}
outputInstructions.Add(currentInstruction.Clone());
}
public override void Decrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction)
{
outputInstructions.Add(currentInstruction.Clone());
2025-05-11 08:53:48 +08:00
DefaultMetadataImporter importer = GetMetadataImporter(method);
EncryptionServiceMetadataImporter encryptionServiceMetadataImporter = importer.GetEncryptionServiceMetadataImporterOfModule(field.Module);
2025-05-10 19:09:44 +08:00
FieldEncryptInfo fei = GetFieldEncryptInfo(field);
if (fei.fieldType == ElementType.I4 || fei.fieldType == ElementType.U4 || fei.fieldType == ElementType.R4)
{
// value has been put on stack
// xor
if (fei.fieldType == ElementType.R4)
{
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.CastFloatAsInt));
}
2025-05-10 19:09:44 +08:00
outputInstructions.Add(Instruction.CreateLdcI4((int)fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
// decrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.DecryptInt));
2025-05-10 19:09:44 +08:00
}
else if (fei.fieldType == ElementType.I8 || fei.fieldType == ElementType.U8 || fei.fieldType == ElementType.R8)
{
// value has been put on stack
// xor
if (fei.fieldType == ElementType.R8)
{
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.CastDoubleAsLong));
}
2025-05-10 19:09:44 +08:00
outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
// decrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.DecryptLong));
2025-05-10 19:09:44 +08:00
}
else
{
Assert.IsTrue(false, $"Unsupported field type: {fei.fieldType} for decryption");
}
}
}
}