diff --git a/Editor/Emit/ConstFieldAllocator.cs b/Editor/Emit/ConstFieldAllocator.cs new file mode 100644 index 0000000..a3c2894 --- /dev/null +++ b/Editor/Emit/ConstFieldAllocator.cs @@ -0,0 +1,233 @@ +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using Obfuz.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine.Assertions; + +namespace Obfuz.Emit +{ + public class ModuleConstFieldAllocator + { + private readonly ModuleDef _module; + private readonly IRandom _random; + + private TypeDef _holderTypeDef; + + class ConstFieldInfo + { + public FieldDef field; + public object value; + } + private readonly Dictionary _allocatedFields = new Dictionary(); + private readonly Dictionary _field2Fields = new Dictionary(); + + private readonly List _holderTypeDefs = new List(); + + + public ModuleConstFieldAllocator(ModuleDef mod, IRandom random) + { + _module = mod; + _random = random; + } + + 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)); + _holderTypeDef = new TypeDefUser("$Obfuz$ConstFieldHolder$", objectTypeRef); + _module.Types.Add(_holderTypeDef); + _holderTypeDefs.Add(_holderTypeDef); + _module.EnableTypeDefFindCache = true; + } + + var field = new FieldDefUser($"$RVA_Value{_holderTypeDef.Fields.Count}", new FieldSig(GetTypeSigOfValue(value)), FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly); + field.DeclaringType = _holderTypeDef; + return new ConstFieldInfo + { + field = field, + value = value, + }; + } + + private FieldDef AllocateAny(object value) + { + 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); + } + + //public FieldDef Allocate(byte[] value) + //{ + // throw new NotImplementedException(); + //} + + 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); + + // TODO. obfuscate init codes + foreach (var field in type.Fields) + { + ConstFieldInfo constInfo = _field2Fields[field]; + switch (constInfo.value) + { + case int i: + ins.Add(Instruction.Create(OpCodes.Ldc_I4, i)); + break; + case long l: + ins.Add(Instruction.Create(OpCodes.Ldc_I8, l)); + break; + case float f: + ins.Add(Instruction.Create(OpCodes.Ldc_R4, f)); + break; + case double d: + ins.Add(Instruction.Create(OpCodes.Ldc_R8, d)); + break; + case string s: + ins.Add(Instruction.Create(OpCodes.Ldstr, s)); + break; + //case byte[] b: + // ins.Add(Instruction.Create(OpCodes.Ldlen, b.Length)); + // break; + 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() + { + foreach (var typeDef in _holderTypeDefs) + { + CreateCCtorOfRvaTypeDef(typeDef); + } + } + } + + public class ConstFieldAllocator + { + private readonly IRandom _random; + + private readonly Dictionary _moduleAllocators = new Dictionary(); + + public ConstFieldAllocator(IRandom random) + { + _random = random; + } + + private ModuleConstFieldAllocator GetModuleAllocator(ModuleDef mod) + { + if (!_moduleAllocators.TryGetValue(mod, out var moduleAllocator)) + { + moduleAllocator = new ModuleConstFieldAllocator(mod, _random); + _moduleAllocators[mod] = moduleAllocator; + } + return moduleAllocator; + } + + 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); + } + + public FieldDef Allocate(ModuleDef mod, string value) + { + return GetModuleAllocator(mod).Allocate(value); + } + + public void Done() + { + foreach (var moduleAllocator in _moduleAllocators.Values) + { + moduleAllocator.Done(); + } + } + + //public FieldDef Allocate(ModuleDef mod, byte[] value) + //{ + // return GetModuleAllocator(mod).Allocate(value); + //} + } +} diff --git a/Editor/Virtualization/CompileContext.cs b/Editor/Virtualization/CompileContext.cs index 6a30ad4..75e3a86 100644 --- a/Editor/Virtualization/CompileContext.cs +++ b/Editor/Virtualization/CompileContext.cs @@ -11,5 +11,6 @@ namespace Obfuz.Virtualization public MethodDef method; public List output; public RvaDataAllocator rvaDataAllocator; + public ConstFieldAllocator constFieldAllocator; } } diff --git a/Editor/Virtualization/DataNodes/ConstFieldDataNode.cs b/Editor/Virtualization/DataNodes/ConstFieldDataNode.cs index 65d80bc..3c3926a 100644 --- a/Editor/Virtualization/DataNodes/ConstFieldDataNode.cs +++ b/Editor/Virtualization/DataNodes/ConstFieldDataNode.cs @@ -1,4 +1,7 @@ -using System; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System; +using System.Reflection; using System.Runtime.CompilerServices; namespace Obfuz.Virtualization @@ -8,36 +11,34 @@ namespace Obfuz.Virtualization public override void Compile(CompileContext ctx) { + ModuleDef mod = ctx.method.Module; + var output = ctx.output; + FieldDef field; switch (Type) { - //case DataNodeType.Byte: - //{ - // // create ldloc.i4 - // break; - //} case DataNodeType.Int32: { - // create ldloc.i4 + field = ctx.constFieldAllocator.Allocate(mod, IntValue); break; } case DataNodeType.Int64: { - // create ldloc.i8 + field = ctx.constFieldAllocator.Allocate(mod, LongValue); break; } case DataNodeType.Float32: { - // create ldloc.r4 + field = ctx.constFieldAllocator.Allocate(mod, FloatValue); break; } case DataNodeType.Float64: { - // create ldloc.r8 + field = ctx.constFieldAllocator.Allocate(mod, DoubleValue); break; } case DataNodeType.String: { - // create ldstr + field = ctx.constFieldAllocator.Allocate(mod, StringValue); break; } case DataNodeType.Bytes: @@ -45,13 +46,15 @@ namespace Obfuz.Virtualization // ldsfld // ldtoken // RuntimeHelpers.InitializeArray(array, fieldHandle); - break; + //break; + throw new NotSupportedException("Bytes not supported"); } default: { throw new NotImplementedException($"Type:{Type} not implemented"); } } + output.Add(Instruction.Create(OpCodes.Ldsfld, field)); } } } diff --git a/Editor/Virtualization/DefaultDataObfuscator.cs b/Editor/Virtualization/DefaultDataObfuscator.cs index 9ad00f7..9be395d 100644 --- a/Editor/Virtualization/DefaultDataObfuscator.cs +++ b/Editor/Virtualization/DefaultDataObfuscator.cs @@ -12,12 +12,14 @@ namespace Obfuz.Virtualization private readonly IRandom _random; private readonly RandomDataNodeCreator _nodeCreator; private readonly RvaDataAllocator _rvaDataAllocator; + private readonly ConstFieldAllocator _constFieldAllocator; public DefaultDataObfuscator() { _random = new RandomWithKey(new byte[] { 0x1, 0x2, 0x3, 0x4 }, 0x5); _nodeCreator = new RandomDataNodeCreator(_random); _rvaDataAllocator = new RvaDataAllocator(_random); + _constFieldAllocator = new ConstFieldAllocator(_random); } private void CompileNode(IDataNode node, MethodDef method, List obfuscatedInstructions) @@ -27,6 +29,7 @@ namespace Obfuz.Virtualization method = method, output = obfuscatedInstructions, rvaDataAllocator = _rvaDataAllocator, + constFieldAllocator = _constFieldAllocator, }; node.Compile(ctx); } @@ -67,14 +70,15 @@ namespace Obfuz.Virtualization public void ObfuscateString(MethodDef method, string value, List obfuscatedInstructions) { - //IDataNode node = _nodeCreator.CreateRandom(DataNodeType.String, value); - //CompileNode(node, method, obfuscatedInstructions); - obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldstr, value)); + IDataNode node = _nodeCreator.CreateRandom(DataNodeType.String, value); + CompileNode(node, method, obfuscatedInstructions); + //obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldstr, value)); } public void Stop() { _rvaDataAllocator.Done(); + _constFieldAllocator.Done(); } } } diff --git a/Editor/Virtualization/Functions/ConstFieldDataCreator.cs b/Editor/Virtualization/Functions/ConstFieldDataCreator.cs new file mode 100644 index 0000000..22c128d --- /dev/null +++ b/Editor/Virtualization/Functions/ConstFieldDataCreator.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Obfuz.Virtualization.Functions +{ + public class ConstFieldDataCreator : NodeCreatorBase + { + public override IDataNode CreateExpr(DataNodeType type, object value, CreateExpressionOptions options) + { + return new ConstFieldDataNode { Type = type, Value = value }; + } + } +} diff --git a/Editor/Virtualization/Functions/IntAdd.cs b/Editor/Virtualization/Functions/IntAdd.cs index d9296db..9b2fa25 100644 --- a/Editor/Virtualization/Functions/IntAdd.cs +++ b/Editor/Virtualization/Functions/IntAdd.cs @@ -5,7 +5,6 @@ using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; -using static UnityEngine.Networking.UnityWebRequest; namespace Obfuz.Virtualization.Functions { diff --git a/Editor/Virtualization/RandomDataNodeCreator.cs b/Editor/Virtualization/RandomDataNodeCreator.cs index ccac00f..5946421 100644 --- a/Editor/Virtualization/RandomDataNodeCreator.cs +++ b/Editor/Virtualization/RandomDataNodeCreator.cs @@ -32,6 +32,12 @@ namespace Obfuz.Virtualization }; _functions.Add(DataNodeType.Float32, floatFuncs); _functions.Add(DataNodeType.Float64, floatFuncs); + + var stringFuncs = new List() + { + new ConstFieldDataCreator(), + }; + _functions.Add(DataNodeType.String, stringFuncs); } public override IDataNode CreateRandom(DataNodeType type, object value, CreateExpressionOptions options)