diff --git a/Editor/Data/ConstFieldAllocator.cs b/Editor/Data/ConstFieldAllocator.cs index 9d1f30c..f70e166 100644 --- a/Editor/Data/ConstFieldAllocator.cs +++ b/Editor/Data/ConstFieldAllocator.cs @@ -4,6 +4,7 @@ using Obfuz.Editor; using Obfuz.Emit; using Obfuz.Utils; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; @@ -17,7 +18,7 @@ namespace Obfuz.Data public class ModuleConstFieldAllocator : IGroupByModuleEntity { private ModuleDef _module; - private readonly IRandom _random; + private readonly RandomCreator _randomCreator; private readonly IEncryptor _encryptor; private readonly RvaDataAllocator _rvaDataAllocator; private readonly GroupByModuleEntityManager _moduleEntityManager; @@ -29,17 +30,40 @@ namespace Obfuz.Data public FieldDef field; public object value; } - private readonly Dictionary _allocatedFields = new Dictionary(); + + class AnyComparer : IEqualityComparer + { + 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 _allocatedFields = new Dictionary(new AnyComparer()); private readonly Dictionary _field2Fields = new Dictionary(); private readonly List _holderTypeDefs = new List(); private bool _done; - public ModuleConstFieldAllocator(IEncryptor encryptor, IRandom random, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager) + public ModuleConstFieldAllocator(IEncryptor encryptor, RandomCreator randomCreator, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager) { _encryptor = encryptor; - _random = random; + _randomCreator = randomCreator; _rvaDataAllocator = rvaDataAllocator; _moduleEntityManager = moduleEntityManager; } @@ -135,16 +159,6 @@ namespace Obfuz.Data return AllocateAny(value); } - private int GenerateEncryptionOperations() - { - return _random.NextInt(); - } - - public int GenerateSalt() - { - return _random.NextInt(); - } - private DefaultMetadataImporter GetModuleMetadataImporter() { return _moduleEntityManager.GetDefaultModuleMetadataImporter(_module); @@ -171,8 +185,9 @@ namespace Obfuz.Data foreach (var field in type.Fields) { ConstFieldInfo constInfo = _field2Fields[field]; - int ops = GenerateEncryptionOperations(); - int salt = GenerateSalt(); + IRandom localRandom = _randomCreator(HashUtil.ComputePrimitiveOrStringOrBytesHashCode(constInfo.value)); + int ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, _encryptor, 4); + int salt = localRandom.NextInt(); switch (constInfo.value) { case int i: @@ -221,13 +236,12 @@ namespace Obfuz.Data } case string s: { - int stringByteLength = Encoding.UTF8.GetByteCount(s); 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)); - //// should use stringByteLength, can't use rvaData.size, because rvaData.size is align to 4, it's not the actual length. - ins.Add(Instruction.CreateLdcI4(stringByteLength)); + Assert.AreEqual(encryptedValue.Length, rvaData.size); + ins.Add(Instruction.CreateLdcI4(encryptedValue.Length)); ins.Add(Instruction.CreateLdcI4(ops)); ins.Add(Instruction.CreateLdcI4(salt)); ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaString)); @@ -236,10 +250,10 @@ namespace Obfuz.Data case byte[] bs: { byte[] encryptedValue = _encryptor.Encrypt(bs, 0, bs.Length, ops, salt); + Assert.AreEqual(encryptedValue.Length, bs.Length); RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue); ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field)); ins.Add(Instruction.CreateLdcI4(rvaData.offset)); - //// should use stringByteLength, can't use rvaData.size, because rvaData.size is align to 4, it's not the actual length. ins.Add(Instruction.CreateLdcI4(bs.Length)); ins.Add(Instruction.CreateLdcI4(ops)); ins.Add(Instruction.CreateLdcI4(salt)); @@ -270,21 +284,21 @@ namespace Obfuz.Data public class ConstFieldAllocator { private readonly IEncryptor _encryptor; - private readonly IRandom _random; + private readonly RandomCreator _randomCreator; private readonly RvaDataAllocator _rvaDataAllocator; private readonly GroupByModuleEntityManager _moduleEntityManager; - public ConstFieldAllocator(IEncryptor encryptor, IRandom random, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager) + public ConstFieldAllocator(IEncryptor encryptor, RandomCreator randomCreator, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager) { _encryptor = encryptor; - _random = random; + _randomCreator = randomCreator; _rvaDataAllocator = rvaDataAllocator; _moduleEntityManager = moduleEntityManager; } private ModuleConstFieldAllocator GetModuleAllocator(ModuleDef mod) { - return _moduleEntityManager.GetEntity(mod, () => new ModuleConstFieldAllocator(_encryptor, _random, _rvaDataAllocator, _moduleEntityManager)); + return _moduleEntityManager.GetEntity(mod, () => new ModuleConstFieldAllocator(_encryptor, _randomCreator, _rvaDataAllocator, _moduleEntityManager)); } public FieldDef Allocate(ModuleDef mod, int value) diff --git a/Editor/EncryptionVM/VirtualMachineSimulator.cs b/Editor/EncryptionVM/VirtualMachineSimulator.cs index 74f2e33..a8e25c8 100644 --- a/Editor/EncryptionVM/VirtualMachineSimulator.cs +++ b/Editor/EncryptionVM/VirtualMachineSimulator.cs @@ -33,7 +33,7 @@ namespace Obfuz.EncryptionVM { int encryptedValue = _opCodes[i].Encrypt(value, _secretKey, i); int decryptedValue = _opCodes[i].Decrypt(encryptedValue, _secretKey, i); - Debug.Log($"instruction type:{_opCodes[i].function.GetType()}"); + //Debug.Log($"instruction type:{_opCodes[i].function.GetType()}"); Assert.AreEqual(value, decryptedValue); } diff --git a/Editor/Obfuscator.cs b/Editor/Obfuscator.cs index 6f58080..c0e0975 100644 --- a/Editor/Obfuscator.cs +++ b/Editor/Obfuscator.cs @@ -128,7 +128,17 @@ namespace Obfuz var gvmInstance = (IEncryptor)Activator.CreateInstance(generatedVmTypes[0], new object[] { _byteSecret } ); - int testValue = 11223344; + VerifyVm(vm, vms, gvmInstance); + + return vms; + } + + private void VerifyVm(VirtualMachine vm, VirtualMachineSimulator vms, IEncryptor gvmInstance) + { + int testInt = 11223344; + long testLong = 1122334455667788L; + float testFloat = 1234f; + double testDouble = 1122334455.0; string testString = "hello,world"; for (int i = 0; i < vm.opCodes.Length; i++) { @@ -136,42 +146,98 @@ namespace Obfuz //int salt = i; //int ops = -1135538782; int salt = -879409147; - int encryptedValueOfVms = vms.Encrypt(testValue, ops, salt); - int decryptedValueOfVms = vms.Decrypt(encryptedValueOfVms, ops, salt); - if (decryptedValueOfVms != testValue) { - throw new Exception($"VirtualMachineSimulator decrypt failed! opCode:{i}, originalValue:{testValue} decryptedValue:{decryptedValueOfVms}"); + int encryptedIntOfVms = vms.Encrypt(testInt, ops, salt); + int decryptedIntOfVms = vms.Decrypt(encryptedIntOfVms, ops, salt); + if (decryptedIntOfVms != testInt) + { + throw new Exception($"VirtualMachineSimulator decrypt failed! opCode:{i}, originalValue:{testInt} decryptedValue:{decryptedIntOfVms}"); + } + int encryptedValueOfGvm = gvmInstance.Encrypt(testInt, ops, salt); + int decryptedValueOfGvm = gvmInstance.Decrypt(encryptedValueOfGvm, ops, salt); + if (encryptedValueOfGvm != encryptedIntOfVms) + { + throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testInt} encryptedValue VirtualMachineSimulator:{encryptedIntOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}"); + } + if (decryptedValueOfGvm != testInt) + { + throw new Exception($"GeneratedEncryptionVirtualMachine decrypt failed! opCode:{i}, originalValue:{testInt} decryptedValue:{decryptedValueOfGvm}"); + } } - int encryptedValueOfGvm = gvmInstance.Encrypt(testValue, ops, salt); - int decryptedValueOfGvm = gvmInstance.Decrypt(encryptedValueOfGvm, ops, salt); - if (encryptedValueOfGvm != encryptedValueOfVms) { - throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testValue} encryptedValue VirtualMachineSimulator:{encryptedValueOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}"); + long encryptedLongOfVms = vms.Encrypt(testLong, ops, salt); + long decryptedLongOfVms = vms.Decrypt(encryptedLongOfVms, ops, salt); + if (decryptedLongOfVms != testLong) + { + throw new Exception($"VirtualMachineSimulator decrypt long failed! opCode:{i}, originalValue:{testLong} decryptedValue:{decryptedLongOfVms}"); + } + long encryptedValueOfGvm = gvmInstance.Encrypt(testLong, ops, salt); + long decryptedValueOfGvm = gvmInstance.Decrypt(encryptedValueOfGvm, ops, salt); + if (encryptedValueOfGvm != encryptedLongOfVms) + { + throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testLong} encryptedValue VirtualMachineSimulator:{encryptedLongOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}"); + } + if (decryptedValueOfGvm != testLong) + { + throw new Exception($"GeneratedEncryptionVirtualMachine decrypt long failed! opCode:{i}, originalValue:{testLong} decryptedValue:{decryptedValueOfGvm}"); + } } - if (decryptedValueOfGvm != testValue) { - throw new Exception($"GeneratedEncryptionVirtualMachine decrypt failed! opCode:{i}, originalValue:{testValue} decryptedValue:{decryptedValueOfGvm}"); + float encryptedFloatOfVms = vms.Encrypt(testFloat, ops, salt); + float decryptedFloatOfVms = vms.Decrypt(encryptedFloatOfVms, ops, salt); + if (decryptedFloatOfVms != testFloat) + { + throw new Exception("encryptedFloat not match"); + } + float encryptedValueOfGvm = gvmInstance.Encrypt(testFloat, ops, salt); + float decryptedValueOfGvm = gvmInstance.Decrypt(encryptedFloatOfVms, ops, salt); + if (encryptedFloatOfVms != encryptedValueOfGvm) + { + throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testFloat} encryptedValue"); + } + if (decryptedValueOfGvm != testFloat) + { + throw new Exception($"GeneratedEncryptionVirtualMachine decrypt float failed! opCode:{i}, originalValue:{testFloat}"); + } + } + { + double encryptedFloatOfVms = vms.Encrypt(testDouble, ops, salt); + double decryptedFloatOfVms = vms.Decrypt(encryptedFloatOfVms, ops, salt); + if (decryptedFloatOfVms != testDouble) + { + throw new Exception("encryptedFloat not match"); + } + double encryptedValueOfGvm = gvmInstance.Encrypt(testDouble, ops, salt); + double decryptedValueOfGvm = gvmInstance.Decrypt(encryptedFloatOfVms, ops, salt); + if (encryptedFloatOfVms != encryptedValueOfGvm) + { + throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testDouble} encryptedValue"); + } + if (decryptedValueOfGvm != testDouble) + { + throw new Exception($"GeneratedEncryptionVirtualMachine decrypt float failed! opCode:{i}, originalValue:{testDouble}"); + } } - byte[] encryptedStrOfVms = vms.Encrypt(testString, ops, salt); - string descryptedStrOfVms = vms.DecryptString(encryptedStrOfVms, 0, encryptedStrOfVms.Length, ops, salt); - if (descryptedStrOfVms != testString) { - throw new Exception($"VirtualMachineSimulator decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{descryptedStrOfVms}"); - } - byte[] encryptedStrOfGvm = gvmInstance.Encrypt(testString, ops, salt); - string descryptedStrOfGvm = gvmInstance.DecryptString(encryptedStrOfGvm, 0, encryptedStrOfGvm.Length, ops, salt); - if (!encryptedStrOfGvm.SequenceEqual(encryptedStrOfVms)) - { - throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testString} encryptedValue VirtualMachineSimulator:{encryptedStrOfVms} GeneratedEncryptionVirtualMachine:{encryptedStrOfGvm}"); - } - if (descryptedStrOfGvm != testString) - { - throw new Exception($"GeneratedEncryptionVirtualMachine decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{descryptedStrOfGvm}"); + byte[] encryptedStrOfVms = vms.Encrypt(testString, ops, salt); + string descryptedStrOfVms = vms.DecryptString(encryptedStrOfVms, 0, encryptedStrOfVms.Length, ops, salt); + if (descryptedStrOfVms != testString) + { + throw new Exception($"VirtualMachineSimulator decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{descryptedStrOfVms}"); + } + byte[] encryptedStrOfGvm = gvmInstance.Encrypt(testString, ops, salt); + string descryptedStrOfGvm = gvmInstance.DecryptString(encryptedStrOfGvm, 0, encryptedStrOfGvm.Length, ops, salt); + if (!encryptedStrOfGvm.SequenceEqual(encryptedStrOfVms)) + { + throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testString} encryptedValue VirtualMachineSimulator:{encryptedStrOfVms} GeneratedEncryptionVirtualMachine:{encryptedStrOfGvm}"); + } + if (descryptedStrOfGvm != testString) + { + throw new Exception($"GeneratedEncryptionVirtualMachine decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{descryptedStrOfGvm}"); + } } } - - return vms; } private void OnPreObfuscation(Pipeline pipeline) @@ -182,10 +248,11 @@ namespace Obfuz LoadAssemblies(assemblyCache, toObfuscatedModules, obfuscatedAndNotObfuscatedModules); var random = new RandomWithKey(_intSecret, _randomSeed); + RandomCreator localRandomCreator = (seed) => new RandomWithKey(_intSecret, _randomSeed ^ seed); var encryptor = CreateEncryptionVirtualMachine(); var moduleEntityManager = new GroupByModuleEntityManager(); var rvaDataAllocator = new RvaDataAllocator(random, encryptor, moduleEntityManager); - var constFieldAllocator = new ConstFieldAllocator(encryptor, random, rvaDataAllocator, moduleEntityManager); + var constFieldAllocator = new ConstFieldAllocator(encryptor, localRandomCreator, rvaDataAllocator, moduleEntityManager); _ctx = new ObfuscationPassContext { assemblyCache = assemblyCache, @@ -197,7 +264,7 @@ namespace Obfuz moduleEntityManager = moduleEntityManager, globalRandom = random, - localRandomCreator = (seed) => new RandomWithKey(_intSecret, _randomSeed ^ seed), + localRandomCreator = localRandomCreator, encryptor = encryptor, rvaDataAllocator = rvaDataAllocator, constFieldAllocator = constFieldAllocator, diff --git a/Editor/Utils/HashUtil.cs b/Editor/Utils/HashUtil.cs index 5cf8acb..5b0d823 100644 --- a/Editor/Utils/HashUtil.cs +++ b/Editor/Utils/HashUtil.cs @@ -1,5 +1,6 @@ using dnlib.DotNet; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -49,5 +50,18 @@ namespace Obfuz.Utils return num + num2 * 1566083941; } } + + public static int ComputePrimitiveOrStringOrBytesHashCode(object obj) + { + if (obj is byte[] bytes) + { + return StructuralComparisons.StructuralEqualityComparer.GetHashCode(bytes); + } + if (obj is string s) + { + return HashUtil.ComputeHash(s); + } + return obj.GetHashCode(); + } } } diff --git a/Runtime/EncryptionService.cs b/Runtime/EncryptionService.cs index 908ad24..946df8c 100644 --- a/Runtime/EncryptionService.cs +++ b/Runtime/EncryptionService.cs @@ -101,13 +101,13 @@ namespace Obfuz public static float DecryptFromRvaFloat(byte[] data, int offset, int ops, int salt) { - int encryptedValue = ConstUtility.GetInt(data, offset); + float encryptedValue = ConstUtility.GetFloat(data, offset); return Decrypt(encryptedValue, ops, salt); } public static double DecryptFromRvaDouble(byte[] data, int offset, int ops, int salt) { - long encryptedValue = ConstUtility.GetLong(data, offset); + double encryptedValue = ConstUtility.GetDouble(data, offset); return Decrypt(encryptedValue, ops, salt); }