CallObufs新增Delegate Proxy支持

main
walon 2025-07-01 18:46:09 +08:00
parent 9b9eb6d12d
commit 4b0c5d7521
26 changed files with 662 additions and 188 deletions

View File

@ -303,7 +303,7 @@ namespace Obfuz.Data
}
}
public void Done()
public override void Done()
{
if (_done)
{

View File

@ -248,6 +248,11 @@ namespace Obfuz.Emit
}
}
public override void Done()
{
}
public EncryptionServiceMetadataImporter GetEncryptionServiceMetadataImporterOfModule(ModuleDef mod)
{
return _encryptionScopeProvider.IsDynamicSecretAssembly(mod) ? _dynamicDefaultEncryptionServiceMetadataImporter : _staticDefaultEncryptionServiceMetadataImporter;

View File

@ -270,7 +270,7 @@ namespace Obfuz.Emit
private void SimulateRunAllBlocks()
{
bool methodHasReturnValue = _method.ReturnType.RemovePinnedAndModifiers().ElementType != ElementType.Void;
bool methodHasReturnValue = !MetaUtil.IsVoidType(_method.ReturnType);
CilBody body = _method.Body;
if (body.HasExceptionHandlers)

View File

@ -7,11 +7,15 @@ namespace Obfuz.Emit
public interface IGroupByModuleEntity
{
void Init(ModuleDef mod);
void Done();
}
public abstract class GroupByModuleEntityBase : IGroupByModuleEntity
{
public abstract void Init(ModuleDef mod);
public abstract void Done();
}
public class GroupByModuleEntityManager
@ -55,6 +59,16 @@ namespace Obfuz.Emit
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)
{
return GetEntity<DefaultMetadataImporter>(module, () => new DefaultMetadataImporter(encryptionScopeProvider));

View File

@ -11,6 +11,8 @@ namespace Obfuz.Emit
private readonly List<Local> _allocatedVars = new List<Local>();
public IReadOnlyList<Local> AllocatedLocals => _allocatedVars;
public ScopeLocalVariables(LocalVariableAllocator localVariableAllocator)
{
@ -63,5 +65,10 @@ namespace Obfuz.Emit
{
_freeLocals.Add(local);
}
public ScopeLocalVariables CreateScope()
{
return new ScopeLocalVariables(this);
}
}
}

View File

@ -7,8 +7,15 @@ using System.Collections.Generic;
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 SpecialWhiteListMethodCalculator _specialWhiteListMethodCache;
@ -32,17 +39,80 @@ namespace Obfuz.ObfusPasses.CallObfus
public override void Start()
{
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);
}
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)
{
return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method);
}
protected override bool TryObfuscateInstruction(MethodDef callerMethod, Instruction inst, BasicBlock block,
int instructionIndex, IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
private bool TryObfuscateInstruction(MethodDef callerMethod, Instruction lastInst, Instruction inst, List<Instruction> outputInstructions, ObfusMethodContext ctx)
{
IMethod calledMethod = inst.Operand as IMethod;
if (calledMethod == null || !calledMethod.IsMethod)
@ -64,7 +134,7 @@ namespace Obfuz.ObfusPasses.CallObfus
}
case Code.Callvirt:
{
if (instructionIndex > 0 && globalInstructions[instructionIndex - 1].OpCode.Code == Code.Constrained)
if (lastInst != null && lastInst.OpCode.Code == Code.Constrained)
{
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;
}
ObfuscationCachePolicy cachePolicy = _dynamicProxyPolicy.GetMethodObfuscationCachePolicy(callerMethod);
bool cachedCallIndex = block.inLoop ? cachePolicy.cacheInLoop : cachePolicy.cacheNotInLoop;
_dynamicProxyObfuscator.Obfuscate(callerMethod, calledMethod, callVir, cachedCallIndex, outputInstructions);
return true;
return _dynamicProxyObfuscator.Obfuscate(callerMethod, calledMethod, callVir, outputInstructions);
}
}
}

View File

