obfuz/Obfuz/Packages/com.code-philosophy.obfuz/Editor/ObfusPasses/SymbolObfus/Policies/ConfigurableRenamePolicy.cs

617 lines
25 KiB
C#

using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using UnityEngine;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
public class ConfigurableRenamePolicy : ObfuscationPolicyBase
{
enum RuleType
{
Assembly = 1,
Type = 2,
Method = 3,
Field = 4,
Property = 5,
Event = 6,
}
enum ModifierType
{
Private = 0,
Protected = 1,
Public = 2,
}
class MethodRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public bool? obfuscateName;
}
class FieldRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public bool? obfuscateName;
}
class PropertyRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public bool? obfuscateName;
public bool? obfuscateGetter;
public bool? obfuscateSetter;
}
class EventRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public bool? obfuscateName;
public bool? obfuscateAdd;
public bool? obfuscateRemove;
public bool? obfuscateFire;
}
class TypeRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public bool? obfuscateName;
public bool? obfuscateNamespace;
public List<FieldRuleSpec> fields;
public List<MethodRuleSpec> methods;
public List<PropertyRuleSpec> properties;
public List<EventRuleSpec> events;
}
class AssemblyRuleSpec
{
public string assemblyName;
public bool? obfuscateName;
public List<TypeRuleSpec> types;
}
private readonly Dictionary<string, AssemblyRuleSpec> _assemblyRuleSpecs = new Dictionary<string, AssemblyRuleSpec>();
private AssemblyRuleSpec ParseAssembly(XmlElement ele)
{
string assemblyName = ele.GetAttribute("name");
if (string.IsNullOrEmpty(assemblyName))
{
throw new Exception($"Invalid xml file, assembly name is empty");
}
if (!_obfuscationAssemblyNames.Contains(assemblyName))
{
throw new Exception($"unknown assembly name:{assemblyName}, not in ObfuzSettings.obfuscationAssemblyNames");
}
if (_assemblyRuleSpecs.ContainsKey(assemblyName))
{
throw new Exception($"Invalid xml file, duplicate assembly name {assemblyName}");
}
var rule = new AssemblyRuleSpec()
{
assemblyName = assemblyName,
obfuscateName = ConfigUtil.ParseNullableBool(ele.GetAttribute("obName")),
types = new List<TypeRuleSpec>(),
};
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement childElement))
{
continue;
}
if (childElement.Name != "type")
{
throw new Exception($"Invalid xml file, unknown node {childElement.Name}");
}
TypeRuleSpec type = ParseType(childElement);
rule.types.Add(type);
}
return rule;
}
private ModifierType ParseModifierType(string modifierType)
{
if (string.IsNullOrEmpty(modifierType))
{
return ModifierType.Private;
}
switch (modifierType)
{
case "public": return ModifierType.Public;
case "protected": return ModifierType.Protected;
case "private": return ModifierType.Private;
default: throw new Exception($"Invalid modifier type {modifierType}");
}
}
private TypeRuleSpec ParseType(XmlElement element)
{
var rule = new TypeRuleSpec();
rule.nameMatcher = new NameMatcher(element.GetAttribute("name"));
rule.obfuscateName = ConfigUtil.ParseNullableBool(element.GetAttribute("obName"));
rule.obfuscateNamespace = ConfigUtil.ParseNullableBool(element.GetAttribute("obNamespace"));
rule.modifierType = ParseModifierType(element.GetAttribute("modifier"));
//rule.nestTypeRuleSpecs = new List<TypeRuleSpec>();
rule.fields = new List<FieldRuleSpec>();
rule.methods = new List<MethodRuleSpec>();
rule.properties = new List<PropertyRuleSpec>();
rule.events = new List<EventRuleSpec>();
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement childElement))
{
continue;
}
switch (childElement.Name)
{
case "field":
{
var fieldRuleSpec = new FieldRuleSpec();
fieldRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
fieldRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
fieldRuleSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
rule.fields.Add(fieldRuleSpec);
break;
}
case "method":
{
var methodRuleSpec = new MethodRuleSpec();
methodRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
methodRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
methodRuleSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
rule.methods.Add(methodRuleSpec);
break;
}
case "property":
{
var propertyRulerSpec = new PropertyRuleSpec();
propertyRulerSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
propertyRulerSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
propertyRulerSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
propertyRulerSpec.obfuscateGetter = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obGetter"));
propertyRulerSpec.obfuscateSetter = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obSetter"));
rule.properties.Add(propertyRulerSpec);
break;
}
case "event":
{
var eventRuleSpec = new EventRuleSpec();
eventRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
eventRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
eventRuleSpec.obfuscateName = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obName"));
eventRuleSpec.obfuscateAdd = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obAdd"));
eventRuleSpec.obfuscateRemove = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obRemove"));
eventRuleSpec.obfuscateFire = ConfigUtil.ParseNullableBool(childElement.GetAttribute("obFire"));
rule.events.Add(eventRuleSpec);
break;
}
default: throw new Exception($"Invalid xml file, unknown node {childElement.Name} in type node");
}
}
return rule;
}
private void LoadXmls(List<string> xmlFiles)
{
var rawAssemblySpecElements = new List<XmlElement>();
foreach (string file in xmlFiles)
{
LoadRawXml(file, rawAssemblySpecElements);
}
ResolveAssemblySpecs(rawAssemblySpecElements);
}
private void ResolveAssemblySpecs(List<XmlElement> rawAssemblySpecElements)
{
foreach (XmlElement ele in rawAssemblySpecElements)
{
var assemblyRule = ParseAssembly(ele);
_assemblyRuleSpecs.Add(assemblyRule.assemblyName, assemblyRule);
}
}
private void LoadRawXml(string xmlFile, List<XmlElement> rawAssemblyElements)
{
Debug.Log($"ObfuscateRule::LoadXml {xmlFile}");
var doc = new XmlDocument();
doc.Load(xmlFile);
var root = doc.DocumentElement;
if (root.Name != "obfuz")
{
throw new Exception($"Invalid xml file {xmlFile}, root name should be 'obfuz'");
}
foreach (XmlNode node in root.ChildNodes)
{
if (!(node is XmlElement element))
{
continue;
}
switch (element.Name)
{
case "assembly":
{
rawAssemblyElements.Add(element);
break;
}
default:
{
throw new Exception($"Invalid xml file {xmlFile}, unknown node {element.Name}");
}
}
}
}
private ModifierType ComputeModifierType(TypeAttributes visibility)
{
if (visibility == TypeAttributes.NotPublic || visibility == TypeAttributes.NestedPrivate)
{
return ModifierType.Private;
}
if (visibility == TypeAttributes.Public || visibility == TypeAttributes.NestedPublic)
{
return ModifierType.Public;
}
return ModifierType.Protected;
}
private ModifierType ComputeModifierType(FieldAttributes access)
{
if (access == FieldAttributes.Private || access == FieldAttributes.PrivateScope)
{
return ModifierType.Private;
}
if (access == FieldAttributes.Public)
{
return ModifierType.Public;
}
return ModifierType.Protected;
}
//private ModifierType ComputeModifierType(MethodAttributes access)
//{
// if (access == MethodAttributes.Private || access == MethodAttributes.PrivateScope)
// {
// return ModifierType.Private;
// }
// if (access == MethodAttributes.Public)
// {
// return ModifierType.Public;
// }
// return ModifierType.Protected;
//}
private bool MatchModifier(ModifierType modifierType, TypeDef typeDef)
{
return modifierType <= ComputeModifierType(typeDef.Visibility);
}
private bool MatchModifier(ModifierType modifierType, FieldDef fieldDef)
{
return modifierType <= ComputeModifierType(fieldDef.Access);
}
private bool MatchModifier(ModifierType modifierType, MethodDef methodDef)
{
return modifierType <= ComputeModifierType((FieldAttributes)methodDef.Access);
}
private bool MatchModifier(ModifierType modifierType, PropertyDef propertyDef)
{
return modifierType <= ComputeModifierType((FieldAttributes)propertyDef.Attributes);
}
private bool MatchModifier(ModifierType modifierType, EventDef eventDef)
{
return modifierType <= ComputeModifierType((FieldAttributes)eventDef.Attributes);
}
private class MethodComputeCache
{
public bool obfuscateName = true;
public bool obfuscateParam = true;
public bool obfuscateBody = true;
}
//private class TypeDefComputeCache
//{
// public bool obfuscateName = true;
// public bool obfuscateNamespace = true;
// public readonly Dictionary<MethodDef, MethodComputeCache> methods = new Dictionary<MethodDef, MethodComputeCache>();
// public readonly HashSet<FieldDef> notObfuscatedFields = new HashSet<FieldDef>();
// public readonly HashSet<PropertyDef> notObfuscatedProperties = new HashSet<PropertyDef>();
// public readonly HashSet<EventDef> notObfuscatedEvents = new HashSet<EventDef>();
//}
private readonly Dictionary<TypeDef, TypeRuleSpec> _typeSpecCache = new Dictionary<TypeDef, TypeRuleSpec>();
private readonly Dictionary<MethodDef, MethodRuleSpec> _methodSpecCache = new Dictionary<MethodDef, MethodRuleSpec>();
private readonly Dictionary<FieldDef, FieldRuleSpec> _fieldSpecCache = new Dictionary<FieldDef, FieldRuleSpec>();
private readonly Dictionary<PropertyDef, PropertyRuleSpec> _propertySpecCache = new Dictionary<PropertyDef, PropertyRuleSpec>();
private readonly Dictionary<EventDef, EventRuleSpec> _eventSpecCache = new Dictionary<EventDef, EventRuleSpec>();
private readonly HashSet<string> _obfuscationAssemblyNames;
public ConfigurableRenamePolicy(List<string> obfuscationAssemblyNames, List<string> xmlFiles)
{
_obfuscationAssemblyNames = new HashSet<string>(obfuscationAssemblyNames);
LoadXmls(xmlFiles);
}
private void BuildDefaultTypeMemberCache(TypeDef typeDef, TypeRuleSpec typeRule)
{
foreach (var fieldDef in typeDef.Fields)
{
var fieldRule = new FieldRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
};
_fieldSpecCache.Add(fieldDef, fieldRule);
}
foreach (var eventDef in typeDef.Events)
{
var eventRule = new EventRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
obfuscateAdd = typeRule.obfuscateName,
obfuscateRemove = typeRule.obfuscateName,
obfuscateFire = typeRule.obfuscateName,
};
_eventSpecCache.Add(eventDef, eventRule);
}
foreach (var propertyDef in typeDef.Properties)
{
var propertyRule = new PropertyRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
obfuscateGetter = typeRule.obfuscateName,
obfuscateSetter = typeRule.obfuscateName,
};
_propertySpecCache.Add(propertyDef, propertyRule);
}
foreach (MethodDef methodDef in typeDef.Methods)
{
var methodRule = new MethodRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
};
_methodSpecCache.Add(methodDef, methodRule);
}
}
private TypeRuleSpec GetOrCreateTypeDefRenameComputeCache(TypeDef typeDef)
{
if (_typeSpecCache.TryGetValue(typeDef, out var typeRule))
{
return typeRule;
}
typeRule = new TypeRuleSpec();
_typeSpecCache.Add(typeDef, typeRule);
if (!_assemblyRuleSpecs.TryGetValue(typeDef.Module.Assembly.Name, out var assemblyRuleSpec))
{
typeRule.obfuscateName = true;
typeRule.obfuscateNamespace = true;
BuildDefaultTypeMemberCache(typeDef, typeRule);
return typeRule;
}
typeRule.obfuscateName = assemblyRuleSpec.obfuscateName ?? true;
typeRule.obfuscateNamespace = assemblyRuleSpec.obfuscateName ?? true;
string typeName = typeDef.FullName;
bool findMatch = false;
foreach (var typeSpec in assemblyRuleSpec.types)
{
if (!typeSpec.nameMatcher.IsMatch(typeName) || !MatchModifier(typeSpec.modifierType, typeDef))
{
continue;
}
findMatch = true;
if (typeSpec.obfuscateName != null)
{
typeRule.obfuscateName = typeSpec.obfuscateName;
}
if (typeSpec.obfuscateNamespace != null)
{
typeRule.obfuscateNamespace = typeSpec.obfuscateNamespace;
}
foreach (var fieldDef in typeDef.Fields)
{
var fieldRule = new FieldRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
};
_fieldSpecCache.Add(fieldDef, fieldRule);
foreach (var fieldSpec in typeSpec.fields)
{
if (fieldSpec.nameMatcher.IsMatch(fieldDef.Name) && MatchModifier(fieldSpec.modifierType, fieldDef))
{
if (fieldSpec.obfuscateName != null)
{
fieldRule.obfuscateName = fieldSpec.obfuscateName;
}
break;
}
}
}
var methodObfuscateFromPropertyOrEvent = new Dictionary<MethodDef, bool>();
foreach (var eventDef in typeDef.Events)
{
var eventRule = new EventRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
obfuscateAdd = typeRule.obfuscateName,
obfuscateRemove = typeRule.obfuscateName,
obfuscateFire = typeRule.obfuscateName,
};
_eventSpecCache.Add(eventDef, eventRule);
foreach (var eventSpec in typeSpec.events)
{
if (!eventSpec.nameMatcher.IsMatch(eventDef.Name) || !MatchModifier(eventSpec.modifierType, eventDef))
{
continue;
}
if (eventSpec.obfuscateName != null)
{
eventRule.obfuscateName = eventSpec.obfuscateName;
}
if (eventSpec.obfuscateAdd != null)
{
eventRule.obfuscateAdd = eventSpec.obfuscateAdd;
}
if (eventSpec.obfuscateRemove != null)
{
eventRule.obfuscateRemove = eventSpec.obfuscateRemove;
}
if (eventSpec.obfuscateFire != null)
{
eventRule.obfuscateFire = eventSpec.obfuscateFire;
}
if (eventDef.AddMethod != null && eventRule.obfuscateAdd != null)
{
methodObfuscateFromPropertyOrEvent.Add(eventDef.AddMethod, eventRule.obfuscateAdd.Value);
}
if (eventDef.RemoveMethod != null && eventRule.obfuscateRemove != null)
{
methodObfuscateFromPropertyOrEvent.Add(eventDef.RemoveMethod, eventRule.obfuscateRemove.Value);
}
if (eventDef.InvokeMethod != null && eventRule.obfuscateFire != null)
{
methodObfuscateFromPropertyOrEvent.Add(eventDef.InvokeMethod, eventRule.obfuscateFire.Value);
}
break;
}
}
foreach (var propertyDef in typeDef.Properties)
{
var propertyRule = new PropertyRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
obfuscateGetter = typeRule.obfuscateName,
obfuscateSetter = typeRule.obfuscateName,
};
_propertySpecCache.Add(propertyDef, propertyRule);
foreach (var propertySpec in typeSpec.properties)
{
if (!propertySpec.nameMatcher.IsMatch(propertyDef.Name) || !MatchModifier(propertySpec.modifierType, propertyDef))
{
continue;
}
if (propertySpec.obfuscateName != null)
{
propertyRule.obfuscateName = propertySpec.obfuscateName;
}
if (propertySpec.obfuscateGetter != null)
{
propertyRule.obfuscateGetter = propertySpec.obfuscateGetter;
}
if (propertySpec.obfuscateSetter != null)
{
propertyRule.obfuscateSetter = propertySpec.obfuscateSetter;
}
if (propertyDef.GetMethod != null && propertyRule.obfuscateGetter != null)
{
methodObfuscateFromPropertyOrEvent.Add(propertyDef.GetMethod, propertyRule.obfuscateGetter.Value);
}
if (propertyDef.SetMethod != null && propertyRule.obfuscateSetter != null)
{
methodObfuscateFromPropertyOrEvent.Add(propertyDef.SetMethod, propertyRule.obfuscateSetter.Value);
}
break;
}
}
foreach (MethodDef methodDef in typeDef.Methods)
{
var methodRule = new MethodRuleSpec()
{
obfuscateName = typeRule.obfuscateName,
};
_methodSpecCache.Add(methodDef, methodRule);
if (methodObfuscateFromPropertyOrEvent.TryGetValue(methodDef, out var obfuscateName))
{
methodRule.obfuscateName = obfuscateName;
}
foreach (MethodRuleSpec methodSpec in typeSpec.methods)
{
if (!methodSpec.nameMatcher.IsMatch(methodDef.Name) || !MatchModifier(methodSpec.modifierType, methodDef))
{
continue;
}
if (methodSpec.obfuscateName != null)
{
methodRule.obfuscateName = methodSpec.obfuscateName;
}
break;
}
}
}
if (!findMatch)
{
BuildDefaultTypeMemberCache(typeDef, typeRule);
}
return typeRule;
}
public override bool NeedRename(TypeDef typeDef)
{
var cache = GetOrCreateTypeDefRenameComputeCache(typeDef);
return cache.obfuscateName != false;
}
public override bool NeedRename(MethodDef methodDef)
{
TypeDef typeDef = methodDef.DeclaringType;
GetOrCreateTypeDefRenameComputeCache(typeDef);
return _methodSpecCache[methodDef].obfuscateName != false;
}
public override bool NeedRename(FieldDef fieldDef)
{
TypeDef typeDef = fieldDef.DeclaringType;
GetOrCreateTypeDefRenameComputeCache(typeDef);
return _fieldSpecCache[fieldDef].obfuscateName != false;
}
public override bool NeedRename(PropertyDef propertyDef)
{
TypeDef typeDef = propertyDef.DeclaringType;
GetOrCreateTypeDefRenameComputeCache(typeDef);
return _propertySpecCache[propertyDef].obfuscateName != false;
}
public override bool NeedRename(EventDef eventDef)
{
TypeDef typeDef = eventDef.DeclaringType;
GetOrCreateTypeDefRenameComputeCache(typeDef);
return _eventSpecCache[eventDef].obfuscateName != false;
}
}
}