using dnlib.DotNet; using Obfuz.Rename; using System; using System.Collections; using System.Collections.Generic; using System.Data.OleDb; using System.Linq; using System.Runtime.InteropServices; using UnityEditor.SceneManagement; using UnityEngine; namespace Obfuz { public class SymbolRename { private readonly ObfuscatorContext _ctx; private readonly IRenamePolicy _renamePolicy; private readonly INameMaker _nameMaker; private readonly Dictionary> _customAttributeArgumentsWithTypeByMods = new Dictionary>(); class CustomAttributeInfo { public CustomAttributeCollection customAttributes; public int index; public List arguments; public List namedArguments; } public SymbolRename(ObfuscatorContext ctx) { _ctx = ctx; _renamePolicy = ctx.renamePolicy; _nameMaker = ctx.nameMaker; BuildCustomAttributeArguments(); } private bool MayRenameType(ElementType type) { return type == ElementType.Class || type == ElementType.ValueType || type == ElementType.Object || type == ElementType.SZArray; } private void CollectCArgumentWithTypeOf(IHasCustomAttribute meta, List customAttributes) { int index = 0; foreach (CustomAttribute ca in meta.CustomAttributes) { List arguments = null; if (ca.ConstructorArguments.Any(a => MayRenameType(a.Type.ElementType))) { arguments = ca.ConstructorArguments.ToList(); } List namedArguments = null; if (ca.NamedArguments.Any(a => MayRenameType(a.Type.ElementType))) { namedArguments = ca.NamedArguments.ToList(); } if (arguments != null | namedArguments != null) { customAttributes.Add(new CustomAttributeInfo { customAttributes = meta.CustomAttributes, index = index, arguments = arguments, namedArguments = namedArguments }); } ++index; } } private void BuildCustomAttributeArguments() { foreach (ObfuzAssemblyInfo ass in _ctx.assemblies) { var customAttributes = new List(); CollectCArgumentWithTypeOf(ass.module, customAttributes); foreach (TypeDef type in ass.module.GetTypes()) { CollectCArgumentWithTypeOf(type, customAttributes); foreach (FieldDef field in type.Fields) { CollectCArgumentWithTypeOf(field, customAttributes); } foreach (MethodDef method in type.Methods) { CollectCArgumentWithTypeOf(method, customAttributes); foreach (Parameter param in method.Parameters) { if (param.ParamDef != null) { CollectCArgumentWithTypeOf(param.ParamDef, customAttributes); } } } foreach (PropertyDef property in type.Properties) { CollectCArgumentWithTypeOf(property, customAttributes); } foreach (EventDef eventDef in type.Events) { CollectCArgumentWithTypeOf (eventDef, customAttributes); } } _customAttributeArgumentsWithTypeByMods.Add(ass.module, customAttributes); } } public void Process() { foreach (ObfuzAssemblyInfo ass in _ctx.assemblies) { if (_renamePolicy.NeedRename(ass.module)) { Rename(ass.module); } foreach (TypeDef type in ass.module.GetTypes()) { if (!IsSystemReservedType(type) && _renamePolicy.NeedRename(type)) { Rename(type); } foreach (FieldDef field in type.Fields) { if (_renamePolicy.NeedRename(field)) { Rename(field); } } foreach (MethodDef method in type.Methods) { if (_renamePolicy.NeedRename(method)) { Rename(method); foreach (Parameter param in method.Parameters) { if (param.ParamDef != null) { Rename(param.ParamDef); } } } } foreach (EventDef eventDef in type.Events) { if (_renamePolicy.NeedRename(eventDef)) { Rename(eventDef); } } foreach (PropertyDef property in type.Properties) { if (_renamePolicy.NeedRename(property)) { Rename(property); } } } } } private bool IsSystemReservedType(TypeDef type) { if (type.FullName == "") { return true; } return false; } private List GetReferenceMeAssemblies(ModuleDef mod) { return _ctx.assemblies.Find(ass => ass.module == mod).referenceMeAssemblies; } private void Rename(ModuleDefMD mod) { string oldName = MetaUtil.GetModuleNameWithoutExt(mod.Name); string newName = _nameMaker.GetNewName(mod, oldName); mod.Name = $"{newName}.dll"; Debug.Log($"rename module. oldName:{oldName} newName:{newName}"); foreach (ObfuzAssemblyInfo ass in GetReferenceMeAssemblies(mod)) { foreach (AssemblyRef assRef in ass.module.GetAssemblyRefs()) { if (assRef.Name == oldName) { assRef.Name = newName; Debug.Log($"rename assembly:{ass.name} ref oldName:{oldName} newName:{newName}"); } } } } private void Rename(TypeDef type) { string moduleName = MetaUtil.GetModuleNameWithoutExt(type.Module.Name); string oldFullName = type.FullName; string oldNamespace = type.Namespace; string newNamespace; if (string.IsNullOrEmpty(oldNamespace)) { newNamespace = oldNamespace; } else { newNamespace = _nameMaker.GetNewNamespace(type, oldNamespace); type.Namespace = newNamespace; } string oldName = type.Name; string newName = _nameMaker.GetNewName(type, oldName); ModuleDefMD mod = (ModuleDefMD)type.Module; //RenameTypeRefInCustomAttribute(mod, type, oldFullName, null); foreach (ObfuzAssemblyInfo ass in GetReferenceMeAssemblies(mod)) { RenameTypeRefInCustomAttribute(ass.module, mod, type, oldFullName); } foreach (ObfuzAssemblyInfo ass in GetReferenceMeAssemblies(mod)) { foreach (TypeRef typeRef in ass.module.GetTypeRefs()) { if (typeRef.FullName != oldFullName) { continue; } if (typeRef.DefinitionAssembly.Name != moduleName) { continue; } if (!string.IsNullOrEmpty(oldNamespace)) { typeRef.Namespace = newNamespace; } typeRef.Name = newName; Debug.Log($"rename assembly:{ass.module.Name} reference {oldFullName} => {typeRef.FullName}"); } } type.Name = newName; string newFullName = type.FullName; Debug.Log($"rename typedef. assembly:{type.Module.Name} oldName:{oldFullName} => newName:{newFullName}"); } private TypeSig RenameTypeSig(TypeSig type, ModuleDefMD mod, string oldFullName) { TypeSig next = type.Next; TypeSig newNext = next != null ? RenameTypeSig(next, mod, oldFullName) : null; if (type.IsModifier || type.IsPinned) { if (next == newNext) { return type; } if (type is CModReqdSig cmrs) { return new CModReqdSig(cmrs.Modifier, newNext); } if (type is CModOptSig cmos) { return new CModOptSig(cmos.Modifier, newNext); } if (type is PinnedSig ps) { return new PinnedSig(newNext); } throw new System.NotSupportedException(type.ToString()); } switch (type.ElementType) { case ElementType.Ptr: { if (next == newNext) { return type; } return new PtrSig(newNext); } case ElementType.ValueType: case ElementType.Class: { var vts = type as ClassOrValueTypeSig; if (vts.DefinitionAssembly.Name != mod.Assembly.Name || vts.TypeDefOrRef.FullName != oldFullName) { return type; } TypeDef typeDef = vts.TypeDefOrRef.ResolveTypeDefThrow(); if (typeDef == vts.TypeDefOrRef) { return type; } return type.IsClassSig ? new ClassSig(typeDef) : new ValueTypeSig(typeDef); } case ElementType.Array: { if (next == newNext) { return type; } return new ArraySig(newNext); } case ElementType.SZArray: { if (next == newNext) { return type; } return new SZArraySig(newNext); } case ElementType.GenericInst: { var gis = type as GenericInstSig; ClassOrValueTypeSig genericType = gis.GenericType; ClassOrValueTypeSig newGenericType = (ClassOrValueTypeSig)RenameTypeSig(genericType, mod, oldFullName); bool anyChange = genericType != newGenericType; var genericArgs = new List(); foreach (var arg in gis.GenericArguments) { TypeSig newArg = RenameTypeSig(arg, mod, oldFullName); anyChange |= newArg != genericType; genericArgs.Add(newArg); } if (!anyChange) { return type; } return new GenericInstSig(newGenericType, genericArgs); } case ElementType.FnPtr: { var fp = type as FnPtrSig; MethodSig methodSig = fp.MethodSig; TypeSig newReturnType = RenameTypeSig(methodSig.RetType, mod, oldFullName); bool anyChange = newReturnType != methodSig.RetType; var newArgs = new List(); foreach (TypeSig arg in methodSig.Params) { TypeSig newArg = RenameTypeSig (arg, mod, oldFullName); anyChange |= newArg != newReturnType; } if (!anyChange) { return type; } var newParamsAfterSentinel = new List(); foreach(TypeSig arg in methodSig.ParamsAfterSentinel) { TypeSig newArg = RenameTypeSig(arg, mod, oldFullName); anyChange |= newArg != arg; newParamsAfterSentinel.Add(newArg); } var newMethodSig = new MethodSig(methodSig.CallingConvention, methodSig.GenParamCount, newReturnType, newArgs, newParamsAfterSentinel); return new FnPtrSig(newMethodSig); } case ElementType.ByRef: { if (next == newNext) { return type; } return new ByRefSig(newNext); } default: { return type; } } } private object RenameTypeSigOfValue(object oldValue, ModuleDefMD mod, string oldFullName) { if (oldValue == null) { return null; } if (oldValue.GetType().IsPrimitive) { return oldValue; } if (oldValue is string || oldValue is UTF8String) { return oldValue; } if (oldValue is TypeSig typeSig) { return RenameTypeSig(typeSig, mod, oldFullName); } if (oldValue is CAArgument caValue) { TypeSig newType = RenameTypeSig(caValue.Type, mod, oldFullName); object newValue = RenameTypeSigOfValue(caValue.Value, mod, oldFullName); if (newType != caValue.Type || newValue != caValue.Value) { return new CAArgument(newType, newValue); } return oldValue; } if (oldValue is List oldArr) { bool anyChange = false; var newArr = new List(); foreach (CAArgument oldArg in oldArr) { if (TryRenameArgument(mod, oldFullName, oldArg, out var newArg)) { anyChange = true; newArr.Add(newArg); } else { newArr.Add(oldArg); } } return anyChange ? newArr : oldArr; } throw new NotSupportedException($"type:{oldValue.GetType()} value:{oldValue}"); } private bool TryRenameArgument(ModuleDefMD mod, string oldFullName, CAArgument oldArg, out CAArgument newArg) { TypeSig newType = RenameTypeSig(oldArg.Type, mod, oldFullName); object newValue = RenameTypeSigOfValue(oldArg.Value, mod, oldFullName); if (newType != oldArg.Type || oldArg.Value != newValue) { newArg = new CAArgument(newType, newValue); return true; } newArg = default; return false; } private void RenameTypeRefInCustomAttribute(ModuleDefMD referenceMeMod, ModuleDefMD mod, TypeDef typeDef, string oldFullName) { List customAttributes = _customAttributeArgumentsWithTypeByMods[referenceMeMod]; foreach (CustomAttributeInfo cai in customAttributes) { CustomAttribute oldAttr = cai.customAttributes[cai.index]; bool anyChange = false; if (cai.arguments != null) { for (int i = 0; i < cai.arguments.Count; i++) { CAArgument oldArg = cai.arguments[i]; if (TryRenameArgument(mod, oldFullName, oldArg, out CAArgument newArg)) { anyChange = true; cai.arguments[cai.index] = newArg; } } } if (cai.namedArguments != null) { for (int i = 0; i < cai.namedArguments.Count; i++) { CANamedArgument oldArg = cai.namedArguments[i]; if (TryRenameArgument(mod, oldFullName, oldArg.Argument, out var newArg)) { anyChange = true; oldArg.Argument = newArg; } } } if (anyChange) { cai.customAttributes[cai.index] = new CustomAttribute(oldAttr.Constructor, cai.arguments != null ? cai.arguments : oldAttr.ConstructorArguments, cai.namedArguments != null ? cai.namedArguments : oldAttr.NamedArguments); } } } private void Rename(FieldDef field) { string oldName = field.Name; string newName = _nameMaker.GetNewName(field, oldName); foreach (ObfuzAssemblyInfo ass in GetReferenceMeAssemblies(field.DeclaringType.Module)) { foreach (MemberRef memberRef in ass.module.GetMemberRefs()) { if (!memberRef.IsFieldRef) { continue; } if (oldName != memberRef.Name || !TypeEqualityComparer.Instance.Equals(memberRef.FieldSig.Type, field.FieldSig.Type)) { continue; } IMemberRefParent parent = memberRef.Class; if (parent is ITypeDefOrRef typeDefOrRef) { if (typeDefOrRef.IsTypeDef) { if (typeDefOrRef != field.DeclaringType) { continue; } } else if (typeDefOrRef.IsTypeRef) { if (typeDefOrRef.ResolveTypeDefThrow() != field.DeclaringType) { continue; } } else if (typeDefOrRef.IsTypeSpec) { var typeSpec = (TypeSpec)typeDefOrRef; GenericInstSig gis = typeSpec.TryGetGenericInstSig(); if (gis == null || gis.GenericType.ToTypeDefOrRef().ResolveTypeDef() != field.DeclaringType) { continue; } } else { continue; } } string oldFieldFullName = memberRef.ToString(); memberRef.Name = newName; Debug.Log($"rename assembly:{ass.name} field:{oldFieldFullName} => {memberRef}"); } } field.Name = newName; Debug.Log($"rename field. {field} => {newName}"); } private void Rename(MethodDef method) { } private void Rename(ParamDef param) { param.Name = _nameMaker.GetNewName(param, param.Name); } private void Rename(EventDef eventDef) { eventDef.Name = _nameMaker.GetNewName(eventDef, eventDef.Name); } private void Rename(PropertyDef property) { property.Name = _nameMaker.GetNewName(property, property.Name); } } }