@ -1,5 +1,6 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Settings;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
@ -34,29 +35,12 @@ namespace Obfuz.ObfusPasses.CallObfus
class ObfuscationRule : IRule<ObfuscationRule>
{
public bool? disableObfuscation;
public bool? obfuscateCallInLoop;
public bool? cacheCallIndexInLoop;
public bool? cacheCallIndexNotLoop;
public ObfuscationLevel? obfuscationLevel;
public void InheritParent(ObfuscationRule parentRule)
{
if (disableObfuscation == null)
{
disableObfuscation = parentRule.disableObfuscation;
}
if (obfuscateCallInLoop == null)
{
obfuscateCallInLoop = parentRule.obfuscateCallInLoop;
}
if (cacheCallIndexInLoop == null)
{
cacheCallIndexInLoop = parentRule.cacheCallIndexInLoop;
}
if (cacheCallIndexNotLoop == null)
{
cacheCallIndexNotLoop = parentRule.cacheCallIndexNotLoop;
}
if (obfuscationLevel == null)
obfuscationLevel = parentRule.obfuscationLevel;
}
}
@ -75,10 +59,7 @@ namespace Obfuz.ObfusPasses.CallObfus
private static readonly ObfuscationRule s_default = new ObfuscationRule()
{
disableObfuscation = false,
obfuscateCallInLoop = true,
cacheCallIndexInLoop = true,
cacheCallIndexNotLoop = true,
obfuscationLevel = ObfuscationLevel.Basic,
};
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _configParser;
@ -147,21 +128,9 @@ namespace Obfuz.ObfusPasses.CallObfus
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
{
var rule = new ObfuscationRule();
if (ele.HasAttribute("disableObfuscation"))
if (ele.HasAttribute("obfuscationLevel"))
{
rule.disableObfuscation = ConfigUtil.ParseBool(ele.GetAttribute("disableObfuscation"));
}
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"));
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
}
return rule;
}
@ -262,17 +231,7 @@ namespace Obfuz.ObfusPasses.CallObfus
public override bool NeedObfuscateCallInMethod(MethodDef method)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
return rule.disableObfuscation != true;
}
public override ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
return new ObfuscationCachePolicy()
{
cacheInLoop = rule.cacheCallIndexInLoop.Value,
cacheNotInLoop = rule.cacheCallIndexNotLoop.Value,
};
return rule.obfuscationLevel != null && rule.obfuscationLevel.Value >= ObfuscationLevel.Basic;
}
private bool ComputeIsInWhiteList(IMethod calledMethod)
@ -329,18 +288,12 @@ namespace Obfuz.ObfusPasses.CallObfus
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))
{
return false;
}
ObfuscationRule rule = GetMethodObfuscationRule(callerMethod);
if (currentInLoop && rule.obfuscateCallInLoop == false)
{
return false;
}
return true;
}
}

View File

@ -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));
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02761bacbed8a8b489ae3e7f49f0f84a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1102cd9f03de27c4b9fde3d6a87277c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -12,6 +12,7 @@ using TypeAttributes = dnlib.DotNet.TypeAttributes;
namespace Obfuz.ObfusPasses.CallObfus
{
public struct ProxyCallMethodData
{
public readonly MethodDef proxyMethod;
@ -30,7 +31,7 @@ namespace Obfuz.ObfusPasses.CallObfus
}
}
class ModuleCallProxyAllocator : IGroupByModuleEntity
class ModuleDispatchProxyAllocator : IGroupByModuleEntity
{
private ModuleDef _module;
private readonly EncryptionScopeProvider _encryptionScopeProvider;
@ -39,29 +40,6 @@ namespace Obfuz.ObfusPasses.CallObfus
private EncryptionScopeInfo _encryptionScope;
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
{
@ -93,7 +71,7 @@ namespace Obfuz.ObfusPasses.CallObfus
private TypeDef _proxyTypeDef;
public ModuleCallProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, CallObfuscationSettingsFacade settings)
public ModuleDispatchProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, CallObfuscationSettingsFacade settings)
{
_encryptionScopeProvider = encryptionScopeProvider;
_settings = settings;
@ -298,33 +276,33 @@ namespace Obfuz.ObfusPasses.CallObfus
}
}
public class CallProxyAllocator
public class DispatchProxyAllocator
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private GroupByModuleEntityManager _moduleEntityManager;
private readonly CallObfuscationSettingsFacade _settings;
public CallProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, CallObfuscationSettingsFacade settings)
public DispatchProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, CallObfuscationSettingsFacade settings)
{
_encryptionScopeProvider = encryptionScopeProvider;
_moduleEntityManager = moduleEntityManager;
_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)
{
ModuleCallProxyAllocator allocator = GetModuleAllocator(mod);
ModuleDispatchProxyAllocator allocator = GetModuleAllocator(mod);
return allocator.Allocate(method, callVir);
}
public void Done()
{
foreach (var allocator in _moduleEntityManager.GetEntities<ModuleCallProxyAllocator>())
foreach (var allocator in _moduleEntityManager.GetEntities<ModuleDispatchProxyAllocator>())
{
allocator.Done();
}

View File

@ -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;
}
}
}

