重构:将CallObfus的特殊白名单函数计算逻辑从 ConfigurableObfuscationPolicy移到 CallObfusPass

dev
walon 2025-06-26 10:49:14 +08:00
parent 62deffa10d
commit 905da05afc
3 changed files with 143 additions and 85 deletions

View File

@ -12,12 +12,14 @@ namespace Obfuz.ObfusPasses.CallObfus
private readonly CallObfuscationSettingsFacade _settings; private readonly CallObfuscationSettingsFacade _settings;
private IObfuscator _dynamicProxyObfuscator; private IObfuscator _dynamicProxyObfuscator;
private IObfuscationPolicy _dynamicProxyPolicy; private IObfuscationPolicy _dynamicProxyPolicy;
private readonly CachedDictionary<IMethod, bool> _specialwhiteListMethodCache;
public override ObfuscationPassType Type => ObfuscationPassType.CallObfus; public override ObfuscationPassType Type => ObfuscationPassType.CallObfus;
public CallObfusPass(CallObfuscationSettingsFacade settings) public CallObfusPass(CallObfuscationSettingsFacade settings)
{ {
_settings = settings; _settings = settings;
_specialwhiteListMethodCache = new CachedDictionary<IMethod, bool>(MethodEqualityComparer.CompareDeclaringTypes, this.ComputeIsInWhiteList);
} }
public override void Stop() public override void Stop()
@ -37,6 +39,129 @@ namespace Obfuz.ObfusPasses.CallObfus
return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method); return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method);
} }
private static readonly HashSet<string> _specialTypeFullNames = new HashSet<string>
{
"System.Enum",
"System.Delegate",
"System.MulticastDelegate",
"Obfuz.EncryptionService`1",
};
private static readonly HashSet<string> _specialMethodNames = new HashSet<string>
{
"GetEnumerator", // List<T>.Enumerator.GetEnumerator()
".ctor", // constructor
};
private static readonly HashSet<string> _specialMethodFullNames = new HashSet<string>
{
"System.Reflection.MethodBase.GetCurrentMethod",
"System.Reflection.Assembly.GetCallingAssembly",
"System.Reflection.Assembly.GetExecutingAssembly",
"System.Reflection.Assembly.GetEntryAssembly",
};
private bool IsSpecialNotObfuscatedMethod(TypeDef typeDef, IMethod method)
{
if (typeDef.IsDelegate || typeDef.IsEnum)
return true;
string fullName = typeDef.FullName;
if (_specialTypeFullNames.Contains(fullName))
{
return true;
}
//if (fullName.StartsWith("System.Runtime.CompilerServices."))
//{
// return true;
//}
string methodName = method.Name;
if (_specialMethodNames.Contains(methodName))
{
return true;
}
string methodFullName = $"{fullName}.{methodName}";
if (_specialMethodFullNames.Contains(methodFullName))
{
return true;
}
return false;
}
private bool ComputeIsInWhiteList(IMethod calledMethod)
{
// mono has more strict access control, calls non-public method will raise exception.
if (PlatformUtil.IsMonoBackend())
{
MethodDef calledMethodDef = calledMethod.ResolveMethodDef();
if (calledMethodDef != null && (!calledMethodDef.IsPublic || !IsTypeSelfAndParentPublic(calledMethodDef.DeclaringType)))
{
return true;
}
}
ITypeDefOrRef declaringType = calledMethod.DeclaringType;
TypeSig declaringTypeSig = calledMethod.DeclaringType.ToTypeSig();
declaringTypeSig = declaringTypeSig.RemovePinnedAndModifiers();
switch (declaringTypeSig.ElementType)
{
case ElementType.ValueType:
case ElementType.Class:
{
break;
}
case ElementType.GenericInst:
{
if (MetaUtil.ContainsContainsGenericParameter(calledMethod))
{
return true;
}
break;
}
default: return true;
}
TypeDef typeDef = declaringType.ResolveTypeDef();
if (typeDef.IsDelegate || typeDef.IsEnum)
return true;
string fullName = typeDef.FullName;
if (_specialTypeFullNames.Contains(fullName))
{
return true;
}
//if (fullName.StartsWith("System.Runtime.CompilerServices."))
//{
// return true;
//}
string methodName = calledMethod.Name;
if (_specialMethodNames.Contains(methodName))
{
return true;
}
string methodFullName = $"{fullName}.{methodName}";
if (_specialMethodFullNames.Contains(methodFullName))
{
return true;
}
return false;
}
private bool IsTypeSelfAndParentPublic(TypeDef type)
{
if (type.DeclaringType != null && !IsTypeSelfAndParentPublic(type.DeclaringType))
{
return false;
}
return type.IsPublic;
}
protected override bool TryObfuscateInstruction(MethodDef callerMethod, Instruction inst, BasicBlock block, protected override bool TryObfuscateInstruction(MethodDef callerMethod, Instruction inst, BasicBlock block,
int instructionIndex, IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions) int instructionIndex, IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
{ {
@ -70,6 +195,13 @@ namespace Obfuz.ObfusPasses.CallObfus
default: return false; default: return false;
} }
if (_specialwhiteListMethodCache.GetValue(calledMethod))
{
return false;
}
if (!_dynamicProxyPolicy.NeedObfuscateCalledMethod(callerMethod, calledMethod, callVir, block.inLoop)) if (!_dynamicProxyPolicy.NeedObfuscateCalledMethod(callerMethod, calledMethod, callVir, block.inLoop))
{ {
return false; return false;

View File

@ -86,11 +86,12 @@ namespace Obfuz.ObfusPasses.CallObfus
private ObfuscationRule _global; private ObfuscationRule _global;
private readonly List<WhiteListAssembly> _whiteListAssemblies = new List<WhiteListAssembly>(); private readonly List<WhiteListAssembly> _whiteListAssemblies = new List<WhiteListAssembly>();
private readonly Dictionary<IMethod, bool> _whiteListMethodCache = new Dictionary<IMethod, bool>(MethodEqualityComparer.CompareDeclaringTypes); private readonly CachedDictionary<IMethod, bool> _whiteListMethodCache;
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>(); private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles) public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
{ {
_whiteListMethodCache = new CachedDictionary<IMethod, bool>(MethodEqualityComparer.CompareDeclaringTypes, this.ComputeIsInWhiteList);
_configParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(toObfuscatedAssemblyNames, _configParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(toObfuscatedAssemblyNames,
ParseObfuscationRule, ParseGlobalElement); ParseObfuscationRule, ParseGlobalElement);
LoadConfigs(xmlConfigFiles); LoadConfigs(xmlConfigFiles);
@ -274,54 +275,6 @@ namespace Obfuz.ObfusPasses.CallObfus
}; };
} }
private readonly HashSet<string> _specialTypeFullNames = new HashSet<string>
{
"System.Enum",
"System.Delegate",
"System.MulticastDelegate",
"Obfuz.EncryptionService`1",
};
private readonly HashSet<string> _specialMethodNames = new HashSet<string>
{
"GetEnumerator", // List<T>.Enumerator.GetEnumerator()
".ctor", // constructor
};
private readonly HashSet<string> _specialMethodFullNames = new HashSet<string>
{
"System.Reflection.MethodBase.GetCurrentMethod",
"System.Reflection.Assembly.GetCallingAssembly",
"System.Reflection.Assembly.GetExecutingAssembly",
"System.Reflection.Assembly.GetEntryAssembly",
};
private bool IsSpecialNotObfuscatedMethod(TypeDef typeDef, IMethod method)
{
if (typeDef.IsDelegate || typeDef.IsEnum)
return true;
string fullName = typeDef.FullName;
if (_specialTypeFullNames.Contains(fullName))
{
return true;
}
string methodName = method.Name;
if (_specialMethodNames.Contains(methodName))
{
return true;
}
string methodFullName = $"{fullName}.{methodName}";
if (_specialMethodFullNames.Contains(methodFullName))
{
return true;
}
return false;
}
private bool ComputeIsInWhiteList(IMethod calledMethod) private bool ComputeIsInWhiteList(IMethod calledMethod)
{ {
ITypeDefOrRef declaringType = calledMethod.DeclaringType; ITypeDefOrRef declaringType = calledMethod.DeclaringType;
@ -347,11 +300,6 @@ namespace Obfuz.ObfusPasses.CallObfus
TypeDef typeDef = declaringType.ResolveTypeDef(); TypeDef typeDef = declaringType.ResolveTypeDef();
if (IsSpecialNotObfuscatedMethod(typeDef, calledMethod))
{
return true;
}
string assName = typeDef.Module.Assembly.Name; string assName = typeDef.Module.Assembly.Name;
string typeFullName = typeDef.FullName; string typeFullName = typeDef.FullName;
string methodName = calledMethod.Name; string methodName = calledMethod.Name;
@ -381,42 +329,13 @@ namespace Obfuz.ObfusPasses.CallObfus
return false; return false;
} }
private bool IsInWhiteList(IMethod method)
{
if (!_whiteListMethodCache.TryGetValue(method, out var isWhiteList))
{
isWhiteList = ComputeIsInWhiteList(method);
_whiteListMethodCache.Add(method, isWhiteList);
}
return isWhiteList;
}
private bool IsTypeSelfAndParentPublic(TypeDef type)
{
if (type.DeclaringType != null && !IsTypeSelfAndParentPublic(type.DeclaringType))
{
return false;
}
return type.IsPublic;
}
public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop) public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop)
{ {
if (IsInWhiteList(calledMethod)) if (_whiteListMethodCache.GetValue(calledMethod))
{ {
return false; return false;
} }
// mono has more strict access control, calls non-public method will raise exception.
if (PlatformUtil.IsMonoBackend())
{
MethodDef calledMethodDef = calledMethod.ResolveMethodDef();
if (calledMethodDef != null && (!calledMethodDef.IsPublic || !IsTypeSelfAndParentPublic(calledMethodDef.DeclaringType)))
{
return false;
}
}
ObfuscationRule rule = GetMethodObfuscationRule(callerMethod); ObfuscationRule rule = GetMethodObfuscationRule(callerMethod);
if (currentInLoop && rule.obfuscateCallInLoop == false) if (currentInLoop && rule.obfuscateCallInLoop == false)
{ {

View File

@ -6,10 +6,17 @@ namespace Obfuz.Utils
public class CachedDictionary<K, V> public class CachedDictionary<K, V>
{ {
private readonly Func<K, V> _valueFactory; private readonly Func<K, V> _valueFactory;
private readonly Dictionary<K, V> _cache = new Dictionary<K, V>(); private readonly Dictionary<K, V> _cache;
public CachedDictionary(Func<K, V> valueFactory) public CachedDictionary(Func<K, V> valueFactory)
{ {
_cache = new Dictionary<K, V>();
_valueFactory = valueFactory;
}
public CachedDictionary(IEqualityComparer<K> equalityComparer, Func<K, V> valueFactory)
{
_cache = new Dictionary<K, V>(equalityComparer);
_valueFactory = valueFactory; _valueFactory = valueFactory;
} }