From f513b9f414846f9c547f1c616f4e7be593b99f6f Mon Sep 17 00:00:00 2001 From: walon Date: Mon, 28 Apr 2025 11:37:48 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AE=8C=E6=88=90=20ProxyCal?= =?UTF-8?q?l?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DefaultDynamicProxyObfuscator.cs | 14 -- Editor/Emit/DynamicProxyMethodAllocator.cs | 69 ------ Editor/Emit/ProxyCallAllocator.cs | 192 +++++++++++++++ Editor/Obfuz.Editor.asmdef | 2 +- .../ConfigDynamicProxyPolicy.cs | 8 + .../DefaultDynamicProxyObfuscator.cs | 33 +++ .../DynamicProxyObfuscatorBase.cs | 1 + .../DynamicProxyPass.cs | 20 +- .../DynamicProxyPolicyBase.cs | 0 .../IDynamicProxyObfuscator.cs | 2 + .../IDynamicProxyPolicy.cs | 0 Editor/Rename/RenameRecordMap.cs | 1 + Editor/Rename/SymbolRename.cs | 1 + Editor/Rename/UnityRenamePolicy.cs | 1 + Editor/Rename/VirtualMethodGroupCalculator.cs | 1 + Editor/Utils/GenericArgumentContext.cs | 126 ++++++++++ Editor/Utils/HashUtil.cs | 28 +++ Editor/Utils/MetaUtil.cs | 225 +++++++++++++++++- .../Virtualization/DataVirtualizationPass.cs | 2 +- .../Virtualization/DefaultDataObfuscator.cs | 2 +- Editor/Virtualization/IDataObfuscator.cs | 3 +- 21 files changed, 636 insertions(+), 95 deletions(-) delete mode 100644 Editor/DynamicProxy/DefaultDynamicProxyObfuscator.cs delete mode 100644 Editor/Emit/DynamicProxyMethodAllocator.cs create mode 100644 Editor/Emit/ProxyCallAllocator.cs rename Editor/{DynamicProxy => ProxyCall}/ConfigDynamicProxyPolicy.cs (61%) create mode 100644 Editor/ProxyCall/DefaultDynamicProxyObfuscator.cs rename Editor/{DynamicProxy => ProxyCall}/DynamicProxyObfuscatorBase.cs (90%) rename Editor/{DynamicProxy => ProxyCall}/DynamicProxyPass.cs (82%) rename Editor/{DynamicProxy => ProxyCall}/DynamicProxyPolicyBase.cs (100%) rename Editor/{DynamicProxy => ProxyCall}/IDynamicProxyObfuscator.cs (94%) rename Editor/{DynamicProxy => ProxyCall}/IDynamicProxyPolicy.cs (100%) create mode 100644 Editor/Utils/GenericArgumentContext.cs create mode 100644 Editor/Utils/HashUtil.cs diff --git a/Editor/DynamicProxy/DefaultDynamicProxyObfuscator.cs b/Editor/DynamicProxy/DefaultDynamicProxyObfuscator.cs deleted file mode 100644 index b1338d4..0000000 --- a/Editor/DynamicProxy/DefaultDynamicProxyObfuscator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using dnlib.DotNet.Emit; -using dnlib.DotNet; -using System.Collections.Generic; - -namespace Obfuz.DynamicProxy -{ - public class DefaultDynamicProxyObfuscator : DynamicProxyObfuscatorBase - { - public override void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List obfuscatedInstructions) - { - // Default implementation does nothing - } - } -} diff --git a/Editor/Emit/DynamicProxyMethodAllocator.cs b/Editor/Emit/DynamicProxyMethodAllocator.cs deleted file mode 100644 index 4e4de17..0000000 --- a/Editor/Emit/DynamicProxyMethodAllocator.cs +++ /dev/null @@ -1,69 +0,0 @@ -using dnlib.DotNet; -using Obfuz.Utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Obfuz.Emit -{ - public struct DynamicProxyMethodData - { - public MethodDef proxyMethod; - public int methodId; - } - - class ModuleDynamicProxyMethodAllocator - { - private readonly ModuleDef _module; - private readonly IRandom _random; - - public ModuleDynamicProxyMethodAllocator(ModuleDef module, IRandom random) - { - _module = module; - _random = random; - } - - public DynamicProxyMethodData Allocate(IMethod method) - { - return default; - } - - public void Done() - { - - } - } - - public class DynamicProxyMethodAllocator - { - private readonly IRandom _random; - - private readonly Dictionary _moduleAllocators = new Dictionary(); - - public DynamicProxyMethodAllocator(IRandom random) - { - _random = random; - } - - public DynamicProxyMethodData Allocate(ModuleDef mod, IMethod method) - { - if (!_moduleAllocators.TryGetValue(mod, out var allocator)) - { - allocator = new ModuleDynamicProxyMethodAllocator(mod, _random); - _moduleAllocators.Add(mod, allocator); - } - return allocator.Allocate(method); - } - - public void Done() - { - foreach (var allocator in _moduleAllocators.Values) - { - allocator.Done(); - } - _moduleAllocators.Clear(); - } - } -} diff --git a/Editor/Emit/ProxyCallAllocator.cs b/Editor/Emit/ProxyCallAllocator.cs new file mode 100644 index 0000000..e50bf46 --- /dev/null +++ b/Editor/Emit/ProxyCallAllocator.cs @@ -0,0 +1,192 @@ +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 MethodImplAttributes = dnlib.DotNet.MethodImplAttributes; +using TypeAttributes = dnlib.DotNet.TypeAttributes; + +namespace Obfuz.Emit +{ + public struct ProxyCallMethodData + { + public MethodDef proxyMethod; + public int secret; + } + + class ModuleDynamicProxyMethodAllocator + { + private readonly ModuleDef _module; + private readonly IRandom _random; + + class MethodKey : IEquatable + { + public readonly IMethod _method; + public readonly bool _callVir; + private readonly int _hashCode; + + public MethodKey(IMethod method, bool callVir) + { + _method = method; + _callVir = callVir; + _hashCode = HashUtil.CombineHash(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method), callVir ? 1 : 0); + } + + public override int GetHashCode() + { + return _hashCode; + } + + public bool Equals(MethodKey other) + { + return MethodEqualityComparer.CompareDeclaringTypes.Equals(_method, other._method) && _callVir == other._callVir; + } + } + + class MethodProxyInfo + { + public MethodDef proxyMethod; + public int secret; + } + + private readonly Dictionary _methodProxys = new Dictionary(); + + + const int maxProxyMethodPerDispatchMethod = 1000; + class DispatchMethodInfo + { + public MethodDef methodDef; + public int secret; + public List methods = new List(); + } + + private readonly Dictionary> _dispatchMethods = new Dictionary>(SignatureEqualityComparer.Instance); + + + private TypeDef _proxyTypeDef; + + public ModuleDynamicProxyMethodAllocator(ModuleDef module, IRandom random) + { + _module = module; + _random = random; + } + + private TypeDef CreateProxyTypeDef() + { + var typeDef = new TypeDefUser("$Obfuz$ProxyCall", _module.CorLibTypes.Object.ToTypeDefOrRef()); + typeDef.Attributes = TypeAttributes.NotPublic | TypeAttributes.Sealed; + _module.EnableTypeDefFindCache = false; + _module.Types.Add(typeDef); + _module.EnableTypeDefFindCache = true; + return typeDef; + } + + private MethodDef CreateDispatchMethodInfo(MethodSig methodSig) + { + if (_proxyTypeDef == null) + { + _proxyTypeDef = CreateProxyTypeDef(); + } + MethodDef methodDef = new MethodDefUser($"$Obfuz$ProxyCall$Dispatch${_proxyTypeDef.Methods.Count}", methodSig, + MethodImplAttributes.IL | MethodImplAttributes.Managed, + MethodAttributes.Static | MethodAttributes.Private); + methodDef.DeclaringType = _proxyTypeDef; + return methodDef; + } + + private MethodSig CreateDispatchMethodSig(IMethod method) + { + MethodSig methodSig = MetaUtil.ToSharedMethodSig(_module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method)); + // extra param for secret + methodSig.Params.Add(_module.CorLibTypes.Int32); + return methodSig; + } + + private DispatchMethodInfo GetDispatchMethod(IMethod method) + { + MethodSig methodSig = CreateDispatchMethodSig(method); + if (!_dispatchMethods.TryGetValue(methodSig, out var dispatchMethods)) + { + dispatchMethods = new List(); + _dispatchMethods.Add(methodSig, dispatchMethods); + } + if (dispatchMethods.Count == 0 || dispatchMethods.Last().methods.Count >= maxProxyMethodPerDispatchMethod) + { + var newDispatchMethodInfo = new DispatchMethodInfo + { + methodDef = CreateDispatchMethodInfo(methodSig), + secret = 0, + }; + dispatchMethods.Add(newDispatchMethodInfo); + } + return dispatchMethods.Last(); + } + + public ProxyCallMethodData Allocate(IMethod method, bool callVir) + { + var key = new MethodKey(method, callVir); + if (!_methodProxys.TryGetValue(key, out var proxyInfo)) + { + var methodDispatcher = GetDispatchMethod(method); + proxyInfo = new MethodProxyInfo() + { + proxyMethod = methodDispatcher.methodDef, + secret = methodDispatcher.methods.Count, + }; + methodDispatcher.methods.Add(method); + _methodProxys.Add(key, proxyInfo); + } + return new ProxyCallMethodData { proxyMethod = proxyInfo.proxyMethod, secret = proxyInfo.secret }; + } + + public void Done() + { + foreach (DispatchMethodInfo dispatchMethod in _dispatchMethods.Values.SelectMany(ms => ms)) + { + var methodDef = dispatchMethod.methodDef; + var secret = dispatchMethod.secret; + var methodSig = methodDef.MethodSig; + + + var body = new CilBody(); + methodDef.Body = body; + var ins = body.Instructions; + ins.Add(Instruction.Create(OpCodes.Ret)); + } + } + } + + public class ProxyCallAllocator + { + private readonly IRandom _random; + + private readonly Dictionary _moduleAllocators = new Dictionary(); + + public ProxyCallAllocator(IRandom random) + { + _random = random; + } + + public ProxyCallMethodData Allocate(ModuleDef mod, IMethod method, bool callVir) + { + if (!_moduleAllocators.TryGetValue(mod, out var allocator)) + { + allocator = new ModuleDynamicProxyMethodAllocator(mod, _random); + _moduleAllocators.Add(mod, allocator); + } + return allocator.Allocate(method, callVir); + } + + public void Done() + { + foreach (var allocator in _moduleAllocators.Values) + { + allocator.Done(); + } + _moduleAllocators.Clear(); + } + } +} diff --git a/Editor/Obfuz.Editor.asmdef b/Editor/Obfuz.Editor.asmdef index 485efac..241eca7 100644 --- a/Editor/Obfuz.Editor.asmdef +++ b/Editor/Obfuz.Editor.asmdef @@ -1,5 +1,5 @@ { - "name": "Obfuz", + "name": "Obfuz.Editor", "rootNamespace": "", "references": [ "GUID:7e4de3067c2ab5c43a03ac49273dfb68", diff --git a/Editor/DynamicProxy/ConfigDynamicProxyPolicy.cs b/Editor/ProxyCall/ConfigDynamicProxyPolicy.cs similarity index 61% rename from Editor/DynamicProxy/ConfigDynamicProxyPolicy.cs rename to Editor/ProxyCall/ConfigDynamicProxyPolicy.cs index caec8db..bbd8426 100644 --- a/Editor/DynamicProxy/ConfigDynamicProxyPolicy.cs +++ b/Editor/ProxyCall/ConfigDynamicProxyPolicy.cs @@ -16,6 +16,14 @@ namespace Obfuz.DynamicProxy public override bool NeedDynamicProxyCalledMethod(IMethod method, bool callVir) { + + ITypeDefOrRef declaringType = method.DeclaringType; + TypeDef typeDef = declaringType.ResolveTypeDef(); + // doesn't proxy call if the method is a delegate + if (typeDef != null && typeDef.IsDelegate) + { + return false; + } return true; } } diff --git a/Editor/ProxyCall/DefaultDynamicProxyObfuscator.cs b/Editor/ProxyCall/DefaultDynamicProxyObfuscator.cs new file mode 100644 index 0000000..9400813 --- /dev/null +++ b/Editor/ProxyCall/DefaultDynamicProxyObfuscator.cs @@ -0,0 +1,33 @@ +using dnlib.DotNet.Emit; +using dnlib.DotNet; +using System.Collections.Generic; +using Obfuz.Utils; +using Obfuz.Emit; + +namespace Obfuz.DynamicProxy +{ + public class DefaultDynamicProxyObfuscator : DynamicProxyObfuscatorBase + { + private readonly IRandom _random; + private readonly ProxyCallAllocator _proxyCallAllocator; + + public DefaultDynamicProxyObfuscator(IRandom random) + { + _random = random; + _proxyCallAllocator = new ProxyCallAllocator(random); + } + + public override void Done() + { + _proxyCallAllocator.Done(); + } + + public override void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List obfuscatedInstructions) + { + MethodSig sharedMethodSig = MetaUtil.ToSharedMethodSig(calledMethod.Module.CorLibTypes, MetaUtil.GetInflatedMethodSig(calledMethod)); + ProxyCallMethodData proxyCallMethodData = _proxyCallAllocator.Allocate(callingMethod.Module, calledMethod, callVir); + obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.secret)); + obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, proxyCallMethodData.proxyMethod)); + } + } +} diff --git a/Editor/DynamicProxy/DynamicProxyObfuscatorBase.cs b/Editor/ProxyCall/DynamicProxyObfuscatorBase.cs similarity index 90% rename from Editor/DynamicProxy/DynamicProxyObfuscatorBase.cs rename to Editor/ProxyCall/DynamicProxyObfuscatorBase.cs index 827abcb..ae23bc4 100644 --- a/Editor/DynamicProxy/DynamicProxyObfuscatorBase.cs +++ b/Editor/ProxyCall/DynamicProxyObfuscatorBase.cs @@ -7,5 +7,6 @@ namespace Obfuz.DynamicProxy public abstract class DynamicProxyObfuscatorBase : IDynamicProxyObfuscator { public abstract void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List obfuscatedInstructions); + public abstract void Done(); } } diff --git a/Editor/DynamicProxy/DynamicProxyPass.cs b/Editor/ProxyCall/DynamicProxyPass.cs similarity index 82% rename from Editor/DynamicProxy/DynamicProxyPass.cs rename to Editor/ProxyCall/DynamicProxyPass.cs index 49c37ad..91ee7fe 100644 --- a/Editor/DynamicProxy/DynamicProxyPass.cs +++ b/Editor/ProxyCall/DynamicProxyPass.cs @@ -1,5 +1,6 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; +using Obfuz.Utils; using Obfuz.Virtualization; using System; using System.Collections.Generic; @@ -13,18 +14,20 @@ namespace Obfuz.DynamicProxy { public class DynamicProxyPass : MethodBodyObfuscationPassBase { + private readonly IRandom _random; private readonly IDynamicProxyPolicy _dynamicProxyPolicy; private readonly IDynamicProxyObfuscator _dynamicProxyObfuscator; public DynamicProxyPass() { + _random = new RandomWithKey(new byte[] { 0x1, 0x2, 0x3, 0x4 }, 0x5); _dynamicProxyPolicy = new ConfigDynamicProxyPolicy(); - _dynamicProxyObfuscator = new DefaultDynamicProxyObfuscator(); + _dynamicProxyObfuscator = new DefaultDynamicProxyObfuscator(_random); } public override void Stop(ObfuscatorContext ctx) { - + _dynamicProxyObfuscator.Done(); } public override void Start(ObfuscatorContext ctx) @@ -40,12 +43,20 @@ namespace Obfuz.DynamicProxy protected override bool TryObfuscateInstruction(MethodDef method, Instruction inst, IList instructions, int instructionIndex, List outputInstructions, List totalFinalInstructions) { - return false; + IMethod calledMethod = inst.Operand as IMethod; + if (calledMethod == null || !calledMethod.IsMethod) + { + return false; + } + if (MetaUtil.ContainsContainsGenericParameter(calledMethod)) + { + return false; + } + switch (inst.OpCode.Code) { case Code.Call: { - IMethod calledMethod = (IMethod)inst.Operand; if (!_dynamicProxyPolicy.NeedDynamicProxyCalledMethod(calledMethod, false)) { return false; @@ -59,7 +70,6 @@ namespace Obfuz.DynamicProxy { return false; } - IMethod calledMethod = (IMethod)inst.Operand; if (!_dynamicProxyPolicy.NeedDynamicProxyCalledMethod(calledMethod, true)) { return false; diff --git a/Editor/DynamicProxy/DynamicProxyPolicyBase.cs b/Editor/ProxyCall/DynamicProxyPolicyBase.cs similarity index 100% rename from Editor/DynamicProxy/DynamicProxyPolicyBase.cs rename to Editor/ProxyCall/DynamicProxyPolicyBase.cs diff --git a/Editor/DynamicProxy/IDynamicProxyObfuscator.cs b/Editor/ProxyCall/IDynamicProxyObfuscator.cs similarity index 94% rename from Editor/DynamicProxy/IDynamicProxyObfuscator.cs rename to Editor/ProxyCall/IDynamicProxyObfuscator.cs index 14e1fbc..bd3126b 100644 --- a/Editor/DynamicProxy/IDynamicProxyObfuscator.cs +++ b/Editor/ProxyCall/IDynamicProxyObfuscator.cs @@ -11,5 +11,7 @@ namespace Obfuz.DynamicProxy public interface IDynamicProxyObfuscator { void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List obfuscatedInstructions); + + void Done(); } } diff --git a/Editor/DynamicProxy/IDynamicProxyPolicy.cs b/Editor/ProxyCall/IDynamicProxyPolicy.cs similarity index 100% rename from Editor/DynamicProxy/IDynamicProxyPolicy.cs rename to Editor/ProxyCall/IDynamicProxyPolicy.cs diff --git a/Editor/Rename/RenameRecordMap.cs b/Editor/Rename/RenameRecordMap.cs index ab20c28..bcc744f 100644 --- a/Editor/Rename/RenameRecordMap.cs +++ b/Editor/Rename/RenameRecordMap.cs @@ -1,5 +1,6 @@ using dnlib.DotNet; using Obfuz.Rename; +using Obfuz.Utils; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/Editor/Rename/SymbolRename.cs b/Editor/Rename/SymbolRename.cs index 0c3f1e8..aab622c 100644 --- a/Editor/Rename/SymbolRename.cs +++ b/Editor/Rename/SymbolRename.cs @@ -1,5 +1,6 @@ using dnlib.DotNet; using Obfuz.Rename; +using Obfuz.Utils; using System; using System.Collections; using System.Collections.Generic; diff --git a/Editor/Rename/UnityRenamePolicy.cs b/Editor/Rename/UnityRenamePolicy.cs index 5ae0ad9..e9503d7 100644 --- a/Editor/Rename/UnityRenamePolicy.cs +++ b/Editor/Rename/UnityRenamePolicy.cs @@ -1,4 +1,5 @@ using dnlib.DotNet; +using Obfuz.Utils; using System; using System.Collections.Generic; using System.Runtime.InteropServices; diff --git a/Editor/Rename/VirtualMethodGroupCalculator.cs b/Editor/Rename/VirtualMethodGroupCalculator.cs index de81f15..ab0bc51 100644 --- a/Editor/Rename/VirtualMethodGroupCalculator.cs +++ b/Editor/Rename/VirtualMethodGroupCalculator.cs @@ -1,4 +1,5 @@ using dnlib.DotNet; +using Obfuz.Utils; using System; using System.Collections.Generic; using System.Linq; diff --git a/Editor/Utils/GenericArgumentContext.cs b/Editor/Utils/GenericArgumentContext.cs new file mode 100644 index 0000000..a9f89be --- /dev/null +++ b/Editor/Utils/GenericArgumentContext.cs @@ -0,0 +1,126 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Obfuz.Utils +{ + /// + /// Replaces generic type/method var with its generic argument + /// + public sealed class GenericArgumentContext + { + List typeArgsStack; + List methodArgsStack; + + public GenericArgumentContext(IList typeArgsStack, IList methodArgsStack) + { + this.typeArgsStack = typeArgsStack?.ToList(); + this.methodArgsStack = methodArgsStack?.ToList(); + } + + + + /// + /// Replaces a generic type/method var with its generic argument (if any). If + /// isn't a generic type/method var or if it can't + /// be resolved, it itself is returned. Else the resolved type is returned. + /// + /// Type signature + /// New which is never null unless + /// is null + public TypeSig Resolve(TypeSig typeSig) + { + if (!typeSig.ContainsGenericParameter) + { + return typeSig; + } + typeSig = typeSig.RemovePinnedAndModifiers(); + switch (typeSig.ElementType) + { + case ElementType.Ptr: return new PtrSig(Resolve(typeSig.Next)); + case ElementType.ByRef: return new PtrSig(Resolve(typeSig.Next)); + + case ElementType.SZArray: return new PtrSig(Resolve(typeSig.Next)); + case ElementType.Array: + { + var ara = (ArraySig)typeSig; + return new ArraySig(Resolve(typeSig.Next), ara.Rank, ara.Sizes, ara.LowerBounds); + } + + case ElementType.Var: + { + GenericVar genericVar = (GenericVar)typeSig; + var newSig = Resolve(typeArgsStack, genericVar.Number, true); + if (newSig == null) + { + throw new Exception(); + } + return newSig; + } + + case ElementType.MVar: + { + GenericMVar genericVar = (GenericMVar)typeSig; + var newSig = Resolve(methodArgsStack, genericVar.Number, true); + if (newSig == null) + { + throw new Exception(); + } + return newSig; + } + case ElementType.GenericInst: + { + var gia = (GenericInstSig)typeSig; + return new GenericInstSig(gia.GenericType, gia.GenericArguments.Select(ga => Resolve(ga)).ToList()); + } + + case ElementType.FnPtr: + { + var fptr = (FnPtrSig)typeSig; + var cs = fptr.Signature; + CallingConventionSig ccs; + switch (cs) + { + case MethodSig ms: + { + ccs = new MethodSig(ms.GetCallingConvention(), ms.GenParamCount, Resolve(ms.RetType), ms.Params.Select(p => Resolve(p)).ToList()); + break; + } + case PropertySig ps: + { + ccs = new PropertySig(ps.HasThis, Resolve(ps.RetType)); + break; + } + case GenericInstMethodSig gims: + { + ccs = new GenericInstMethodSig(gims.GenericArguments.Select(ga => Resolve(ga)).ToArray()); + break; + } + default: throw new NotSupportedException(cs.ToString()); + } + return new FnPtrSig(ccs); + } + + case ElementType.ValueArray: + { + var vas = (ValueArraySig)typeSig; + return new ValueArraySig(Resolve(vas.Next), vas.Size); + } + default: return typeSig; + } + } + + private TypeSig Resolve(List args, uint number, bool isTypeVar) + { + var typeSig = args[(int)number]; + var gvar = typeSig as GenericSig; + if (gvar is null || gvar.IsTypeVar != isTypeVar) + return typeSig; + return gvar; + } + } + +} diff --git a/Editor/Utils/HashUtil.cs b/Editor/Utils/HashUtil.cs new file mode 100644 index 0000000..6d858d5 --- /dev/null +++ b/Editor/Utils/HashUtil.cs @@ -0,0 +1,28 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Obfuz +{ + public static class HashUtil + { + public static int CombineHash(int hash1, int hash2) + { + return hash1 * 1566083941 + hash2; + } + + public static int ComputeHash(List sigs) + { + int hash = 135781321; + TypeEqualityComparer tc = TypeEqualityComparer.Instance; + foreach (var sig in sigs) + { + hash = hash * 1566083941 + tc.GetHashCode(sig); + } + return hash; + } + } +} diff --git a/Editor/Utils/MetaUtil.cs b/Editor/Utils/MetaUtil.cs index f250f6d..06ae6b7 100644 --- a/Editor/Utils/MetaUtil.cs +++ b/Editor/Utils/MetaUtil.cs @@ -5,8 +5,9 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using UnityEngine.Assertions; -namespace Obfuz +namespace Obfuz.Utils { public static class MetaUtil { @@ -165,9 +166,9 @@ namespace Obfuz case ElementType.R4: case ElementType.R8: case ElementType.String: - return true; + return true; case ElementType.Class: - return IsScriptOrSerializableType(typeSig.ToTypeDefOrRef().ResolveTypeDefThrow()); + return IsScriptOrSerializableType(typeSig.ToTypeDefOrRef().ResolveTypeDefThrow()); case ElementType.ValueType: { TypeDef typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow(); @@ -420,5 +421,223 @@ namespace Obfuz } return anyChange; } + + //public static bool ContainsContainsGenericParameter1(MethodDef method) + //{ + // Assert.IsTrue(!(method.DeclaringType.ContainsGenericParameter || method.MethodSig.ContainsGenericParameter)); + // return false; + //} + + public static bool ContainsContainsGenericParameter1(MethodSpec methodSpec) + { + if (methodSpec.GenericInstMethodSig.ContainsGenericParameter) + { + return true; + } + IMethodDefOrRef method = methodSpec.Method; + if (method.IsMethodDef) + { + return false;// ContainsContainsGenericParameter1((MethodDef)method); + } + if (method.IsMemberRef) + { + return ContainsContainsGenericParameter1((MemberRef)method); + } + throw new Exception($"unknown method: {method}"); + } + + public static bool ContainsContainsGenericParameter1(MemberRef memberRef) + { + IMemberRefParent parent = memberRef.Class; + if (parent is TypeSpec typeSpec) + { + return typeSpec.ContainsGenericParameter; + } + return false; + } + + public static bool ContainsContainsGenericParameter(IMethod method) + { + Assert.IsTrue(method.IsMethod); + if (method is MethodDef methodDef) + { + return false; + } + + if (method is MethodSpec methodSpec) + { + return ContainsContainsGenericParameter1(methodSpec); + } + if (method is MemberRef memberRef) + { + return ContainsContainsGenericParameter1(memberRef); + } + throw new Exception($"unknown method: {method}"); + } + + + + public static TypeSig Inflate(TypeSig sig, GenericArgumentContext ctx) + { + if (!sig.ContainsGenericParameter) + { + return sig; + } + return ctx.Resolve(sig); + } + + + public static MethodSig InflateMethodSig(MethodSig methodSig, GenericArgumentContext genericArgumentContext) + { + var newReturnType = Inflate(methodSig.RetType, genericArgumentContext); + var newParams = new List(); + foreach (var param in methodSig.Params) + { + newParams.Add(Inflate(param, genericArgumentContext)); + } + var newParamsAfterSentinel = new List(); + if (methodSig.ParamsAfterSentinel != null) + { + throw new NotSupportedException($"methodSig.ParamsAfterSentinel is not supported: {methodSig}"); + //foreach (var param in methodSig.ParamsAfterSentinel) + //{ + // newParamsAfterSentinel.Add(Inflate(param, genericArgumentContext)); + //} + } + return new MethodSig(methodSig.CallingConvention, methodSig.GenParamCount, newReturnType, newParams, null); + } + + public static IList GetGenericArguments(IMemberRefParent type) + { + if (type is TypeDef typeDef) + { + return null; + } + if (type is TypeRef typeRef) + { + return null; + } + if (type is TypeSpec typeSpec) + { + GenericInstSig genericInstSig = typeSpec.TypeSig.ToGenericInstSig(); + return genericInstSig?.GenericArguments; + } + throw new NotSupportedException($"type:{type}"); + } + + public static MethodSig GetInflatedMethodSig(IMethod method) + { + if (method is MethodDef methodDef) + { + return methodDef.MethodSig; + } + if (method is MemberRef memberRef) + { + return InflateMethodSig(memberRef.MethodSig, new GenericArgumentContext(GetGenericArguments(memberRef.Class), null)); + } + if (method is MethodSpec methodSpec) + { + var genericInstMethodSig = methodSpec.GenericInstMethodSig; + if (methodSpec.Method is MethodDef methodDef2) + { + return InflateMethodSig(methodDef2.MethodSig, new GenericArgumentContext(null, genericInstMethodSig.GenericArguments)); + } + if (methodSpec.Method is MemberRef memberRef2) + { + return InflateMethodSig(memberRef2.MethodSig, new GenericArgumentContext(GetGenericArguments(memberRef2.Class), genericInstMethodSig.GenericArguments)); + } + + } + throw new NotSupportedException($" method: {method}"); + } + + public static MethodSig ToSharedMethodSig(ICorLibTypes corTypes, MethodSig methodSig) + { + var newReturnType = ToShareTypeSig(corTypes, methodSig.RetType); + var newParams = new List(); + foreach (var param in methodSig.Params) + { + newParams.Add(ToShareTypeSig(corTypes, param)); + } + if (methodSig.ParamsAfterSentinel != null) + { + //foreach (var param in methodSig.ParamsAfterSentinel) + //{ + // newParamsAfterSentinel.Add(ToShareTypeSig(corTypes, param)); + //} + throw new NotSupportedException($"methodSig.ParamsAfterSentinel is not supported: {methodSig}"); + } + return new MethodSig(methodSig.CallingConvention, methodSig.GenParamCount, newReturnType, newParams, null); + } + + public static TypeSig ToShareTypeSig(ICorLibTypes corTypes, TypeSig typeSig) + { + var a = typeSig.RemovePinnedAndModifiers(); + switch (a.ElementType) + { + case ElementType.Void: return corTypes.Void; + case ElementType.Boolean: return corTypes.Byte; + case ElementType.Char: return corTypes.UInt16; + case ElementType.I1: return corTypes.SByte; + case ElementType.U1: return corTypes.Byte; + case ElementType.I2: return corTypes.Int16; + case ElementType.U2: return corTypes.UInt16; + case ElementType.I4: return corTypes.Int32; + case ElementType.U4: return corTypes.UInt32; + case ElementType.I8: return corTypes.Int64; + case ElementType.U8: return corTypes.UInt64; + case ElementType.R4: return corTypes.Single; + case ElementType.R8: return corTypes.Double; + case ElementType.String: return corTypes.Object; + case ElementType.TypedByRef: return corTypes.TypedReference; + case ElementType.I: return corTypes.IntPtr; + case ElementType.U: return corTypes.UIntPtr; + case ElementType.Object: return corTypes.Object; + case ElementType.Sentinel: return typeSig; + case ElementType.Ptr: return corTypes.UIntPtr; + case ElementType.ByRef: return corTypes.UIntPtr; + case ElementType.SZArray: return corTypes.Object; + case ElementType.Array: return corTypes.Object; + case ElementType.ValueType: + { + TypeDef typeDef = a.ToTypeDefOrRef().ResolveTypeDef(); + if (typeDef == null) + { + throw new Exception($"type:{a} definition could not be found"); + } + if (typeDef.IsEnum) + { + return ToShareTypeSig(corTypes, typeDef.GetEnumUnderlyingType()); + } + return typeSig; + } + case ElementType.Var: + case ElementType.MVar: + case ElementType.Class: return corTypes.Object; + case ElementType.GenericInst: + { + var gia = (GenericInstSig)a; + TypeDef typeDef = gia.GenericType.ToTypeDefOrRef().ResolveTypeDef(); + if (typeDef == null) + { + throw new Exception($"type:{a} definition could not be found"); + } + if (typeDef.IsEnum) + { + return ToShareTypeSig(corTypes, typeDef.GetEnumUnderlyingType()); + } + if (!typeDef.IsValueType) + { + return corTypes.Object; + } + return new GenericInstSig(gia.GenericType, gia.GenericArguments.Select(ga => ToShareTypeSig(corTypes, ga)).ToList()); + } + case ElementType.FnPtr: return corTypes.UIntPtr; + case ElementType.ValueArray: return typeSig; + case ElementType.Module: return typeSig; + default: + throw new NotSupportedException(typeSig.ToString()); + } + } } } diff --git a/Editor/Virtualization/DataVirtualizationPass.cs b/Editor/Virtualization/DataVirtualizationPass.cs index e6c65e0..755ae47 100644 --- a/Editor/Virtualization/DataVirtualizationPass.cs +++ b/Editor/Virtualization/DataVirtualizationPass.cs @@ -24,7 +24,7 @@ namespace Obfuz.Virtualization public override void Stop(ObfuscatorContext ctx) { - _dataObfuscator.Stop(); + _dataObfuscator.Done(); } protected override bool NeedObfuscateMethod(MethodDef method) diff --git a/Editor/Virtualization/DefaultDataObfuscator.cs b/Editor/Virtualization/DefaultDataObfuscator.cs index f5b168e..050214a 100644 --- a/Editor/Virtualization/DefaultDataObfuscator.cs +++ b/Editor/Virtualization/DefaultDataObfuscator.cs @@ -79,7 +79,7 @@ namespace Obfuz.Virtualization //obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldstr, value)); } - public void Stop() + public void Done() { _rvaDataAllocator.Done(); _constFieldAllocator.Done(); diff --git a/Editor/Virtualization/IDataObfuscator.cs b/Editor/Virtualization/IDataObfuscator.cs index 84e1821..175952b 100644 --- a/Editor/Virtualization/IDataObfuscator.cs +++ b/Editor/Virtualization/IDataObfuscator.cs @@ -19,6 +19,7 @@ namespace Obfuz.Virtualization void ObfuscateString(MethodDef method, string value, List obfuscatedInstructions); void ObfuscateBytes(MethodDef method, Array value, List obfuscatedInstructions); - void Stop(); + + void Done(); } }