CallObufs新增Delegate Proxy支持
parent
9b9eb6d12d
commit
4b0c5d7521
|
@ -303,7 +303,7 @@ namespace Obfuz.Data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Done()
|
public override void Done()
|
||||||
{
|
{
|
||||||
if (_done)
|
if (_done)
|
||||||
{
|
{
|
||||||
|
|
|
@ -248,6 +248,11 @@ namespace Obfuz.Emit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Done()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public EncryptionServiceMetadataImporter GetEncryptionServiceMetadataImporterOfModule(ModuleDef mod)
|
public EncryptionServiceMetadataImporter GetEncryptionServiceMetadataImporterOfModule(ModuleDef mod)
|
||||||
{
|
{
|
||||||
return _encryptionScopeProvider.IsDynamicSecretAssembly(mod) ? _dynamicDefaultEncryptionServiceMetadataImporter : _staticDefaultEncryptionServiceMetadataImporter;
|
return _encryptionScopeProvider.IsDynamicSecretAssembly(mod) ? _dynamicDefaultEncryptionServiceMetadataImporter : _staticDefaultEncryptionServiceMetadataImporter;
|
||||||
|
|
|
@ -270,7 +270,7 @@ namespace Obfuz.Emit
|
||||||
|
|
||||||
private void SimulateRunAllBlocks()
|
private void SimulateRunAllBlocks()
|
||||||
{
|
{
|
||||||
bool methodHasReturnValue = _method.ReturnType.RemovePinnedAndModifiers().ElementType != ElementType.Void;
|
bool methodHasReturnValue = !MetaUtil.IsVoidType(_method.ReturnType);
|
||||||
|
|
||||||
CilBody body = _method.Body;
|
CilBody body = _method.Body;
|
||||||
if (body.HasExceptionHandlers)
|
if (body.HasExceptionHandlers)
|
||||||
|
|
|
@ -7,11 +7,15 @@ namespace Obfuz.Emit
|
||||||
public interface IGroupByModuleEntity
|
public interface IGroupByModuleEntity
|
||||||
{
|
{
|
||||||
void Init(ModuleDef mod);
|
void Init(ModuleDef mod);
|
||||||
|
|
||||||
|
void Done();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class GroupByModuleEntityBase : IGroupByModuleEntity
|
public abstract class GroupByModuleEntityBase : IGroupByModuleEntity
|
||||||
{
|
{
|
||||||
public abstract void Init(ModuleDef mod);
|
public abstract void Init(ModuleDef mod);
|
||||||
|
|
||||||
|
public abstract void Done();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GroupByModuleEntityManager
|
public class GroupByModuleEntityManager
|
||||||
|
@ -55,6 +59,16 @@ namespace Obfuz.Emit
|
||||||
return managers;
|
return managers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Done<T>() where T : IGroupByModuleEntity
|
||||||
|
{
|
||||||
|
var managers = GetEntities<T>();
|
||||||
|
foreach (var manager in managers)
|
||||||
|
{
|
||||||
|
manager.Done();
|
||||||
|
}
|
||||||
|
_moduleEntityManagers.Remove((default(ModuleDef), typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
public DefaultMetadataImporter GetDefaultModuleMetadataImporter(ModuleDef module, EncryptionScopeProvider encryptionScopeProvider)
|
public DefaultMetadataImporter GetDefaultModuleMetadataImporter(ModuleDef module, EncryptionScopeProvider encryptionScopeProvider)
|
||||||
{
|
{
|
||||||
return GetEntity<DefaultMetadataImporter>(module, () => new DefaultMetadataImporter(encryptionScopeProvider));
|
return GetEntity<DefaultMetadataImporter>(module, () => new DefaultMetadataImporter(encryptionScopeProvider));
|
||||||
|
|
|
@ -11,6 +11,8 @@ namespace Obfuz.Emit
|
||||||
|
|
||||||
private readonly List<Local> _allocatedVars = new List<Local>();
|
private readonly List<Local> _allocatedVars = new List<Local>();
|
||||||
|
|
||||||
|
public IReadOnlyList<Local> AllocatedLocals => _allocatedVars;
|
||||||
|
|
||||||
|
|
||||||
public ScopeLocalVariables(LocalVariableAllocator localVariableAllocator)
|
public ScopeLocalVariables(LocalVariableAllocator localVariableAllocator)
|
||||||
{
|
{
|
||||||
|
@ -63,5 +65,10 @@ namespace Obfuz.Emit
|
||||||
{
|
{
|
||||||
_freeLocals.Add(local);
|
_freeLocals.Add(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScopeLocalVariables CreateScope()
|
||||||
|
{
|
||||||
|
return new ScopeLocalVariables(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,15 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Obfuz.ObfusPasses.CallObfus
|
namespace Obfuz.ObfusPasses.CallObfus
|
||||||
{
|
{
|
||||||
|
class ObfusMethodContext
|
||||||
|
{
|
||||||
|
public MethodDef method;
|
||||||
|
public LocalVariableAllocator localVariableAllocator;
|
||||||
|
public IRandom localRandom;
|
||||||
|
public EncryptionScopeInfo encryptionScope;
|
||||||
|
}
|
||||||
|
|
||||||
public class CallObfusPass : BasicBlockObfuscationPassBase
|
public class CallObfusPass : ObfuscationMethodPassBase
|
||||||
{
|
{
|
||||||
private readonly CallObfuscationSettingsFacade _settings;
|
private readonly CallObfuscationSettingsFacade _settings;
|
||||||
private readonly SpecialWhiteListMethodCalculator _specialWhiteListMethodCache;
|
private readonly SpecialWhiteListMethodCalculator _specialWhiteListMethodCache;
|
||||||
|
@ -32,17 +39,80 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
var ctx = ObfuscationPassContext.Current;
|
var ctx = ObfuscationPassContext.Current;
|
||||||
_dynamicProxyObfuscator = new DefaultCallProxyObfuscator(ctx.encryptionScopeProvider, ctx.constFieldAllocator, ctx.moduleEntityManager, _settings);
|
_dynamicProxyObfuscator = CreateObfuscator(ctx, _settings.proxyMode);
|
||||||
_dynamicProxyPolicy = new ConfigurableObfuscationPolicy(ctx.coreSettings.assembliesToObfuscate, _settings.ruleFiles);
|
_dynamicProxyPolicy = new ConfigurableObfuscationPolicy(ctx.coreSettings.assembliesToObfuscate, _settings.ruleFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IObfuscator CreateObfuscator(ObfuscationPassContext ctx, ProxyMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case ProxyMode.Dispatch:
|
||||||
|
return new DispatchProxyObfuscator(ctx.encryptionScopeProvider, ctx.constFieldAllocator, ctx.moduleEntityManager, _settings);
|
||||||
|
case ProxyMode.Delegate:
|
||||||
|
return new DelegateProxyObfuscator(ctx.encryptionScopeProvider, ctx.moduleEntityManager, ctx.rvaDataAllocator, _settings);
|
||||||
|
default:
|
||||||
|
throw new System.NotSupportedException($"Unsupported proxy mode: {mode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ObfuscateData(MethodDef method)
|
||||||
|
{
|
||||||
|
BasicBlockCollection bbc = new BasicBlockCollection(method, false);
|
||||||
|
|
||||||
|
IList<Instruction> instructions = method.Body.Instructions;
|
||||||
|
|
||||||
|
var outputInstructions = new List<Instruction>();
|
||||||
|
var totalFinalInstructions = new List<Instruction>();
|
||||||
|
|
||||||
|
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
|
||||||
|
var encryptionScope = ctx.encryptionScopeProvider.GetScope(method.Module);
|
||||||
|
var localRandom = encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method));
|
||||||
|
var omc = new ObfusMethodContext
|
||||||
|
{
|
||||||
|
method = method,
|
||||||
|
localVariableAllocator = new LocalVariableAllocator(method),
|
||||||
|
localRandom = localRandom,
|
||||||
|
encryptionScope = encryptionScope,
|
||||||
|
};
|
||||||
|
Instruction lastInst = null;
|
||||||
|
for (int i = 0; i < instructions.Count; i++)
|
||||||
|
{
|
||||||
|
Instruction inst = instructions[i];
|
||||||
|
BasicBlock block = bbc.GetBasicBlockByInstruction(inst);
|
||||||
|
outputInstructions.Clear();
|
||||||
|
if (TryObfuscateInstruction(method, lastInst, inst, outputInstructions, omc))
|
||||||
|
{
|
||||||
|
// current instruction may be the target of control flow instruction, so we can't remove it directly.
|
||||||
|
// we replace it with nop now, then remove it in CleanUpInstructionPass
|
||||||
|
inst.OpCode = outputInstructions[0].OpCode;
|
||||||
|
inst.Operand = outputInstructions[0].Operand;
|
||||||
|
totalFinalInstructions.Add(inst);
|
||||||
|
for (int k = 1; k < outputInstructions.Count; k++)
|
||||||
|
{
|
||||||
|
totalFinalInstructions.Add(outputInstructions[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
totalFinalInstructions.Add(inst);
|
||||||
|
}
|
||||||
|
lastInst = inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.Clear();
|
||||||
|
foreach (var obInst in totalFinalInstructions)
|
||||||
|
{
|
||||||
|
instructions.Add(obInst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool NeedObfuscateMethod(MethodDef method)
|
protected override bool NeedObfuscateMethod(MethodDef method)
|
||||||
{
|
{
|
||||||
return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method);
|
return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool TryObfuscateInstruction(MethodDef callerMethod, Instruction inst, BasicBlock block,
|
private bool TryObfuscateInstruction(MethodDef callerMethod, Instruction lastInst, Instruction inst, List<Instruction> outputInstructions, ObfusMethodContext ctx)
|
||||||
int instructionIndex, IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
|
|
||||||
{
|
{
|
||||||
IMethod calledMethod = inst.Operand as IMethod;
|
IMethod calledMethod = inst.Operand as IMethod;
|
||||||
if (calledMethod == null || !calledMethod.IsMethod)
|
if (calledMethod == null || !calledMethod.IsMethod)
|
||||||
|
@ -64,7 +134,7 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
}
|
}
|
||||||
case Code.Callvirt:
|
case Code.Callvirt:
|
||||||
{
|
{
|
||||||
if (instructionIndex > 0 && globalInstructions[instructionIndex - 1].OpCode.Code == Code.Constrained)
|
if (lastInst != null && lastInst.OpCode.Code == Code.Constrained)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -81,15 +151,12 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!_dynamicProxyPolicy.NeedObfuscateCalledMethod(callerMethod, calledMethod, callVir, block.inLoop))
|
if (!_dynamicProxyPolicy.NeedObfuscateCalledMethod(callerMethod, calledMethod, callVir))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObfuscationCachePolicy cachePolicy = _dynamicProxyPolicy.GetMethodObfuscationCachePolicy(callerMethod);
|
return _dynamicProxyObfuscator.Obfuscate(callerMethod, calledMethod, callVir, outputInstructions);
|
||||||
bool cachedCallIndex = block.inLoop ? cachePolicy.cacheInLoop : cachePolicy.cacheNotInLoop;
|
|
||||||
_dynamicProxyObfuscator.Obfuscate(callerMethod, calledMethod, callVir, cachedCallIndex, outputInstructions);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using dnlib.DotNet;
|
using dnlib.DotNet;
|
||||||
using Obfuz.Conf;
|
using Obfuz.Conf;
|
||||||
|
using Obfuz.Settings;
|
||||||
using Obfuz.Utils;
|
using Obfuz.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -34,29 +35,12 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
|
||||||
class ObfuscationRule : IRule<ObfuscationRule>
|
class ObfuscationRule : IRule<ObfuscationRule>
|
||||||
{
|
{
|
||||||
public bool? disableObfuscation;
|
public ObfuscationLevel? obfuscationLevel;
|
||||||
public bool? obfuscateCallInLoop;
|
|
||||||
public bool? cacheCallIndexInLoop;
|
|
||||||
public bool? cacheCallIndexNotLoop;
|
|
||||||
|
|
||||||
public void InheritParent(ObfuscationRule parentRule)
|
public void InheritParent(ObfuscationRule parentRule)
|
||||||
{
|
{
|
||||||
if (disableObfuscation == null)
|
if (obfuscationLevel == null)
|
||||||
{
|
obfuscationLevel = parentRule.obfuscationLevel;
|
||||||
disableObfuscation = parentRule.disableObfuscation;
|
|
||||||
}
|
|
||||||
if (obfuscateCallInLoop == null)
|
|
||||||
{
|
|
||||||
obfuscateCallInLoop = parentRule.obfuscateCallInLoop;
|
|
||||||
}
|
|
||||||
if (cacheCallIndexInLoop == null)
|
|
||||||
{
|
|
||||||
cacheCallIndexInLoop = parentRule.cacheCallIndexInLoop;
|
|
||||||
}
|
|
||||||
if (cacheCallIndexNotLoop == null)
|
|
||||||
{
|
|
||||||
cacheCallIndexNotLoop = parentRule.cacheCallIndexNotLoop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,10 +59,7 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
|
||||||
private static readonly ObfuscationRule s_default = new ObfuscationRule()
|
private static readonly ObfuscationRule s_default = new ObfuscationRule()
|
||||||
{
|
{
|
||||||
disableObfuscation = false,
|
obfuscationLevel = ObfuscationLevel.Basic,
|
||||||
obfuscateCallInLoop = true,
|
|
||||||
cacheCallIndexInLoop = true,
|
|
||||||
cacheCallIndexNotLoop = true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _configParser;
|
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _configParser;
|
||||||
|
@ -147,21 +128,9 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||||
{
|
{
|
||||||
var rule = new ObfuscationRule();
|
var rule = new ObfuscationRule();
|
||||||
if (ele.HasAttribute("disableObfuscation"))
|
if (ele.HasAttribute("obfuscationLevel"))
|
||||||
{
|
{
|
||||||
rule.disableObfuscation = ConfigUtil.ParseBool(ele.GetAttribute("disableObfuscation"));
|
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
||||||
}
|
|
||||||
if (ele.HasAttribute("obfuscateCallInLoop"))
|
|
||||||
{
|
|
||||||
rule.obfuscateCallInLoop = ConfigUtil.ParseBool(ele.GetAttribute("obfuscateCallInLoop"));
|
|
||||||
}
|
|
||||||
if (ele.HasAttribute("cacheCallIndexInLoop"))
|
|
||||||
{
|
|
||||||
rule.cacheCallIndexInLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheCallIndexInLoop"));
|
|
||||||
}
|
|
||||||
if (ele.HasAttribute("cacheCallIndexNotLoop"))
|
|
||||||
{
|
|
||||||
rule.cacheCallIndexNotLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheCallIndexNotLoop"));
|
|
||||||
}
|
}
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
@ -262,17 +231,7 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
public override bool NeedObfuscateCallInMethod(MethodDef method)
|
public override bool NeedObfuscateCallInMethod(MethodDef method)
|
||||||
{
|
{
|
||||||
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
||||||
return rule.disableObfuscation != true;
|
return rule.obfuscationLevel != null && rule.obfuscationLevel.Value >= ObfuscationLevel.Basic;
|
||||||
}
|
|
||||||
|
|
||||||
public override ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method)
|
|
||||||
{
|
|
||||||
ObfuscationRule rule = GetMethodObfuscationRule(method);
|
|
||||||
return new ObfuscationCachePolicy()
|
|
||||||
{
|
|
||||||
cacheInLoop = rule.cacheCallIndexInLoop.Value,
|
|
||||||
cacheNotInLoop = rule.cacheCallIndexNotLoop.Value,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ComputeIsInWhiteList(IMethod calledMethod)
|
private bool ComputeIsInWhiteList(IMethod calledMethod)
|
||||||
|
@ -329,18 +288,12 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop)
|
public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir)
|
||||||
{
|
{
|
||||||
if (_whiteListMethodCache.GetValue(calledMethod))
|
if (_whiteListMethodCache.GetValue(calledMethod))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObfuscationRule rule = GetMethodObfuscationRule(callerMethod);
|
|
||||||
if (currentInLoop && rule.obfuscateCallInLoop == false)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
using dnlib.DotNet;
|
|
||||||
using dnlib.DotNet.Emit;
|
|
||||||
using Obfuz.Data;
|
|
||||||
using Obfuz.Emit;
|
|
||||||
using Obfuz.Settings;
|
|
||||||
using Obfuz.Utils;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Obfuz.ObfusPasses.CallObfus
|
|
||||||
{
|
|
||||||
public class DefaultCallProxyObfuscator : ObfuscatorBase
|
|
||||||
{
|
|
||||||
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
|
||||||
private readonly ConstFieldAllocator _constFieldAllocator;
|
|
||||||
private readonly CallProxyAllocator _proxyCallAllocator;
|
|
||||||
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
|
||||||
|
|
||||||
public DefaultCallProxyObfuscator(EncryptionScopeProvider encryptionScopeProvider, ConstFieldAllocator constFieldAllocator, GroupByModuleEntityManager moduleEntityManager, CallObfuscationSettingsFacade settings)
|
|
||||||
{
|
|
||||||
_encryptionScopeProvider = encryptionScopeProvider;
|
|
||||||
_constFieldAllocator = constFieldAllocator;
|
|
||||||
_moduleEntityManager = moduleEntityManager;
|
|
||||||
_proxyCallAllocator = new CallProxyAllocator(encryptionScopeProvider, moduleEntityManager, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Done()
|
|
||||||
{
|
|
||||||
_proxyCallAllocator.Done();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Obfuscate(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions)
|
|
||||||
{
|
|
||||||
|
|
||||||
MethodSig sharedMethodSig = MetaUtil.ToSharedMethodSig(calledMethod.Module.CorLibTypes, MetaUtil.GetInflatedMethodSig(calledMethod, null));
|
|
||||||
ProxyCallMethodData proxyCallMethodData = _proxyCallAllocator.Allocate(callerMethod.Module, calledMethod, callVir);
|
|
||||||
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(callerMethod.Module, _encryptionScopeProvider);
|
|
||||||
|
|
||||||
if (needCacheCall)
|
|
||||||
{
|
|
||||||
FieldDef cacheField = _constFieldAllocator.Allocate(callerMethod.Module, proxyCallMethodData.index);
|
|
||||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptedIndex));
|
|
||||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptOps));
|
|
||||||
obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.salt));
|
|
||||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptInt));
|
|
||||||
}
|
|
||||||
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, proxyCallMethodData.proxyMethod));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,300 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Obfuz.Data;
|
||||||
|
using Obfuz.Emit;
|
||||||
|
using Obfuz.Settings;
|
||||||
|
using Obfuz.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
{
|
||||||
|
|
||||||
|
struct DelegateProxyMethodData
|
||||||
|
{
|
||||||
|
public readonly FieldDef delegateInstanceField;
|
||||||
|
public readonly MethodDef delegateInvokeMethod;
|
||||||
|
|
||||||
|
public DelegateProxyMethodData(FieldDef delegateInstanceField, MethodDef delegateInvokeMethod)
|
||||||
|
{
|
||||||
|
this.delegateInstanceField = delegateInstanceField;
|
||||||
|
this.delegateInvokeMethod = delegateInvokeMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleDelegateProxyAllocator : IGroupByModuleEntity
|
||||||
|
{
|
||||||
|
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
||||||
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
||||||
|
private readonly RvaDataAllocator _rvaDataAllocator;
|
||||||
|
private readonly CallObfuscationSettingsFacade _settings;
|
||||||
|
private readonly CachedDictionary<MethodSig, TypeDef> _delegateTypes;
|
||||||
|
private readonly HashSet<string> _allocatedDelegateNames = new HashSet<string>();
|
||||||
|
|
||||||
|
private ModuleDef _module;
|
||||||
|
|
||||||
|
private TypeDef _delegateInstanceHolderType;
|
||||||
|
private bool _done;
|
||||||
|
|
||||||
|
class CallInfo
|
||||||
|
{
|
||||||
|
public string key1;
|
||||||
|
public int key2;
|
||||||
|
public IMethod method;
|
||||||
|
public bool callVir;
|
||||||
|
|
||||||
|
public int index;
|
||||||
|
public TypeDef delegateType;
|
||||||
|
public FieldDef delegateInstanceField;
|
||||||
|
public MethodDef delegateInvokeMethod;
|
||||||
|
public MethodDef proxyMethod;
|
||||||
|
}
|
||||||
|
private readonly Dictionary<MethodKey, CallInfo> _callMethods = new Dictionary<MethodKey, CallInfo>();
|
||||||
|
|
||||||
|
public ModuleDelegateProxyAllocator(GroupByModuleEntityManager moduleEntityManager, EncryptionScopeProvider encryptionScopeProvider, RvaDataAllocator rvaDataAllocator, CallObfuscationSettingsFacade settings)
|
||||||
|
{
|
||||||
|
_moduleEntityManager = moduleEntityManager;
|
||||||
|
_encryptionScopeProvider = encryptionScopeProvider;
|
||||||
|
_rvaDataAllocator = rvaDataAllocator;
|
||||||
|
_settings = settings;
|
||||||
|
_delegateTypes = new CachedDictionary<MethodSig, TypeDef>(SignatureEqualityComparer.Instance, CreateDelegateForSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(ModuleDef mod)
|
||||||
|
{
|
||||||
|
_module = mod;
|
||||||
|
|
||||||
|
_delegateInstanceHolderType = CreateDelegateInstanceHolderTypeDef();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string AllocateDelegateTypeName(MethodSig delegateInvokeSig)
|
||||||
|
{
|
||||||
|
uint hashCode = (uint)SignatureEqualityComparer.Instance.GetHashCode(delegateInvokeSig);
|
||||||
|
string typeName = $"$Obfuz$Delegate_{hashCode}";
|
||||||
|
if (_allocatedDelegateNames.Add(typeName))
|
||||||
|
{
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
for (int i = 0; ;i++)
|
||||||
|
{
|
||||||
|
typeName = $"$Obfuz$Delegate_{hashCode}_{i}";
|
||||||
|
if (_allocatedDelegateNames.Add(typeName))
|
||||||
|
{
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeDef CreateDelegateForSignature(MethodSig delegateInvokeSig)
|
||||||
|
{
|
||||||
|
using (var scope = new DisableTypeDefFindCacheScope(_module))
|
||||||
|
{
|
||||||
|
|
||||||
|
string typeName = AllocateDelegateTypeName(delegateInvokeSig);
|
||||||
|
_module.Import(typeof(MulticastDelegate));
|
||||||
|
|
||||||
|
TypeDef delegateType = new TypeDefUser("", typeName, _module.CorLibTypes.GetTypeRef("System", "MulticastDelegate"));
|
||||||
|
delegateType.Attributes = TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public;
|
||||||
|
_module.Types.Add(delegateType);
|
||||||
|
|
||||||
|
MethodDef ctor = new MethodDefUser(
|
||||||
|
".ctor",
|
||||||
|
MethodSig.CreateInstance(_module.CorLibTypes.Void, _module.CorLibTypes.Object, _module.CorLibTypes.IntPtr),
|
||||||
|
MethodImplAttributes.Runtime,
|
||||||
|
MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Public
|
||||||
|
);
|
||||||
|
ctor.DeclaringType = delegateType;
|
||||||
|
|
||||||
|
|
||||||
|
MethodDef invokeMethod = new MethodDefUser(
|
||||||
|
"Invoke",
|
||||||
|
MethodSig.CreateInstance(delegateInvokeSig.RetType, delegateInvokeSig.Params.ToArray()),
|
||||||
|
MethodImplAttributes.Runtime,
|
||||||
|
MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Virtual
|
||||||
|
);
|
||||||
|
invokeMethod.DeclaringType = delegateType;
|
||||||
|
return delegateType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeDef CreateDelegateInstanceHolderTypeDef()
|
||||||
|
{
|
||||||
|
using (var scope = new DisableTypeDefFindCacheScope(_module))
|
||||||
|
{
|
||||||
|
string typeName = "$Obfuz$DelegateInstanceHolder";
|
||||||
|
TypeDef holderType = new TypeDefUser("", typeName, _module.CorLibTypes.Object.ToTypeDefOrRef());
|
||||||
|
holderType.Attributes = TypeAttributes.Class | TypeAttributes.Public;
|
||||||
|
_module.Types.Add(holderType);
|
||||||
|
return holderType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string AllocateFieldName(IMethod method, bool callVir)
|
||||||
|
{
|
||||||
|
uint hashCode = (uint)MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method);
|
||||||
|
string typeName = $"$Obfuz$Delegate$Field_{hashCode}_{callVir}";
|
||||||
|
if (_allocatedDelegateNames.Add(typeName))
|
||||||
|
{
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
for (int i = 0; ; i++)
|
||||||
|
{
|
||||||
|
typeName = $"$Obfuz$Delegate$Field_{hashCode}_{callVir}_{i}";
|
||||||
|
if (_allocatedDelegateNames.Add(typeName))
|
||||||
|
{
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodDef CreateProxyMethod(string name, IMethod calledMethod, bool callVir, MethodSig delegateInvokeSig)
|
||||||
|
{
|
||||||
|
var proxyMethod = new MethodDefUser(name, delegateInvokeSig, MethodImplAttributes.Managed, MethodAttributes.Public | MethodAttributes.Static);
|
||||||
|
var body = new CilBody();
|
||||||
|
proxyMethod.Body = body;
|
||||||
|
var ins = body.Instructions;
|
||||||
|
|
||||||
|
foreach (Parameter param in proxyMethod.Parameters)
|
||||||
|
{
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Ldarg, param));
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.Add(Instruction.Create(callVir ? OpCodes.Callvirt : OpCodes.Call, calledMethod));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
return proxyMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DelegateProxyMethodData Allocate(IMethod method, bool callVir, MethodSig delegateInvokeSig)
|
||||||
|
{
|
||||||
|
var key = new MethodKey(method, callVir);
|
||||||
|
if (!_callMethods.TryGetValue(key, out var callInfo))
|
||||||
|
{
|
||||||
|
TypeDef delegateType = _delegateTypes.GetValue(delegateInvokeSig);
|
||||||
|
MethodDef delegateInvokeMethod = delegateType.FindMethod("Invoke");
|
||||||
|
string fieldName = AllocateFieldName(method, callVir);
|
||||||
|
FieldDef delegateInstanceField = new FieldDefUser(fieldName, new FieldSig(delegateType.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);
|
||||||
|
string key1 = $"{method.FullName}_{callVir}";
|
||||||
|
callInfo = new CallInfo
|
||||||
|
{
|
||||||
|
key1 = key1,
|
||||||
|
key2 = HashUtil.ComputePrimitiveOrStringOrBytesHashCode(key1) * 33445566,
|
||||||
|
method = method,
|
||||||
|
callVir = callVir,
|
||||||
|
delegateType = delegateType,
|
||||||
|
delegateInstanceField = delegateInstanceField,
|
||||||
|
delegateInvokeMethod = delegateInvokeMethod,
|
||||||
|
proxyMethod = CreateProxyMethod($"{fieldName}$Proxy", method, callVir, delegateInvokeSig),
|
||||||
|
};
|
||||||
|
_callMethods.Add(key, callInfo);
|
||||||
|
}
|
||||||
|
return new DelegateProxyMethodData(callInfo.delegateInstanceField, callInfo.delegateInvokeMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Done()
|
||||||
|
{
|
||||||
|
if (_done)
|
||||||
|
{
|
||||||
|
throw new Exception("Already done");
|
||||||
|
}
|
||||||
|
_done = true;
|
||||||
|
|
||||||
|
// for stable order, we sort methods by name
|
||||||
|
List<CallInfo> callMethodList = _callMethods.Values.ToList();
|
||||||
|
callMethodList.Sort((a, b) => a.key1.CompareTo(b.key1));
|
||||||
|
|
||||||
|
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 = _delegateInstanceHolderType;
|
||||||
|
//_rvaTypeDef.Methods.Add(cctor);
|
||||||
|
var body = new CilBody();
|
||||||
|
cctor.Body = body;
|
||||||
|
var ins = body.Instructions;
|
||||||
|
|
||||||
|
// var arr = new array[];
|
||||||
|
// var d = new delegate;
|
||||||
|
// arr[index] = d;
|
||||||
|
int index = 0;
|
||||||
|
ins.Add(Instruction.CreateLdcI4(callMethodList.Count));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Newarr, _module.CorLibTypes.Object));
|
||||||
|
foreach (CallInfo ci in callMethodList)
|
||||||
|
{
|
||||||
|
ci.index = index;
|
||||||
|
_delegateInstanceHolderType.Methods.Add(ci.proxyMethod);
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Dup));
|
||||||
|
ins.Add(Instruction.CreateLdcI4(index));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Ldnull));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Ldftn, ci.proxyMethod));
|
||||||
|
MethodDef ctor = ci.delegateType.FindMethod(".ctor");
|
||||||
|
Assert.NotNull(ctor, $"Delegate type {ci.delegateType.FullName} does not have a constructor.");
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Newobj, ctor));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
List<CallInfo> callMethodList2 = callMethodList.ToList();
|
||||||
|
callMethodList2.Sort((a, b) => a.key2.CompareTo(b.key2));
|
||||||
|
|
||||||
|
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(_module);
|
||||||
|
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(_module, _encryptionScopeProvider);
|
||||||
|
foreach (CallInfo ci in callMethodList2)
|
||||||
|
{
|
||||||
|
_delegateInstanceHolderType.Fields.Add(ci.delegateInstanceField);
|
||||||
|
|
||||||
|
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Dup));
|
||||||
|
|
||||||
|
IRandom localRandom = encryptionScope.localRandomCreator(HashUtil.ComputePrimitiveOrStringOrBytesHashCode(ci.key1));
|
||||||
|
int ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, encryptionScope.encryptor, 4);
|
||||||
|
int salt = localRandom.NextInt();
|
||||||
|
|
||||||
|
int encryptedValue = encryptionScope.encryptor.Encrypt(ci.index, ops, salt);
|
||||||
|
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
|
||||||
|
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
|
||||||
|
ins.Add(Instruction.CreateLdcI4(ops));
|
||||||
|
ins.Add(Instruction.CreateLdcI4(salt));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Ldelem_Ref));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Stsfld, ci.delegateInstanceField));
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Pop));
|
||||||
|
ins.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DelegateProxyAllocator
|
||||||
|
{
|
||||||
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
||||||
|
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
||||||
|
private readonly CallObfuscationSettingsFacade _settings;
|
||||||
|
private readonly RvaDataAllocator _rvaDataAllocator;
|
||||||
|
|
||||||
|
public DelegateProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, RvaDataAllocator rvaDataAllocator, CallObfuscationSettingsFacade settings)
|
||||||
|
{
|
||||||
|
_encryptionScopeProvider = encryptionScopeProvider;
|
||||||
|
_moduleEntityManager = moduleEntityManager;
|
||||||
|
_rvaDataAllocator = rvaDataAllocator;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModuleDelegateProxyAllocator GetModuleAllocator(ModuleDef mod)
|
||||||
|
{
|
||||||
|
return _moduleEntityManager.GetEntity<ModuleDelegateProxyAllocator>(mod, () => new ModuleDelegateProxyAllocator(_moduleEntityManager, _encryptionScopeProvider, _rvaDataAllocator, _settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Done()
|
||||||
|
{
|
||||||
|
foreach (var allocator in _moduleEntityManager.GetEntities<ModuleDelegateProxyAllocator>())
|
||||||
|
{
|
||||||
|
allocator.Done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 02761bacbed8a8b489ae3e7f49f0f84a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,83 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using Obfuz.Data;
|
||||||
|
using Obfuz.Emit;
|
||||||
|
using Obfuz.Settings;
|
||||||
|
using Obfuz.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
{
|
||||||
|
|
||||||
|
public class DelegateProxyObfuscator : ObfuscatorBase
|
||||||
|
{
|
||||||
|
private readonly DelegateProxyAllocator _delegateProxyAllocator;
|
||||||
|
|
||||||
|
public DelegateProxyObfuscator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, RvaDataAllocator rvaDataAllocator, CallObfuscationSettingsFacade settings)
|
||||||
|
{
|
||||||
|
_delegateProxyAllocator = new DelegateProxyAllocator(encryptionScopeProvider, moduleEntityManager, rvaDataAllocator, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Done()
|
||||||
|
{
|
||||||
|
_delegateProxyAllocator.Done();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodSig CreateProxyMethodSig(ModuleDef module, IMethod method)
|
||||||
|
{
|
||||||
|
MethodSig methodSig = MetaUtil.ToSharedMethodSig(module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method, null));
|
||||||
|
//MethodSig methodSig = MetaUtil.GetInflatedMethodSig(method).Clone();
|
||||||
|
//methodSig.Params
|
||||||
|
switch (MetaUtil.GetThisArgType(method))
|
||||||
|
{
|
||||||
|
case ThisArgType.Class:
|
||||||
|
{
|
||||||
|
methodSig.Params.Insert(0, module.CorLibTypes.Object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ThisArgType.ValueType:
|
||||||
|
{
|
||||||
|
methodSig.Params.Insert(0, module.CorLibTypes.IntPtr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MethodSig.CreateStatic(methodSig.RetType, methodSig.Params.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions)
|
||||||
|
{
|
||||||
|
ModuleDelegateProxyAllocator allocator = _delegateProxyAllocator.GetModuleAllocator(callingMethod.Module);
|
||||||
|
LocalVariableAllocator localVarAllocator = new LocalVariableAllocator(callingMethod);
|
||||||
|
MethodSig methodSig = CreateProxyMethodSig(callingMethod.Module, calledMethod);
|
||||||
|
DelegateProxyMethodData proxyData = allocator.Allocate(calledMethod, callVir, methodSig);
|
||||||
|
bool isVoidReturn = MetaUtil.IsVoidType(methodSig.RetType);
|
||||||
|
|
||||||
|
using (var varScope = localVarAllocator.CreateScope())
|
||||||
|
{
|
||||||
|
List<Local> localVars = new List<Local>();
|
||||||
|
if (!isVoidReturn)
|
||||||
|
{
|
||||||
|
varScope.AllocateLocal(methodSig.RetType);
|
||||||
|
}
|
||||||
|
foreach (var p in methodSig.Params)
|
||||||
|
{
|
||||||
|
localVars.Add(varScope.AllocateLocal(p));
|
||||||
|
}
|
||||||
|
// save args
|
||||||
|
for (int i = localVars.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Stloc, localVars[i]));
|
||||||
|
}
|
||||||
|
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, proxyData.delegateInstanceField));
|
||||||
|
foreach (var local in localVars)
|
||||||
|
{
|
||||||
|
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldloc, local));
|
||||||
|
}
|
||||||
|
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Callvirt, proxyData.delegateInvokeMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1102cd9f03de27c4b9fde3d6a87277c7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -12,6 +12,7 @@ using TypeAttributes = dnlib.DotNet.TypeAttributes;
|
||||||
|
|
||||||
namespace Obfuz.ObfusPasses.CallObfus
|
namespace Obfuz.ObfusPasses.CallObfus
|
||||||
{
|
{
|
||||||
|
|
||||||
public struct ProxyCallMethodData
|
public struct ProxyCallMethodData
|
||||||
{
|
{
|
||||||
public readonly MethodDef proxyMethod;
|
public readonly MethodDef proxyMethod;
|
||||||
|
@ -30,7 +31,7 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModuleCallProxyAllocator : IGroupByModuleEntity
|
class ModuleDispatchProxyAllocator : IGroupByModuleEntity
|
||||||
{
|
{
|
||||||
private ModuleDef _module;
|
private ModuleDef _module;
|
||||||
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
||||||
|
@ -39,29 +40,6 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
private EncryptionScopeInfo _encryptionScope;
|
private EncryptionScopeInfo _encryptionScope;
|
||||||
private bool _done;
|
private bool _done;
|
||||||
|
|
||||||
class MethodKey : IEquatable<MethodKey>
|
|
||||||
{
|
|
||||||
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
|
class MethodProxyInfo
|
||||||
{
|
{
|
||||||
|
@ -93,7 +71,7 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
|
||||||
private TypeDef _proxyTypeDef;
|
private TypeDef _proxyTypeDef;
|
||||||
|
|
||||||
public ModuleCallProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, CallObfuscationSettingsFacade settings)
|
public ModuleDispatchProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, CallObfuscationSettingsFacade settings)
|
||||||
{
|
{
|
||||||
_encryptionScopeProvider = encryptionScopeProvider;
|
_encryptionScopeProvider = encryptionScopeProvider;
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
|
@ -298,33 +276,33 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CallProxyAllocator
|
public class DispatchProxyAllocator
|
||||||
{
|
{
|
||||||
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
||||||
private GroupByModuleEntityManager _moduleEntityManager;
|
private GroupByModuleEntityManager _moduleEntityManager;
|
||||||
private readonly CallObfuscationSettingsFacade _settings;
|
private readonly CallObfuscationSettingsFacade _settings;
|
||||||
|
|
||||||
public CallProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, CallObfuscationSettingsFacade settings)
|
public DispatchProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, CallObfuscationSettingsFacade settings)
|
||||||
{
|
{
|
||||||
_encryptionScopeProvider = encryptionScopeProvider;
|
_encryptionScopeProvider = encryptionScopeProvider;
|
||||||
_moduleEntityManager = moduleEntityManager;
|
_moduleEntityManager = moduleEntityManager;
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModuleCallProxyAllocator GetModuleAllocator(ModuleDef mod)
|
private ModuleDispatchProxyAllocator GetModuleAllocator(ModuleDef mod)
|
||||||
{
|
{
|
||||||
return _moduleEntityManager.GetEntity<ModuleCallProxyAllocator>(mod, () => new ModuleCallProxyAllocator(_encryptionScopeProvider, _settings));
|
return _moduleEntityManager.GetEntity<ModuleDispatchProxyAllocator>(mod, () => new ModuleDispatchProxyAllocator(_encryptionScopeProvider, _settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProxyCallMethodData Allocate(ModuleDef mod, IMethod method, bool callVir)
|
public ProxyCallMethodData Allocate(ModuleDef mod, IMethod method, bool callVir)
|
||||||
{
|
{
|
||||||
ModuleCallProxyAllocator allocator = GetModuleAllocator(mod);
|
ModuleDispatchProxyAllocator allocator = GetModuleAllocator(mod);
|
||||||
return allocator.Allocate(method, callVir);
|
return allocator.Allocate(method, callVir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Done()
|
public void Done()
|
||||||
{
|
{
|
||||||
foreach (var allocator in _moduleEntityManager.GetEntities<ModuleCallProxyAllocator>())
|
foreach (var allocator in _moduleEntityManager.GetEntities<ModuleDispatchProxyAllocator>())
|
||||||
{
|
{
|
||||||
allocator.Done();
|
allocator.Done();
|
||||||
}
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using Obfuz.Data;
|
||||||
|
using Obfuz.Emit;
|
||||||
|
using Obfuz.Settings;
|
||||||
|
using Obfuz.Utils;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
{
|
||||||
|
|
||||||
|
public class DispatchProxyObfuscator : ObfuscatorBase
|
||||||
|
{
|
||||||
|
private readonly EncryptionScopeProvider _encryptionScopeProvider;
|
||||||
|
private readonly ConstFieldAllocator _constFieldAllocator;
|
||||||
|
private readonly DispatchProxyAllocator _proxyCallAllocator;
|
||||||
|
private readonly GroupByModuleEntityManager _moduleEntityManager;
|
||||||
|
|
||||||
|
public DispatchProxyObfuscator(EncryptionScopeProvider encryptionScopeProvider, ConstFieldAllocator constFieldAllocator, GroupByModuleEntityManager moduleEntityManager, CallObfuscationSettingsFacade settings)
|
||||||
|
{
|
||||||
|
_encryptionScopeProvider = encryptionScopeProvider;
|
||||||
|
_constFieldAllocator = constFieldAllocator;
|
||||||
|
_moduleEntityManager = moduleEntityManager;
|
||||||
|
_proxyCallAllocator = new DispatchProxyAllocator(encryptionScopeProvider, moduleEntityManager, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Done()
|
||||||
|
{
|
||||||
|
_proxyCallAllocator.Done();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Obfuscate(MethodDef callerMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions)
|
||||||
|
{
|
||||||
|
|
||||||
|
MethodSig sharedMethodSig = MetaUtil.ToSharedMethodSig(calledMethod.Module.CorLibTypes, MetaUtil.GetInflatedMethodSig(calledMethod, null));
|
||||||
|
ProxyCallMethodData proxyCallMethodData = _proxyCallAllocator.Allocate(callerMethod.Module, calledMethod, callVir);
|
||||||
|
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(callerMethod.Module, _encryptionScopeProvider);
|
||||||
|
|
||||||
|
//if (needCacheCall)
|
||||||
|
//{
|
||||||
|
// FieldDef cacheField = _constFieldAllocator.Allocate(callerMethod.Module, proxyCallMethodData.index);
|
||||||
|
// obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptedIndex));
|
||||||
|
// obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptOps));
|
||||||
|
// obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.salt));
|
||||||
|
// obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptInt));
|
||||||
|
//}
|
||||||
|
|
||||||
|
FieldDef cacheField = _constFieldAllocator.Allocate(callerMethod.Module, proxyCallMethodData.index);
|
||||||
|
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
|
||||||
|
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, proxyCallMethodData.proxyMethod));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,27 +3,17 @@
|
||||||
namespace Obfuz.ObfusPasses.CallObfus
|
namespace Obfuz.ObfusPasses.CallObfus
|
||||||
{
|
{
|
||||||
|
|
||||||
public struct ObfuscationCachePolicy
|
|
||||||
{
|
|
||||||
public bool cacheInLoop;
|
|
||||||
public bool cacheNotInLoop;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IObfuscationPolicy
|
public interface IObfuscationPolicy
|
||||||
{
|
{
|
||||||
bool NeedObfuscateCallInMethod(MethodDef method);
|
bool NeedObfuscateCallInMethod(MethodDef method);
|
||||||
|
|
||||||
ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method);
|
bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir);
|
||||||
|
|
||||||
bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ObfuscationPolicyBase : IObfuscationPolicy
|
public abstract class ObfuscationPolicyBase : IObfuscationPolicy
|
||||||
{
|
{
|
||||||
public abstract bool NeedObfuscateCallInMethod(MethodDef method);
|
public abstract bool NeedObfuscateCallInMethod(MethodDef method);
|
||||||
|
|
||||||
public abstract ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method);
|
public abstract bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir);
|
||||||
|
|
||||||
public abstract bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,15 @@ namespace Obfuz.ObfusPasses.CallObfus
|
||||||
{
|
{
|
||||||
public interface IObfuscator
|
public interface IObfuscator
|
||||||
{
|
{
|
||||||
void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions);
|
bool Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions);
|
||||||
|
|
||||||
void Done();
|
void Done();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ObfuscatorBase : IObfuscator
|
public abstract class ObfuscatorBase : IObfuscator
|
||||||
{
|
{
|
||||||
public abstract void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions);
|
public abstract bool Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions);
|
||||||
|
|
||||||
public abstract void Done();
|
public abstract void Done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using Obfuz.Utils;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Obfuz.ObfusPasses.CallObfus
|
||||||
|
{
|
||||||
|
class MethodKey : IEquatable<MethodKey>
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1193647b317b56f4b83aa080d0a17f7a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -1,6 +1,7 @@
|
||||||
using dnlib.DotNet;
|
using dnlib.DotNet;
|
||||||
using Obfuz.Conf;
|
using Obfuz.Conf;
|
||||||
using Obfuz.Settings;
|
using Obfuz.Settings;
|
||||||
|
using Obfuz.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
@ -97,17 +98,12 @@ namespace Obfuz.ObfusPasses.ControlFlowObfus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObfuscationLevel ParseObfuscationLevel(string str)
|
|
||||||
{
|
|
||||||
return (ObfuscationLevel)Enum.Parse(typeof(ObfuscationLevel), str);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||||
{
|
{
|
||||||
var rule = new ObfuscationRule();
|
var rule = new ObfuscationRule();
|
||||||
if (ele.HasAttribute("obfuscationLevel"))
|
if (ele.HasAttribute("obfuscationLevel"))
|
||||||
{
|
{
|
||||||
rule.obfuscationLevel = ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
||||||
}
|
}
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using dnlib.DotNet;
|
using dnlib.DotNet;
|
||||||
using Obfuz.Conf;
|
using Obfuz.Conf;
|
||||||
using Obfuz.Settings;
|
using Obfuz.Settings;
|
||||||
|
using Obfuz.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
@ -103,17 +104,12 @@ namespace Obfuz.ObfusPasses.EvalStackObfus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObfuscationLevel ParseObfuscationLevel(string str)
|
|
||||||
{
|
|
||||||
return (ObfuscationLevel)Enum.Parse(typeof(ObfuscationLevel), str);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||||
{
|
{
|
||||||
var rule = new ObfuscationRule();
|
var rule = new ObfuscationRule();
|
||||||
if (ele.HasAttribute("obfuscationLevel"))
|
if (ele.HasAttribute("obfuscationLevel"))
|
||||||
{
|
{
|
||||||
rule.obfuscationLevel = ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
||||||
}
|
}
|
||||||
if (ele.HasAttribute("obfuscationPercentage"))
|
if (ele.HasAttribute("obfuscationPercentage"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using dnlib.DotNet;
|
using dnlib.DotNet;
|
||||||
using Obfuz.Conf;
|
using Obfuz.Conf;
|
||||||
using Obfuz.Settings;
|
using Obfuz.Settings;
|
||||||
|
using Obfuz.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
@ -103,17 +104,12 @@ namespace Obfuz.ObfusPasses.ExprObfus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObfuscationLevel ParseObfuscationLevel(string str)
|
|
||||||
{
|
|
||||||
return (ObfuscationLevel)Enum.Parse(typeof(ObfuscationLevel), str);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
|
||||||
{
|
{
|
||||||
var rule = new ObfuscationRule();
|
var rule = new ObfuscationRule();
|
||||||
if (ele.HasAttribute("obfuscationLevel"))
|
if (ele.HasAttribute("obfuscationLevel"))
|
||||||
{
|
{
|
||||||
rule.obfuscationLevel = ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
|
||||||
}
|
}
|
||||||
if (ele.HasAttribute("obfuscationPercentage"))
|
if (ele.HasAttribute("obfuscationPercentage"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,17 +5,26 @@ using UnityEngine;
|
||||||
|
|
||||||
namespace Obfuz.Settings
|
namespace Obfuz.Settings
|
||||||
{
|
{
|
||||||
|
public enum ProxyMode
|
||||||
|
{
|
||||||
|
Dispatch,
|
||||||
|
Delegate,
|
||||||
|
}
|
||||||
|
|
||||||
public class CallObfuscationSettingsFacade
|
public class CallObfuscationSettingsFacade
|
||||||
{
|
{
|
||||||
public List<string> ruleFiles;
|
public ProxyMode proxyMode;
|
||||||
public int obfuscationLevel;
|
public int obfuscationLevel;
|
||||||
public int maxProxyMethodCountPerDispatchMethod;
|
public int maxProxyMethodCountPerDispatchMethod;
|
||||||
public bool obfuscateCallToMethodInMscorlib;
|
public bool obfuscateCallToMethodInMscorlib;
|
||||||
|
public List<string> ruleFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class CallObfuscationSettings
|
public class CallObfuscationSettings
|
||||||
{
|
{
|
||||||
|
public ProxyMode proxyMode = ProxyMode.Dispatch;
|
||||||
|
|
||||||
[Tooltip("The obfuscation level for the obfuscation. Higher levels provide more security but may impact performance.")]
|
[Tooltip("The obfuscation level for the obfuscation. Higher levels provide more security but may impact performance.")]
|
||||||
[Range(1, 4)]
|
[Range(1, 4)]
|
||||||
public int obfuscationLevel = 1;
|
public int obfuscationLevel = 1;
|
||||||
|
@ -23,7 +32,7 @@ namespace Obfuz.Settings
|
||||||
[Tooltip("The maximum number of proxy methods that can be generated per dispatch method. This helps to limit the complexity of the generated code and improve performance.")]
|
[Tooltip("The maximum number of proxy methods that can be generated per dispatch method. This helps to limit the complexity of the generated code and improve performance.")]
|
||||||
public int maxProxyMethodCountPerDispatchMethod = 100;
|
public int maxProxyMethodCountPerDispatchMethod = 100;
|
||||||
|
|
||||||
[Tooltip("Whether to obfuscate calls to methods in mscorlib. This can help to protect against reverse engineering, but may cause compatibility issues with some libraries.")]
|
[Tooltip("Whether to obfuscate calls to methods in mscorlib. Enable this option will impact performance.")]
|
||||||
public bool obfuscateCallToMethodInMscorlib;
|
public bool obfuscateCallToMethodInMscorlib;
|
||||||
|
|
||||||
[Tooltip("rule config xml files")]
|
[Tooltip("rule config xml files")]
|
||||||
|
@ -33,10 +42,11 @@ namespace Obfuz.Settings
|
||||||
{
|
{
|
||||||
return new CallObfuscationSettingsFacade
|
return new CallObfuscationSettingsFacade
|
||||||
{
|
{
|
||||||
ruleFiles = ruleFiles?.ToList() ?? new List<string>(),
|
proxyMode = proxyMode,
|
||||||
obfuscationLevel = obfuscationLevel,
|
obfuscationLevel = obfuscationLevel,
|
||||||
maxProxyMethodCountPerDispatchMethod = maxProxyMethodCountPerDispatchMethod,
|
maxProxyMethodCountPerDispatchMethod = maxProxyMethodCountPerDispatchMethod,
|
||||||
obfuscateCallToMethodInMscorlib = obfuscateCallToMethodInMscorlib,
|
obfuscateCallToMethodInMscorlib = obfuscateCallToMethodInMscorlib,
|
||||||
|
ruleFiles = ruleFiles?.ToList() ?? new List<string>(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using Obfuz.Settings;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Obfuz.Utils
|
namespace Obfuz.Utils
|
||||||
{
|
{
|
||||||
|
@ -68,5 +69,10 @@ namespace Obfuz.Utils
|
||||||
}
|
}
|
||||||
return double.Parse(str);
|
return double.Parse(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ObfuscationLevel ParseObfuscationLevel(string str)
|
||||||
|
{
|
||||||
|
return (ObfuscationLevel)Enum.Parse(typeof(ObfuscationLevel), str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ namespace Obfuz.Utils
|
||||||
return ("", fullName.Substring(index + 1));
|
return ("", fullName.Substring(index + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsVoidType(TypeSig type)
|
||||||
|
{
|
||||||
|
return type.RemovePinnedAndModifiers().ElementType == ElementType.Void;
|
||||||
|
}
|
||||||
|
|
||||||
public static TypeDef GetBaseTypeDef(TypeDef type)
|
public static TypeDef GetBaseTypeDef(TypeDef type)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue