diff --git a/Editor/Encryption/EncryptFunction.cs b/Editor/Encryption/EncryptFunction.cs new file mode 100644 index 0000000..ac7408e --- /dev/null +++ b/Editor/Encryption/EncryptFunction.cs @@ -0,0 +1,118 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Obfuz.Encryption +{ + public interface IEncryptInstruction + { + int Encrypt(int value, int[] secretKey, int salt); + + int Decrypt(int value, int[] secretKey, int salt); + } + + public abstract class EncryptInstructionBase : IEncryptInstruction + { + public abstract int Encrypt(int value, int[] secretKey, int salt); + public abstract int Decrypt(int value, int[] secretKey, int salt); + } + + public class AddInstruction : EncryptInstructionBase + { + private readonly int _addValue; + private readonly int _opKeyIndex; + + public AddInstruction(int addValue, int opKeyIndex) + { + _addValue = addValue; + _opKeyIndex = opKeyIndex; + } + public override int Encrypt(int value, int[] secretKey, int salt) + { + return value + secretKey[_opKeyIndex] + salt + _addValue; + } + + public override int Decrypt(int value, int[] secretKey, int salt) + { + return value - secretKey[_opKeyIndex] - salt - _addValue; + } + } + + public class XorInstruction : EncryptInstructionBase + { + private readonly int _xorValue; + private readonly int _opKeyIndex; + + public XorInstruction(int xorValue, int opKeyIndex) + { + _xorValue = xorValue; + _opKeyIndex = opKeyIndex; + } + + public override int Encrypt(int value, int[] secretKey, int salt) + { + return value ^ secretKey[_opKeyIndex] ^ salt ^ _xorValue; + } + + public override int Decrypt(int value, int[] secretKey, int salt) + { + return value ^ secretKey[_opKeyIndex] ^ salt ^ _xorValue; + } + } + + public class BitRotateInstruction : EncryptInstructionBase + { + private readonly int _rotateBitNum; + private readonly int _opKeyIndex; + + public BitRotateInstruction(int rotateBitNum, int opKeyIndex) + { + _rotateBitNum = rotateBitNum; + _opKeyIndex = opKeyIndex; + } + + public override int Encrypt(int value, int[] secretKey, int salt) + { + uint part1 = (uint)value << _rotateBitNum; + uint part2 = (uint)value >> (32 - _rotateBitNum); + return ((int)(part1 | part2) ^ secretKey[_opKeyIndex]) + salt; + } + + public override int Decrypt(int value, int[] secretKey, int salt) + { + uint value2 = (uint)((value - salt) ^ secretKey[_opKeyIndex]); + uint part1 = value2 >> _rotateBitNum; + uint part2 = value2 << (32 - _rotateBitNum); + return (int)(part1 | part2); + } + } + + public class EncryptFunction : EncryptInstructionBase + { + private readonly IEncryptInstruction[] _instructions; + + public EncryptFunction(IEncryptInstruction[] instructions) + { + _instructions = instructions; + } + + public override int Encrypt(int value, int[] secretKey, int salt) + { + foreach (var instruction in _instructions) + { + value = instruction.Encrypt(value, secretKey, salt); + } + return value; + } + + public override int Decrypt(int value, int[] secretKey, int salt) + { + for (int i = _instructions.Length - 1; i >= 0; i--) + { + value = _instructions[i].Decrypt(value, secretKey, salt); + } + return value; + } + } +} diff --git a/Editor/Encryption/EncryptOpCode.cs b/Editor/Encryption/EncryptOpCode.cs new file mode 100644 index 0000000..0a7ef72 --- /dev/null +++ b/Editor/Encryption/EncryptOpCode.cs @@ -0,0 +1,25 @@ +namespace Obfuz.Encryption +{ + public class EncryptOpCode + { + public readonly ushort code; + + public readonly EncryptFunction function; + + public EncryptOpCode(ushort code, EncryptFunction function) + { + this.code = code; + this.function = function; + } + + public int Encrypt(int value, int[] secretKey, int salt) + { + return function.Encrypt(value, secretKey, salt); + } + + public int Decrypt(int value, int[] secretKey, int salt) + { + return function.Decrypt(value, secretKey, salt); + } + } +} diff --git a/Editor/Encryption/EncryptVirtualMachineSimulator.cs b/Editor/Encryption/EncryptVirtualMachineSimulator.cs new file mode 100644 index 0000000..e645567 --- /dev/null +++ b/Editor/Encryption/EncryptVirtualMachineSimulator.cs @@ -0,0 +1,240 @@ +using Obfuz.Utils; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.Assertions; + +namespace Obfuz.Encryption +{ + public class VirtualMachine + { + public readonly int vmSeed; + public readonly EncryptOpCode[] opCodes; + + public VirtualMachine(int vmSeed, EncryptOpCode[] opCodes) + { + this.vmSeed = vmSeed; + this.opCodes = opCodes; + } + } + + public interface IVirtualMachineCreator + { + VirtualMachine CreateVirtualMachine(int opCodeCount, int vmSeed); + } + + public class RandomVirtualMachineCreator : IVirtualMachineCreator + { + + public RandomVirtualMachineCreator() + { + + } + + private IEncryptInstruction CreateRandomInstruction(IRandom random, int opCodeCount) + { + switch (random.NextInt(3)) + { + case 0: + return new AddInstruction(random.NextInt(), random.NextInt(opCodeCount)); + case 1: + return new XorInstruction(random.NextInt(), random.NextInt(opCodeCount)); + case 2: + return new BitRotateInstruction(random.NextInt(32), random.NextInt(opCodeCount)); + default: + throw new System.Exception("Invalid instruction type"); + } + } + + private EncryptOpCode CreateEncryptOpCode(ushort code, IRandom r, int opCodeCount) + { + Assert.IsTrue(code < opCodeCount); + var insts = new IEncryptInstruction[opCodeCount]; + for (int i = 0; i < insts.Length; i++) + { + insts[i] = CreateRandomInstruction(r, opCodeCount); + } + var function = new EncryptFunction(insts); + return new EncryptOpCode(code, function); + } + + public VirtualMachine CreateVirtualMachine(int opCodeCount, int vmSeed) + { + Assert.IsTrue(opCodeCount > 0); + Assert.AreEqual(0, opCodeCount ^ (opCodeCount - 1)); + IRandom r = new RandomWithKey(new byte[] {1,2,3,4,5,6}, vmSeed); + var opCodes = new EncryptOpCode[opCodeCount]; + for (int i = 0; i < opCodes.Length; i++) + { + opCodes[i] = CreateEncryptOpCode((ushort)i, r, opCodeCount); + } + return new VirtualMachine(vmSeed, opCodes); + } + } + + public class EncryptVirtualMachineSimulator + { + private readonly EncryptOpCode[] _opCodes; + private readonly int[] _secretKey; + + public EncryptVirtualMachineSimulator(EncryptOpCode[] opCodes, int[] secretKey) + { + _opCodes = opCodes; + // should be power of 2 + Assert.AreEqual(0, opCodes.Length ^ (opCodes.Length - 1)); + _secretKey = secretKey; + } + + private List DecodeOps(int ops) + { + var codes = new List(); + while (ops > 0) + { + var code = (ushort)(ops % _opCodes.Length); + codes.Add(code); + ops >>= 16; + } + return codes; + } + + public int Encrypt(int value, int ops, int salt) + { + var codes = DecodeOps(ops); + foreach (var code in codes) + { + var opCode = _opCodes[code]; + value = opCode.Encrypt(value, _secretKey, salt); + } + return value; + } + + public int Decrypt(int value, int ops, int salt) + { + var codes = DecodeOps(ops); + for (int i = codes.Count - 1; i >= 0; i--) + { + var opCode = _opCodes[codes[i]]; + value = opCode.Decrypt(value, _secretKey, salt); + } + return value; + } + + public long Encrypt(long value, int ops, int salt) + { + int low = (int)(value & 0xFFFFFFFF); + int high = (int)((value >> 32) & 0xFFFFFFFF); + var codes = DecodeOps(ops); + + // TODO we should encrypt high with encLow + int encLow = Encrypt(low, ops, salt); + int encHigh = Encrypt(high, ops, salt); + + return ((long)encHigh << 32) | (long)(uint)(encLow); + } + + public long Decrypt(long value, int ops, int salt) + { + int low = (int)(value & 0xFFFFFFFF); + int high = (int)((value >> 32) & 0xFFFFFFFF); + var codes = DecodeOps(ops); + + // TODO we should encrypt high with encLow + int decLow = Decrypt(low, ops, salt); + int decHigh = Decrypt(high, ops, salt); + + return ((long)decHigh << 32) | (long)(uint)(decLow); + } + + public float Encrypt(float value, int ops, int salt) + { + int intValue = UnsafeUtility.As(ref value); + int encValue = Encrypt(intValue, ops, salt); + return UnsafeUtility.As(ref encValue); + } + + public float Decrypt(float value, int ops, int salt) + { + int intValue = UnsafeUtility.As(ref value); + int decValue = Decrypt(intValue, ops, salt); + return UnsafeUtility.As(ref decValue); + } + + public double Encrypt(double value, int ops, int salt) + { + long longValue = UnsafeUtility.As(ref value); + long encValue = Encrypt(longValue, ops, salt); + return UnsafeUtility.As(ref encValue); + } + + public double Decrypt(double value, int ops, int salt) + { + long longValue = UnsafeUtility.As(ref value); + long decValue = Decrypt(longValue, ops, salt); + return UnsafeUtility.As(ref decValue); + } + + public int[] Encrypt(byte[] bytes, int offset, int length, int ops, int salt) + { + if (length == 0) + { + return Array.Empty(); + } + int intLength = (length + 3) / 4; + int[] encInts = new int[intLength]; + Buffer.BlockCopy(bytes, offset, encInts, 0, length); + for (int i = 0; i < intLength; i++) + { + encInts[i] = Encrypt(encInts[i], ops, salt); + } + return encInts; + } + + public byte[] Decrypt(int[] value, int offset, int byteLength, int ops, int salt) + { + if (byteLength == 0) + { + return Array.Empty(); + } + int intLength = (byteLength + 3) / 4; + int[] decValue = new int[intLength]; + for (int i = 0; i < intLength; i++) + { + decValue[i] = Decrypt(value[i], ops, salt); + } + + byte[] bytes = new byte[byteLength]; + Buffer.BlockCopy(decValue, 0, bytes, 0, byteLength); + return bytes; + } + + public int[] Encrypt(string value, int ops, int salt) + { + if (value.Length == 0) + { + return Array.Empty(); + } + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(value); + return Encrypt(bytes, 0, bytes.Length, ops, salt); + } + + public string DecryptString(int[] value, int offset, int stringBytesLength, int ops, int salt) + { + if (stringBytesLength == 0) + { + return string.Empty; + } + int intLength = (stringBytesLength + 3) / 4; + int[] intValue = new int[intLength]; + for (int i = 0; i < intLength; i++) + { + intValue[i] = Decrypt(value[i], ops, salt); + } + + byte[] bytes = new byte[stringBytesLength]; + Buffer.BlockCopy(intValue, 0, bytes, 0, stringBytesLength); + return System.Text.Encoding.UTF8.GetString(bytes); + } + } +} diff --git a/Editor/Encryption/OpCodeSetFactory.cs b/Editor/Encryption/OpCodeSetFactory.cs new file mode 100644 index 0000000..7c4164e --- /dev/null +++ b/Editor/Encryption/OpCodeSetFactory.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Obfuz.Encryption +{ + +} diff --git a/Editor/ObfusPasses/ConstObfus/ConstObfusPass.cs b/Editor/ObfusPasses/ConstObfus/ConstObfusPass.cs index e3620ba..94b3b55 100644 --- a/Editor/ObfusPasses/ConstObfus/ConstObfusPass.cs +++ b/Editor/ObfusPasses/ConstObfus/ConstObfusPass.cs @@ -1,5 +1,6 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; +using Obfuz.ObfusPasses.ConstObfus.Policies; using System; using System.Collections.Generic; using System.Linq; @@ -13,7 +14,7 @@ namespace Obfuz.ObfusPasses.ConstObfus public class ConstObfusPass : InstructionObfuscationPassBase { - private IConstObfuscationPolicy _dataObfuscatorPolicy; + private IObfuscationPolicy _dataObfuscatorPolicy; private IDataObfuscator _dataObfuscator; public override void Start(ObfuscationPassContext ctx) diff --git a/Editor/ObfusPasses/ConstObfus/ConstObfuscationPolicyBase.cs b/Editor/ObfusPasses/ConstObfus/ConstObfuscationPolicyBase.cs deleted file mode 100644 index 43e36bf..0000000 --- a/Editor/ObfusPasses/ConstObfus/ConstObfuscationPolicyBase.cs +++ /dev/null @@ -1,42 +0,0 @@ -using dnlib.DotNet; - -namespace Obfuz.ObfusPasses.ConstObfus -{ - public abstract class ConstObfuscationPolicyBase : IConstObfuscationPolicy - { - public virtual bool NeedObfuscateMethod(MethodDef method) - { - return true; - } - - public virtual bool NeedObfuscateInt(MethodDef method, int value) - { - return true; - } - - public virtual bool NeedObfuscateLong(MethodDef method, long value) - { - return true; - } - - public virtual bool NeedObfuscateFloat(MethodDef method, float value) - { - return true; - } - - public virtual bool NeedObfuscateDouble(MethodDef method, double value) - { - return true; - } - - public virtual bool NeedObfuscateString(MethodDef method, string value) - { - return true; - } - - public virtual bool NeedObfuscateArray(MethodDef method, byte[] array) - { - return true; - } - } -} diff --git a/Editor/ObfusPasses/ConstObfus/IDataObfuscator.cs b/Editor/ObfusPasses/ConstObfus/IDataObfuscator.cs index 9bcdbd1..e3307ca 100644 --- a/Editor/ObfusPasses/ConstObfus/IDataObfuscator.cs +++ b/Editor/ObfusPasses/ConstObfus/IDataObfuscator.cs @@ -7,7 +7,6 @@ namespace Obfuz.ObfusPasses.ConstObfus { public interface IDataObfuscator { - void ObfuscateInt(MethodDef method, int value, List obfuscatedInstructions); void ObfuscateLong(MethodDef method, long value, List obfuscatedInstructions); diff --git a/Editor/ObfusPasses/ConstObfus/IConstObfuscationPolicy.cs b/Editor/ObfusPasses/ConstObfus/IObfuscationPolicy.cs similarity index 93% rename from Editor/ObfusPasses/ConstObfus/IConstObfuscationPolicy.cs rename to Editor/ObfusPasses/ConstObfus/IObfuscationPolicy.cs index e32a861..61bba55 100644 --- a/Editor/ObfusPasses/ConstObfus/IConstObfuscationPolicy.cs +++ b/Editor/ObfusPasses/ConstObfus/IObfuscationPolicy.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Obfuz.ObfusPasses.ConstObfus { - public interface IConstObfuscationPolicy + public interface IObfuscationPolicy { bool NeedObfuscateMethod(MethodDef method); diff --git a/Editor/ObfusPasses/ConstObfus/Policies/ObfuscationPolicyBase.cs b/Editor/ObfusPasses/ConstObfus/Policies/ObfuscationPolicyBase.cs new file mode 100644 index 0000000..bb8014b --- /dev/null +++ b/Editor/ObfusPasses/ConstObfus/Policies/ObfuscationPolicyBase.cs @@ -0,0 +1,15 @@ +using dnlib.DotNet; + +namespace Obfuz.ObfusPasses.ConstObfus.Policies +{ + public abstract class ObfuscationPolicyBase : IObfuscationPolicy + { + public abstract bool NeedObfuscateArray(MethodDef method, byte[] array); + public abstract bool NeedObfuscateDouble(MethodDef method, double value); + public abstract bool NeedObfuscateFloat(MethodDef method, float value); + public abstract bool NeedObfuscateInt(MethodDef method, int value); + public abstract bool NeedObfuscateLong(MethodDef method, long value); + public abstract bool NeedObfuscateMethod(MethodDef method); + public abstract bool NeedObfuscateString(MethodDef method, string value); + } +} diff --git a/Editor/ObfusPasses/ConstObfus/RuleBasedObfuscationPolicy.cs b/Editor/ObfusPasses/ConstObfus/Policies/RuleBasedObfuscationPolicy.cs similarity index 90% rename from Editor/ObfusPasses/ConstObfus/RuleBasedObfuscationPolicy.cs rename to Editor/ObfusPasses/ConstObfus/Policies/RuleBasedObfuscationPolicy.cs index f83c97a..2c17f0d 100644 --- a/Editor/ObfusPasses/ConstObfus/RuleBasedObfuscationPolicy.cs +++ b/Editor/ObfusPasses/ConstObfus/Policies/RuleBasedObfuscationPolicy.cs @@ -5,9 +5,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Obfuz.ObfusPasses.ConstObfus +namespace Obfuz.ObfusPasses.ConstObfus.Policies { - public class RuleBasedObfuscationPolicy : ConstObfuscationPolicyBase + public class RuleBasedObfuscationPolicy : ObfuscationPolicyBase { public override bool NeedObfuscateMethod(MethodDef method) { diff --git a/Editor/Utils/RandomWithKey.cs b/Editor/Utils/RandomWithKey.cs index 4426d57..c17cf91 100644 --- a/Editor/Utils/RandomWithKey.cs +++ b/Editor/Utils/RandomWithKey.cs @@ -9,26 +9,21 @@ namespace Obfuz.Utils { public class RandomWithKey : IRandom { + // LCG 参数(使用经典的数值) + private const long a = 1664525; + private const long c = 1013904223; + private const long m = 4294967296; // 2^32 + private readonly byte[] _key; - private readonly Random _random; - private int _nextIndex; + private int _seed; + public RandomWithKey(byte[] key, int seed) { _key = key; - // TODO use key and seed to generate a random number - _random = new Random(GenerateSeed(key, seed)); - } - - private int GenerateSeed(byte[] key, int seed) - { - foreach (var b in key) - { - seed = seed * 31 + b; - } - return seed; + _seed = seed; } public int NextInt(int min, int max) @@ -52,7 +47,8 @@ namespace Obfuz.Utils public int NextInt() { - return _random.Next() ^ GetNextKeyByte(); + _seed = (int)((a * _seed + c) % m); + return _seed ^ GetNextKeyByte(); } public long NextLong()