View File

@ -3,27 +3,17 @@
namespace Obfuz.ObfusPasses.CallObfus
{
public struct ObfuscationCachePolicy
{
public bool cacheInLoop;
public bool cacheNotInLoop;
}
public interface IObfuscationPolicy
{
bool NeedObfuscateCallInMethod(MethodDef method);
ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method);
bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop);
bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir);
}
public abstract class ObfuscationPolicyBase : IObfuscationPolicy
{
public abstract bool NeedObfuscateCallInMethod(MethodDef method);
public abstract ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method);
public abstract bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop);
public abstract bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir);
}
}

View File

@ -6,14 +6,15 @@ namespace Obfuz.ObfusPasses.CallObfus
{
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();
}
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();
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1193647b317b56f4b83aa080d0a17f7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,6 +1,7 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Settings;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
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)
{
var rule = new ObfuscationRule();
if (ele.HasAttribute("obfuscationLevel"))
{
rule.obfuscationLevel = ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
}
return rule;
}

View File

@ -1,6 +1,7 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Settings;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
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)
{
var rule = new ObfuscationRule();
if (ele.HasAttribute("obfuscationLevel"))
{
rule.obfuscationLevel = ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
}
if (ele.HasAttribute("obfuscationPercentage"))
{

View File

@ -1,6 +1,7 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Settings;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
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)
{
var rule = new ObfuscationRule();
if (ele.HasAttribute("obfuscationLevel"))
{
rule.obfuscationLevel = ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
}
if (ele.HasAttribute("obfuscationPercentage"))
{

View File

@ -5,17 +5,26 @@ using UnityEngine;
namespace Obfuz.Settings
{
public enum ProxyMode
{
Dispatch,
Delegate,
}
public class CallObfuscationSettingsFacade
{
public List<string> ruleFiles;
public ProxyMode proxyMode;
public int obfuscationLevel;
public int maxProxyMethodCountPerDispatchMethod;
public bool obfuscateCallToMethodInMscorlib;
public List<string> ruleFiles;
}
[Serializable]
public class CallObfuscationSettings
{
public ProxyMode proxyMode = ProxyMode.Dispatch;
[Tooltip("The obfuscation level for the obfuscation. Higher levels provide more security but may impact performance.")]
[Range(1, 4)]
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.")]
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;
[Tooltip("rule config xml files")]
@ -33,10 +42,11 @@ namespace Obfuz.Settings
{
return new CallObfuscationSettingsFacade
{
ruleFiles = ruleFiles?.ToList() ?? new List<string>(),
proxyMode = proxyMode,
obfuscationLevel = obfuscationLevel,
maxProxyMethodCountPerDispatchMethod = maxProxyMethodCountPerDispatchMethod,
obfuscateCallToMethodInMscorlib = obfuscateCallToMethodInMscorlib,
ruleFiles = ruleFiles?.ToList() ?? new List<string>(),
};
}
}

View File

@ -1,4 +1,5 @@
using System;
using Obfuz.Settings;
using System;
namespace Obfuz.Utils
{
@ -68,5 +69,10 @@ namespace Obfuz.Utils
}
return double.Parse(str);
}
public static ObfuscationLevel ParseObfuscationLevel(string str)
{
return (ObfuscationLevel)Enum.Parse(typeof(ObfuscationLevel), str);
}
}
}

View File

@ -28,6 +28,10 @@ namespace Obfuz.Utils
return ("", fullName.Substring(index + 1));
}
public static bool IsVoidType(TypeSig type)
{
return type.RemovePinnedAndModifiers().ElementType == ElementType.Void;
}
public static TypeDef GetBaseTypeDef(TypeDef type)
{