diff --git a/Editor/MemEncrypt/DefaultMemoryEncryptor.cs b/Editor/MemEncrypt/DefaultMemoryEncryptor.cs new file mode 100644 index 0000000..41e3acf --- /dev/null +++ b/Editor/MemEncrypt/DefaultMemoryEncryptor.cs @@ -0,0 +1,150 @@ +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System; +using System.Collections.Generic; +using UnityEngine.Assertions; + +namespace Obfuz.MemEncrypt +{ + public class DefaultMemoryEncryptor : MemoryEncryptorBase + { + + private class ModuleDefaultMemoryEncryptor + { + private readonly ModuleDef _module; + + public ModuleDefaultMemoryEncryptor(ModuleDef module) + { + _module = module; + InitMetadatas(module); + } + + private static IMethod s_castIntAsFloat; + private static IMethod s_castLongAsDouble; + private static IMethod s_castFloatAsInt; + private static IMethod s_castDoubleAsLong; + + private void InitMetadatas(ModuleDef mod) + { + if (s_castFloatAsInt != null) + { + return; + } + 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"); + } + + public void Encrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx) + { + ElementType type = field.FieldType.RemovePinnedAndModifiers().ElementType; + if (type == ElementType.R4) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castFloatAsInt)); + type = ElementType.I4; + } + else if (type == ElementType.R8) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castDoubleAsLong)); + type = ElementType.I8; + } + + if (type == ElementType.I4) + { + outputInstructions.Add(Instruction.CreateLdcI4(100)); + outputInstructions.Add(Instruction.Create(OpCodes.Add)); + } + else if (type == ElementType.I8) + { + outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, 100L)); + outputInstructions.Add(Instruction.Create(OpCodes.Add)); + } + else + { + throw new NotSupportedException($"Unsupported type {type} for MemoryEncryptor"); + } + if (type == ElementType.R4) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castIntAsFloat)); + type = ElementType.I4; + } + else if (type == ElementType.R8) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castLongAsDouble)); + type = ElementType.I8; + } + outputInstructions.Add(ctx.currentInstruction.Clone()); + } + + public void Decrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx) + { + outputInstructions.Add(ctx.currentInstruction.Clone()); + ElementType type = field.FieldType.RemovePinnedAndModifiers().ElementType; + if (type == ElementType.R4) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castFloatAsInt)); + type = ElementType.I4; + } + else if (type == ElementType.R8) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castDoubleAsLong)); + type = ElementType.I8; + } + + if (type == ElementType.I4) + { + outputInstructions.Add(Instruction.CreateLdcI4(100)); + outputInstructions.Add(Instruction.Create(OpCodes.Sub)); + } + else if (type == ElementType.I8) + { + outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, 100L)); + outputInstructions.Add(Instruction.Create(OpCodes.Sub)); + } + else + { + throw new NotSupportedException($"Unsupported type {type} for MemoryEncryptor"); + } + if (type == ElementType.R4) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castIntAsFloat)); + type = ElementType.I4; + } + else if (type == ElementType.R8) + { + outputInstructions.Add(Instruction.Create(OpCodes.Call, s_castLongAsDouble)); + type = ElementType.I8; + } + } + } + + private readonly Dictionary _moduleEncryptors = new Dictionary(); + + private ModuleDefaultMemoryEncryptor GetModuleEncryptor(ModuleDef module) + { + if (!_moduleEncryptors.TryGetValue(module, out var encryptor)) + { + encryptor = new ModuleDefaultMemoryEncryptor(module); + _moduleEncryptors.Add(module, encryptor); + } + return encryptor; + } + + public override void Encrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx) + { + GetModuleEncryptor(ctx.module).Encrypt(field, outputInstructions, ctx); + } + + public override void Decrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx) + { + GetModuleEncryptor(ctx.module).Decrypt(field, outputInstructions, ctx); + } + } +} diff --git a/Editor/MemEncrypt/IMemoryEncryptor.cs b/Editor/MemEncrypt/IMemoryEncryptor.cs new file mode 100644 index 0000000..9ea8aa8 --- /dev/null +++ b/Editor/MemEncrypt/IMemoryEncryptor.cs @@ -0,0 +1,23 @@ +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Obfuz.MemEncrypt +{ + public class MemoryEncryptionContext + { + public ModuleDef module; + + public Instruction currentInstruction; + } + + public interface IMemoryEncryptor + { + void Encrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx); + + void Decrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx); + } +} diff --git a/Editor/MemEncrypt/MemoryEncryptionPass.cs b/Editor/MemEncrypt/MemoryEncryptionPass.cs index 06eb3b2..164948e 100644 --- a/Editor/MemEncrypt/MemoryEncryptionPass.cs +++ b/Editor/MemEncrypt/MemoryEncryptionPass.cs @@ -12,6 +12,7 @@ namespace Obfuz.MemEncrypt public class MemoryEncryptionPass : MethodBodyObfuscationPassBase { private readonly IEncryptionPolicy _encryptionPolicy = new ConfigEncryptionPolicy(); + private readonly IMemoryEncryptor _memoryEncryptor = new DefaultMemoryEncryptor(); public override void Start(ObfuscatorContext ctx) { @@ -57,22 +58,31 @@ namespace Obfuz.MemEncrypt { return false; } + var ctx = new MemoryEncryptionContext + { + module = callingMethod.Module, + currentInstruction = inst, + }; switch (code) { case Code.Ldfld: { + _memoryEncryptor.Decrypt(fieldDef, outputInstructions, ctx); break; } case Code.Stfld: { + _memoryEncryptor.Encrypt(fieldDef, outputInstructions, ctx); break; } case Code.Ldsfld: { + _memoryEncryptor.Decrypt(fieldDef, outputInstructions, ctx); break; } case Code.Stsfld: { + _memoryEncryptor.Encrypt(fieldDef, outputInstructions, ctx); break; } case Code.Ldflda: @@ -83,7 +93,6 @@ namespace Obfuz.MemEncrypt default: return false; } Debug.Log($"memory encrypt field: {field}"); - outputInstructions.Add(inst); return true; } } diff --git a/Editor/MemEncrypt/MemoryEncryptorBase.cs b/Editor/MemEncrypt/MemoryEncryptorBase.cs new file mode 100644 index 0000000..a73c73f --- /dev/null +++ b/Editor/MemEncrypt/MemoryEncryptorBase.cs @@ -0,0 +1,12 @@ +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System.Collections.Generic; + +namespace Obfuz.MemEncrypt +{ + public abstract class MemoryEncryptorBase : IMemoryEncryptor + { + public abstract void Encrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx); + public abstract void Decrypt(FieldDef field, List outputInstructions, MemoryEncryptionContext ctx); + } +} diff --git a/Editor/Obfuscator.cs b/Editor/Obfuscator.cs index 4c6808d..437dfbb 100644 --- a/Editor/Obfuscator.cs +++ b/Editor/Obfuscator.cs @@ -1,6 +1,7 @@ using dnlib.DotNet; using Obfuz.DynamicProxy; using Obfuz.ExprObfuscation; +using Obfuz.MemEncrypt; using Obfuz.Rename; using Obfuz.Virtualization; using System;