public static void DoPass(RewriteGlobalContext context) { var assemblyContext = context.GetAssemblyByName("mscorlib"); var typeContext = assemblyContext.GetTypeByName("System.String"); var objectTypeContext = assemblyContext.GetTypeByName("System.Object"); var methodFromMonoString = new MethodDefinition("op_Implicit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeContext.NewType); methodFromMonoString.Parameters.Add(new ParameterDefinition(assemblyContext.Imports.String)); typeContext.NewType.Methods.Add(methodFromMonoString); var fromBuilder = methodFromMonoString.Body.GetILProcessor(); var createIl2CppStringNop = fromBuilder.Create(OpCodes.Nop); fromBuilder.Emit(OpCodes.Ldarg_0); fromBuilder.Emit(OpCodes.Dup); fromBuilder.Emit(OpCodes.Brtrue_S, createIl2CppStringNop); fromBuilder.Emit(OpCodes.Ret); fromBuilder.Append(createIl2CppStringNop); fromBuilder.Emit(OpCodes.Call, assemblyContext.Imports.StringToNative); fromBuilder.Emit(OpCodes.Newobj, new MethodReference(".ctor", assemblyContext.Imports.Void, typeContext.NewType) { HasThis = true, Parameters = { new ParameterDefinition(assemblyContext.Imports.IntPtr) } }); fromBuilder.Emit(OpCodes.Ret); var methodToObject = new MethodDefinition("op_Implicit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, objectTypeContext.NewType); methodToObject.Parameters.Add(new ParameterDefinition(assemblyContext.Imports.String)); objectTypeContext.NewType.Methods.Add(methodToObject); var toObjectBuilder = methodToObject.Body.GetILProcessor(); toObjectBuilder.Emit(OpCodes.Ldarg_0); toObjectBuilder.Emit(OpCodes.Call, methodFromMonoString); toObjectBuilder.Emit(OpCodes.Ret); var methodToMonoString = new MethodDefinition("op_Implicit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, assemblyContext.Imports.String); methodToMonoString.Parameters.Add(new ParameterDefinition(typeContext.NewType)); typeContext.NewType.Methods.Add(methodToMonoString); var toBuilder = methodToMonoString.Body.GetILProcessor(); var createStringNop = toBuilder.Create(OpCodes.Nop); toBuilder.Emit(OpCodes.Ldarg_0); toBuilder.Emit(OpCodes.Call, assemblyContext.Imports.Il2CppObjectBaseToPointer); toBuilder.Emit(OpCodes.Dup); toBuilder.Emit(OpCodes.Brtrue_S, createStringNop); toBuilder.Emit(OpCodes.Pop); toBuilder.Emit(OpCodes.Ldnull); toBuilder.Emit(OpCodes.Ret); toBuilder.Append(createStringNop); toBuilder.Emit(OpCodes.Call, assemblyContext.Imports.StringFromNative); toBuilder.Emit(OpCodes.Ret); AddDelegateConversions(context); }
public static void AnalyzeDeobfuscationParams(UnhollowerOptions options) { RewriteGlobalContext rewriteContext; IIl2CppMetadataAccess inputAssemblies; using (new TimingCookie("Reading assemblies")) inputAssemblies = new CecilMetadataAccess(Directory.EnumerateFiles(options.SourceDir, "*.dll")); using (new TimingCookie("Creating assembly contexts")) rewriteContext = new RewriteGlobalContext(options, inputAssemblies, NullMetadataAccess.Instance, NullMetadataAccess.Instance); for (var chars = 1; chars <= 3; chars++) { for (var uniq = 3; uniq <= 15; uniq++) { options.TypeDeobfuscationCharsPerUniquifier = chars; options.TypeDeobfuscationMaxUniquifiers = uniq; rewriteContext.RenamedTypes.Clear(); rewriteContext.RenameGroups.Clear(); Pass05CreateRenameGroups.DoPass(rewriteContext); var uniqueTypes = rewriteContext.RenameGroups.Values.Count(it => it.Count == 1); var nonUniqueTypes = rewriteContext.RenameGroups.Values.Count(it => it.Count > 1); Console.WriteLine($"Chars=\t{chars}\tMaxU=\t{uniq}\tUniq=\t{uniqueTypes}\tNonUniq=\t{nonUniqueTypes}"); } } }
private static string GetConvertedTypeName(RewriteGlobalContext assemblyContextGlobalContext, TypeDefinition type) { if (type.Name.IsInvalidInSource()) { var newNameBase = assemblyContextGlobalContext.RenamedTypes[type]; var genericParametersCount = type.GenericParameters.Count; var renameGroup = assemblyContextGlobalContext.RenameGroups[((object)type.DeclaringType ?? type.Namespace, newNameBase, genericParametersCount)];
public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { var tasks = context.Assemblies.Where(it => !options.AdditionalAssembliesBlacklist.Contains(it.NewAssembly.Name.Name)).Select(assemblyContext => Task.Run(() => { assemblyContext.NewAssembly.Write(options.OutputDir + "/" + assemblyContext.NewAssembly.Name.Name + ".dll"); })).ToArray(); Task.WaitAll(tasks); }
private static void FindMetadataInitForMethod(RewriteGlobalContext context, long gameAssemblyBase) { var unityObjectCctor = context.Assemblies .Single(it => it.OriginalAssembly.Name.Name == "UnityEngine.CoreModule").GetTypeByName("UnityEngine.Object").OriginalType.Methods.Single(it => it.Name == ".cctor"); MetadataInitForMethodFileOffset = (IntPtr)((long)XrefScannerLowLevel.JumpTargets((IntPtr)(gameAssemblyBase + unityObjectCctor.ExtractOffset())).First()); MetadataInitForMethodRva = (long)MetadataInitForMethodFileOffset - gameAssemblyBase - unityObjectCctor.ExtractOffset() + unityObjectCctor.ExtractRva(); }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { ComputeSpecifics(typeContext); } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var type in assemblyContext.OriginalAssembly.MainModule.Types) { ProcessType(type, assemblyContext, null); } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { typeContext.AddMembers(); } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { GenerateStaticProxy(assemblyContext, typeContext); } } }
private static string NameOrRename(this TypeReference typeRef, RewriteGlobalContext context) { var resolved = typeRef.Resolve(); if (resolved != null && context.PreviousRenamedTypes.TryGetValue(resolved, out var rename)) { return((rename.StableHash() % (ulong)Math.Pow(10, context.Options.TypeDeobfuscationCharsPerUniquifier)).ToString()); } return(typeRef.Name); }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { GenerateStaticProxy(assemblyContext, typeContext); } } LogSupport.Trace($"\nTokenless method count: {ourTokenlessMethods}"); }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { var type = typeContext.OriginalType; var propertyCountsByName = new Dictionary <string, int>(); foreach (var oldProperty in type.Properties) { var unmangledPropertyName = UnmanglePropertyName(assemblyContext, oldProperty, typeContext.NewType, propertyCountsByName); var property = new PropertyDefinition(unmangledPropertyName, oldProperty.Attributes, assemblyContext.RewriteTypeRef(oldProperty.PropertyType)); foreach (var oldParameter in oldProperty.Parameters) { property.Parameters.Add(new ParameterDefinition(oldParameter.Name, oldParameter.Attributes, assemblyContext.RewriteTypeRef(oldParameter.ParameterType))); } typeContext.NewType.Properties.Add(property); if (oldProperty.GetMethod != null) { property.GetMethod = typeContext.GetMethodByOldMethod(oldProperty.GetMethod).NewMethod; } if (oldProperty.SetMethod != null) { property.SetMethod = typeContext.GetMethodByOldMethod(oldProperty.SetMethod).NewMethod; } } var defaultMemberAttribute = type.CustomAttributes.FirstOrDefault(it => it.AttributeType.Name == "AttributeAttribute" && it.Fields.Any(it => it.Name == "Name" && (string)it.Argument.Value == nameof(DefaultMemberAttribute))); if (defaultMemberAttribute != null) { typeContext.NewType.CustomAttributes.Add(new CustomAttribute( new MethodReference(".ctor", assemblyContext.Imports.Void, assemblyContext.Imports.DefaultMemberAttribute) { HasThis = true, Parameters = { new ParameterDefinition(assemblyContext.Imports.String) } }) { ConstructorArguments = { new CustomAttributeArgument(assemblyContext.Imports.String, "Item") } }); } } } }
internal static (string?Namespace, string Name) GetConvertedTypeName(RewriteGlobalContext assemblyContextGlobalContext, TypeDefinition type, TypeDefinition?enclosingType) { if (assemblyContextGlobalContext.Options.PassthroughNames) { return(null, type.Name); } if (type.Name.IsObfuscated(assemblyContextGlobalContext.Options)) { var newNameBase = assemblyContextGlobalContext.RenamedTypes[type]; var genericParametersCount = type.GenericParameters.Count; var renameGroup = assemblyContextGlobalContext.RenameGroups[((object)type.DeclaringType ?? type.Namespace, newNameBase, genericParametersCount)];
static void Main(string[] args) { if (args.Length != 3) { Console.WriteLine("Usage: AssemblyUnhollower.exe SourceAssemblyDir TargetAssemblyDir mscorlib"); return; } var sourceDir = args[0]; var targetDir = args[1]; Console.WriteLine("Reading assemblies"); var rewriteContext = new RewriteGlobalContext(args[2], Directory.EnumerateFiles(sourceDir, "*.dll")); Console.WriteLine("Creating typedefs"); Pass10CreateTypedefs.DoPass(rewriteContext); Console.WriteLine("Filling typedefs"); Pass11FillTypedefs.DoPass(rewriteContext); Console.WriteLine("Filling generic constraints"); Pass12FillGenericConstraints.DoPass(rewriteContext); Console.WriteLine("Creating members"); Pass15GenerateMemberContexts.DoPass(rewriteContext); Console.WriteLine("Creating static constructors"); Pass20GenerateStaticConstructors.DoPass(rewriteContext); Console.WriteLine("Creating value type fields"); Pass21GenerateValueTypeFields.DoPass(rewriteContext); Console.WriteLine("Creating enums"); Pass22GenerateEnums.DoPass(rewriteContext); Console.WriteLine("Creating IntPtr constructors"); Pass23GeneratePointerConstructors.DoPass(rewriteContext); Console.WriteLine("Creating type getters"); Pass24GenerateTypeStaticGetters.DoPass(rewriteContext); Console.WriteLine("Creating generic method static constructors"); Pass30GenerateGenericMethodStoreConstructors.DoPass(rewriteContext); Console.WriteLine("Creating field accessors"); Pass40GenerateFieldAccessors.DoPass(rewriteContext); Console.WriteLine("Filling methods"); Pass50GenerateMethods.DoPass(rewriteContext); Console.WriteLine("Creating properties"); Pass70GenerateProperties.DoPass(rewriteContext); Console.WriteLine("Writing assemblies"); Pass99WriteToDisk.DoPass(rewriteContext, targetDir); }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { var il2CppTypeTypeRewriteContext = assemblyContext.GlobalContext.GetAssemblyByName("mscorlib").GetTypeByName("System.Object"); var il2CppSystemTypeRef = assemblyContext.NewAssembly.MainModule.ImportReference(il2CppTypeTypeRewriteContext.NewType); foreach (var typeContext in assemblyContext.Types) { if (typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.BlittableStruct || typeContext.OriginalType.IsEnum) { continue; } var newType = typeContext.NewType; newType.Attributes = newType.Attributes & ~(TypeAttributes.LayoutMask) | TypeAttributes.ExplicitLayout; UtilGenerator.GenerateBoxMethod(newType, typeContext.ClassPointerFieldRef, il2CppSystemTypeRef); foreach (var fieldContext in typeContext.Fields) { var field = fieldContext.OriginalField; if (field.IsStatic) { continue; } var newField = new FieldDefinition(fieldContext.UnmangledName, field.Attributes.ForcePublic(), !field.FieldType.IsValueType ? assemblyContext.Imports.IntPtr : assemblyContext.RewriteTypeRef(field.FieldType)); newField.Offset = Convert.ToInt32( (string)field.CustomAttributes .Single(it => it.AttributeType.Name == "FieldOffsetAttribute") .Fields.Single().Argument.Value, 16); newType.Fields.Add(newField); } } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { var il2CppTypeTypeRewriteContext = assemblyContext.GlobalContext .GetAssemblyByName("mscorlib").GetTypeByName("System.Type"); var il2CppSystemTypeRef = assemblyContext.NewAssembly.MainModule.ImportReference(il2CppTypeTypeRewriteContext.NewType); foreach (var typeContext in assemblyContext.Types) { var typeGetMethod = new MethodDefinition("get_Il2CppType", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, il2CppSystemTypeRef); typeContext.NewType.Methods.Add(typeGetMethod); var typeProperty = new PropertyDefinition("Il2CppType", PropertyAttributes.None, il2CppSystemTypeRef); typeProperty.GetMethod = typeGetMethod; typeContext.NewType.Properties.Add(typeProperty); typeProperty.CustomAttributes.Add(new CustomAttribute(assemblyContext.Imports.ObsoleteAttributeCtor) { ConstructorArguments = { new CustomAttributeArgument(assemblyContext.Imports.String, "Use Il2CppType.Of<T>() instead. This will be removed in a future version of unhollower.") } }); var bodyBuilder = typeGetMethod.Body.GetILProcessor(); bodyBuilder.Emit(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); bodyBuilder.Emit(OpCodes.Call, assemblyContext.Imports.GetIl2CppTypeFromClass); bodyBuilder.Emit(OpCodes.Call, new MethodReference("internal_from_handle", il2CppSystemTypeRef, il2CppSystemTypeRef) { Parameters = { new ParameterDefinition(assemblyContext.Imports.IntPtr) } }); bodyBuilder.Emit(OpCodes.Ret); } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { for (var i = 0; i < typeContext.OriginalType.GenericParameters.Count; i++) { var originalParameter = typeContext.OriginalType.GenericParameters[i]; var newParameter = typeContext.NewType.GenericParameters[i]; foreach (var originalConstraint in originalParameter.Constraints) { newParameter.Constraints.Add( new GenericParameterConstraint( assemblyContext.RewriteTypeRef(originalConstraint.ConstraintType))); } } } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { if (typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.NonBlittableStruct) { continue; } var emptyCtor = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.HideBySig, assemblyContext.Imports.Void); typeContext.NewType.Methods.Add(emptyCtor); var local0 = new VariableDefinition(assemblyContext.Imports.IntPtr); emptyCtor.Body.Variables.Add(local0); var bodyBuilder = emptyCtor.Body.GetILProcessor(); bodyBuilder.Emit(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); bodyBuilder.Emit(OpCodes.Ldc_I4_0); bodyBuilder.Emit(OpCodes.Conv_U); bodyBuilder.Emit(OpCodes.Call, assemblyContext.Imports.ValueSizeGet); bodyBuilder.Emit(OpCodes.Conv_U); bodyBuilder.Emit(OpCodes.Localloc); bodyBuilder.Emit(OpCodes.Stloc_0); bodyBuilder.Emit(OpCodes.Ldarg_0); bodyBuilder.Emit(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); bodyBuilder.Emit(OpCodes.Ldloc_0); bodyBuilder.Emit(OpCodes.Call, assemblyContext.Imports.ObjectBox); bodyBuilder.Emit(OpCodes.Call, new MethodReference(".ctor", assemblyContext.Imports.Void, typeContext.NewType.BaseType) { HasThis = true, Parameters = { new ParameterDefinition(assemblyContext.Imports.IntPtr) } }); bodyBuilder.Emit(OpCodes.Ret); } } }
public AssemblyKnownImports(ModuleDefinition module, RewriteGlobalContext context) { Module = module; myContext = context; myVoidReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Void)); myIntPtrReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.IntPtr)); myStringReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.String)); myIntReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Int)); myLongReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Long)); myTypeReference = new Lazy <TypeDefinition>(() => TargetTypeSystemHandler.Type); myEnumReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Enum)); myDelegateReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Delegate)); myMulticastDelegateReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.MulticastDelegate)); myValueTypeReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.ValueType)); myObjectReference = new Lazy <TypeReference>(() => Module.ImportReference(TargetTypeSystemHandler.Object)); myIl2CppClassPointerStoreReference = new Lazy <TypeReference>(() => Module.ImportReference(typeof(Il2CppClassPointerStore <>))); myIl2CppReferenceArray = new Lazy <TypeReference>(() => Module.ImportReference(typeof(Il2CppReferenceArray <>))); myIl2CppStructArray = new Lazy <TypeReference>(() => Module.ImportReference(typeof(Il2CppStructArray <>))); myIl2CppStringArray = new Lazy <TypeReference>(() => Module.ImportReference(typeof(Il2CppStringArray)));
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { if (!typeContext.OriginalType.IsEnum) { continue; } var type = typeContext.OriginalType; var newType = typeContext.NewType; if (type.CustomAttributes.Any(it => it.AttributeType.FullName == "System.FlagsAttribute")) { newType.CustomAttributes.Add(new CustomAttribute(assemblyContext.Imports.FlagsAttributeCtor)); } foreach (var fieldDefinition in type.Fields) { var fieldName = fieldDefinition.Name; if (!context.Options.PassthroughNames && fieldName.IsObfuscated(context.Options)) { fieldName = GetUnmangledName(fieldDefinition); } if (context.Options.RenameMap.TryGetValue(typeContext.NewType.GetNamespacePrefix() + "::" + fieldName, out var newName)) { fieldName = newName; } var newDef = new FieldDefinition(fieldName, fieldDefinition.Attributes | FieldAttributes.HasDefault, assemblyContext.RewriteTypeRef(fieldDefinition.FieldType)); newType.Fields.Add(newDef); newDef.Constant = fieldDefinition.Constant; } } } }
private static void ProcessType(RewriteGlobalContext context, TypeDefinition originalType, bool allowExtraHeuristics) { foreach (var nestedType in originalType.NestedTypes) { ProcessType(context, nestedType, allowExtraHeuristics); } if (context.RenamedTypes.ContainsKey(originalType)) { return; } var unobfuscatedName = GetUnobfuscatedNameBase(context, originalType, allowExtraHeuristics); if (unobfuscatedName == null) { return; } context.RenameGroups.GetOrCreate(((object)originalType.DeclaringType ?? originalType.Namespace, unobfuscatedName, originalType.GenericParameters.Count), _ => new List <TypeDefinition>()).Add(originalType); context.RenamedTypes[originalType] = unobfuscatedName; }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { var il2CppTypeTypeRewriteContext = assemblyContext.GlobalContext .GetAssemblyByName("mscorlib").GetTypeByName("System.Type"); var il2CppSystemTypeRef = assemblyContext.NewAssembly.MainModule.ImportReference(il2CppTypeTypeRewriteContext.NewType); foreach (var typeContext in assemblyContext.Types) { var typeGetMethod = new MethodDefinition("get_Il2CppType", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, il2CppSystemTypeRef); if (typeContext.OriginalType.IsEnum) { continue; } typeContext.NewType.Methods.Add(typeGetMethod); var typeProperty = new PropertyDefinition("Il2CppType", PropertyAttributes.None, il2CppSystemTypeRef); typeProperty.GetMethod = typeGetMethod; typeContext.NewType.Properties.Add(typeProperty); var bodyBuilder = typeGetMethod.Body.GetILProcessor(); bodyBuilder.Emit(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); bodyBuilder.Emit(OpCodes.Call, assemblyContext.Imports.GetIl2CppTypeFromClass); bodyBuilder.Emit(OpCodes.Call, new MethodReference("internal_from_handle", il2CppSystemTypeRef, il2CppSystemTypeRef) { Parameters = { new ParameterDefinition(assemblyContext.Imports.IntPtr) } }); bodyBuilder.Emit(OpCodes.Ret); } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { foreach (var methodRewriteContext in typeContext.Methods) { var originalMethod = methodRewriteContext.OriginalMethod; var newMethod = methodRewriteContext.NewMethod; foreach (var originalMethodParameter in originalMethod.Parameters) { var newName = originalMethodParameter.Name.IsObfuscated(context.Options) ? $"param_{originalMethodParameter.Sequence}" : originalMethodParameter.Name; var newParameter = new ParameterDefinition(newName, originalMethodParameter.Attributes & ~ParameterAttributes.HasFieldMarshal, assemblyContext.RewriteTypeRef(originalMethodParameter.ParameterType)); if (originalMethodParameter.HasConstant && (originalMethodParameter.Constant == null || originalMethodParameter.Constant is string || originalMethodParameter.Constant is bool)) { newParameter.Constant = originalMethodParameter.Constant; } else { newParameter.Attributes &= ~ParameterAttributes.HasDefault; } newMethod.Parameters.Add(newParameter); } } } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { foreach (var originalParameter in typeContext.OriginalType.GenericParameters) { var newParameter = new GenericParameter(originalParameter.Name, typeContext.NewType); typeContext.NewType.GenericParameters.Add(newParameter); newParameter.Attributes = originalParameter.Attributes.StripValueTypeConstraint(); } if (typeContext.OriginalType.IsEnum) { typeContext.NewType.BaseType = assemblyContext.Imports.Enum; } else if (typeContext.ComputedTypeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct) { typeContext.NewType.BaseType = assemblyContext.Imports.ValueType; } } } // Second pass is explicitly done after first to account for rewriting of generic base types - value-typeness is important there foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { if (!typeContext.OriginalType.IsEnum && typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.BlittableStruct) { typeContext.NewType.BaseType = assemblyContext.RewriteTypeRef(typeContext.OriginalType.BaseType); } } } }
public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { if (string.IsNullOrEmpty(options.GameAssemblyPath)) { Pass15GenerateMemberContexts.HasObfuscatedMethods = false; return; } if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) { return; } var methodToCallersMap = new Dictionary <long, List <long> >(); var methodToCalleesMap = new Dictionary <long, List <long> >(); using var mappedFile = MemoryMappedFile.CreateFromFile(options.GameAssemblyPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); using var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); IntPtr gameAssemblyPtr; unsafe { byte *fileStartPtr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref fileStartPtr); gameAssemblyPtr = (IntPtr)fileStartPtr; } // Scan xrefs foreach (var assemblyRewriteContext in context.Assemblies) { foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var originalTypeMethod in typeRewriteContext.Methods) { var address = originalTypeMethod.FileOffset; if (address == 0) { continue; } foreach (var callTarget in XrefScannerLowLevel.CallAndIndirectTargets(IntPtr.Add(gameAssemblyPtr, (int)address))) { var targetRelative = (long)callTarget - (long)gameAssemblyPtr; methodToCallersMap.GetOrCreate(targetRelative, _ => new List <long>()).Add(address); methodToCalleesMap.GetOrCreate(address, _ => new List <long>()).Add(targetRelative); } } } } MapOfCallers = methodToCallersMap; void MarkMethodAlive(long address) { if (!NonDeadMethods.Add(address)) { return; } if (!methodToCalleesMap.TryGetValue(address, out var calleeList)) { return; } foreach (var callee in calleeList) { MarkMethodAlive(callee); } } // Now decided which of them are possible dead code foreach (var assemblyRewriteContext in context.Assemblies) { foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var methodRewriteContext in typeRewriteContext.Methods) { if (methodRewriteContext.FileOffset == 0) { continue; } var originalMethod = methodRewriteContext.OriginalMethod; if (!originalMethod.Name.IsObfuscated() || originalMethod.IsVirtual) { MarkMethodAlive(methodRewriteContext.FileOffset); } } } } }
public static void DoPass(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { foreach (var methodRewriteContext in typeContext.Methods) { var originalMethod = methodRewriteContext.OriginalMethod; var newMethod = methodRewriteContext.NewMethod; var imports = assemblyContext.Imports; foreach (var originalMethodParameter in originalMethod.Parameters) { var newParameter = new ParameterDefinition(originalMethodParameter.Name, originalMethodParameter.Attributes & ~ParameterAttributes.HasFieldMarshal, assemblyContext.RewriteTypeRef(originalMethodParameter.ParameterType)); if (originalMethodParameter.HasConstant && (originalMethodParameter.Constant == null || originalMethodParameter.Constant is string || originalMethodParameter.Constant is bool)) { newParameter.Constant = originalMethodParameter.Constant; } else { newParameter.Attributes &= ~ParameterAttributes.HasDefault; } newMethod.Parameters.Add(newParameter); } var bodyBuilder = newMethod.Body.GetILProcessor(); var exceptionLocal = new VariableDefinition(imports.IntPtr); var argArray = new VariableDefinition(new PointerType(imports.IntPtr)); var resultVar = new VariableDefinition(imports.IntPtr); var valueTypeLocal = new VariableDefinition(newMethod.ReturnType); newMethod.Body.Variables.Add(exceptionLocal); newMethod.Body.Variables.Add(argArray); newMethod.Body.Variables.Add(resultVar); if (valueTypeLocal.VariableType.FullName != "System.Void") { newMethod.Body.Variables.Add(valueTypeLocal); } if (!originalMethod.DeclaringType.IsValueType) { if (originalMethod.IsConstructor) { bodyBuilder.Emit(OpCodes.Ldarg_0); bodyBuilder.Emit(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); bodyBuilder.Emit(OpCodes.Call, imports.Il2CppNewObject); bodyBuilder.Emit(OpCodes.Call, new MethodReference(".ctor", imports.Void, typeContext.SelfSubstitutedRef) { Parameters = { new ParameterDefinition(imports.IntPtr) }, HasThis = true }); } else if (!originalMethod.IsStatic) { bodyBuilder.Emit(OpCodes.Ldarg_0); bodyBuilder.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointerNotNull); bodyBuilder.Emit(OpCodes.Pop); } } bodyBuilder.EmitLdcI4(originalMethod.Parameters.Count * IntPtr.Size); bodyBuilder.Emit(OpCodes.Conv_U); bodyBuilder.Emit(OpCodes.Localloc); bodyBuilder.Emit(OpCodes.Stloc, argArray); var argOffset = originalMethod.IsStatic ? 0 : 1; for (var i = 0; i < newMethod.Parameters.Count; i++) { bodyBuilder.Emit(OpCodes.Ldloc, argArray); bodyBuilder.EmitLdcI4(i * IntPtr.Size); bodyBuilder.Emit(OpCodes.Add); var newParam = newMethod.Parameters[i]; if (newParam.ParameterType.FullName == "System.String") { bodyBuilder.Emit(OpCodes.Ldarg, argOffset + i); bodyBuilder.Emit(OpCodes.Call, imports.StringToNative); } else if (newParam.ParameterType is GenericParameter) { GenerateGenericParameter(bodyBuilder, newMethod, newParam, imports, argOffset + i); } else if (newParam.ParameterType.IsValueType) { bodyBuilder.Emit(OpCodes.Ldarga, argOffset + i); } else { bodyBuilder.Emit(OpCodes.Ldarg, argOffset + i); bodyBuilder.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer); } bodyBuilder.Emit(OpCodes.Stind_I); } if (originalMethod.IsVirtual || originalMethod.IsAbstract) { bodyBuilder.Emit(OpCodes.Ldarg_0); bodyBuilder.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer); bodyBuilder.Emit(OpCodes.Ldsfld, methodRewriteContext.NonGenericMethodInfoPointerField); bodyBuilder.Emit(OpCodes.Call, imports.GetVirtualMethod); } else if (methodRewriteContext.GenericInstantiationsStoreSelfSubstRef != null) { bodyBuilder.Emit(OpCodes.Ldsfld, new FieldReference("Pointer", imports.IntPtr, methodRewriteContext.GenericInstantiationsStoreSelfSubstMethodRef)); } else { bodyBuilder.Emit(OpCodes.Ldsfld, methodRewriteContext.NonGenericMethodInfoPointerField); } if (originalMethod.IsStatic) { bodyBuilder.Emit(OpCodes.Ldc_I4_0); } else { if (originalMethod.DeclaringType.IsValueType) { bodyBuilder.Emit(OpCodes.Ldarg_0); } else { bodyBuilder.Emit(OpCodes.Ldarg_0); bodyBuilder.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointer); } } bodyBuilder.Emit(OpCodes.Ldloc, argArray); bodyBuilder.Emit(OpCodes.Ldloca, exceptionLocal); bodyBuilder.Emit(OpCodes.Call, imports.RuntimeInvoke); bodyBuilder.Emit(OpCodes.Stloc, resultVar); bodyBuilder.Emit(OpCodes.Ldloc, exceptionLocal); bodyBuilder.Emit(OpCodes.Call, imports.RaiseExceptionIfNecessary); bodyBuilder.Emit(OpCodes.Ldloc, resultVar); if (originalMethod.ReturnType.FullName == "System.Void") { bodyBuilder.Emit(OpCodes.Pop); } else if (originalMethod.ReturnType.FullName == "System.String") { bodyBuilder.Emit(OpCodes.Call, imports.StringFromNative); } else if (originalMethod.ReturnType.IsValueType) { bodyBuilder.Emit(OpCodes.Call, imports.ObjectUnbox); bodyBuilder.Emit(OpCodes.Ldobj, newMethod.ReturnType); } else if (originalMethod.ReturnType is GenericParameter) { GenerateGenericReturn(bodyBuilder, newMethod, assemblyContext.Imports); } else { var createRealObject = bodyBuilder.Create(OpCodes.Newobj, new MethodReference(".ctor", imports.Void, newMethod.ReturnType) { Parameters = { new ParameterDefinition(imports.IntPtr) }, HasThis = true }); bodyBuilder.Emit(OpCodes.Dup); bodyBuilder.Emit(OpCodes.Brtrue_S, createRealObject); bodyBuilder.Emit(OpCodes.Pop); bodyBuilder.Emit(OpCodes.Ldnull); bodyBuilder.Emit(OpCodes.Ret); bodyBuilder.Append(createRealObject); } bodyBuilder.Emit(OpCodes.Ret); } } } }
internal static TypeReference?ResolveTypeInNewAssembliesRaw(RewriteGlobalContext context, TypeReference unityType, AssemblyKnownImports imports) { if (unityType is ByReferenceType) { var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); return(resolvedElementType == null ? null : new ByReferenceType(resolvedElementType)); } if (unityType is GenericParameter) { return(null); } if (unityType is ArrayType arrayType) { if (arrayType.Rank != 1) { return(null); } var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); if (resolvedElementType == null) { return(null); } if (resolvedElementType.FullName == "System.String") { return(imports.Il2CppStringArray); } var genericBase = resolvedElementType.IsValueType ? imports.Il2CppStructArray : imports.Il2CppReferenceArray; return(new GenericInstanceType(genericBase) { GenericArguments = { resolvedElementType } }); } if (unityType.DeclaringType != null) { var enclosingResolvedType = ResolveTypeInNewAssembliesRaw(context, unityType.DeclaringType, imports); if (enclosingResolvedType == null) { return(null); } var resolvedNestedType = enclosingResolvedType.Resolve().NestedTypes.FirstOrDefault(it => it.Name == unityType.Name); if (resolvedNestedType == null) { return(null); } return(resolvedNestedType); } if (unityType is PointerType) { var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); return(resolvedElementType == null ? null : new PointerType(resolvedElementType)); } if (unityType is GenericInstanceType genericInstance) { var baseRef = ResolveTypeInNewAssemblies(context, genericInstance.ElementType, imports); if (baseRef == null) { return(null); } var newInstance = new GenericInstanceType(baseRef); foreach (var unityGenericArgument in genericInstance.GenericArguments) { var resolvedArgument = ResolveTypeInNewAssemblies(context, unityGenericArgument, imports); if (resolvedArgument == null) { return(null); } newInstance.GenericArguments.Add(resolvedArgument); } return(newInstance); } var targetAssemblyName = unityType.Scope.Name; if (targetAssemblyName.EndsWith(".dll")) { targetAssemblyName = targetAssemblyName.Substring(0, targetAssemblyName.Length - 4); } if ((targetAssemblyName == "mscorlib" || targetAssemblyName == "netstandard") && (unityType.IsValueType || unityType.FullName == "System.String" || unityType.FullName == "System.Void") && unityType.FullName != "System.RuntimeTypeHandle") { return(TargetTypeSystemHandler.Type.Module.GetType(unityType.FullName)); } if (targetAssemblyName == "UnityEngine") { foreach (var assemblyRewriteContext in context.Assemblies) { if (!assemblyRewriteContext.NewAssembly.Name.Name.StartsWith("UnityEngine")) { continue; } var newTypeInAnyUnityAssembly = assemblyRewriteContext.TryGetTypeByName(unityType.FullName)?.NewType; if (newTypeInAnyUnityAssembly != null) { return(newTypeInAnyUnityAssembly); } } } var targetAssembly = context.TryGetAssemblyByName(targetAssemblyName); var newType = targetAssembly?.TryGetTypeByName(unityType.FullName)?.NewType; return(newType); }
public static void DoPass(RewriteGlobalContext context) { int methodsUnstripped = 0; int methodsIgnored = 0; foreach (var unityAssembly in context.UnityAssemblies.Assemblies) { var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name.Name); if (processedAssembly == null) { continue; } var imports = processedAssembly.Imports; foreach (var unityType in unityAssembly.MainModule.Types) { var processedType = processedAssembly.TryGetTypeByName(unityType.FullName); if (processedType == null) { continue; } foreach (var unityMethod in unityType.Methods) { if (unityMethod.Name == ".cctor" || unityMethod.Name == ".ctor") { continue; } if (unityMethod.IsAbstract) { continue; } var processedMethod = processedType.TryGetMethodByUnityAssemblyMethod(unityMethod); if (processedMethod != null) { continue; } var returnType = ResolveTypeInNewAssemblies(context, unityMethod.ReturnType, imports); if (returnType == null) { LogSupport.Trace($"Method {unityMethod} has unsupported return type {unityMethod.ReturnType}"); methodsIgnored++; continue; } var newMethod = new MethodDefinition(unityMethod.Name, unityMethod.Attributes & ~MethodAttributes.MemberAccessMask | MethodAttributes.Public, returnType); var hadBadParameter = false; foreach (var unityMethodParameter in unityMethod.Parameters) { var convertedType = ResolveTypeInNewAssemblies(context, unityMethodParameter.ParameterType, imports); if (convertedType == null) { hadBadParameter = true; LogSupport.Trace($"Method {unityMethod} has unsupported parameter type {unityMethodParameter.ParameterType}"); break; } newMethod.Parameters.Add(new ParameterDefinition(unityMethodParameter.Name, unityMethodParameter.Attributes, convertedType)); } if (hadBadParameter) { methodsIgnored++; continue; } foreach (var unityMethodGenericParameter in unityMethod.GenericParameters) { var newParameter = new GenericParameter(unityMethodGenericParameter.Name, newMethod); newParameter.Attributes = unityMethodGenericParameter.Attributes; foreach (var genericParameterConstraint in unityMethodGenericParameter.Constraints) { if (genericParameterConstraint.ConstraintType.FullName == "System.ValueType") { continue; } if (genericParameterConstraint.ConstraintType.Resolve().IsInterface) { continue; } var newType = ResolveTypeInNewAssemblies(context, genericParameterConstraint.ConstraintType, imports); if (newType != null) { newParameter.Constraints.Add(new GenericParameterConstraint(newType)); } } newMethod.GenericParameters.Add(newParameter); } if ((unityMethod.ImplAttributes & MethodImplAttributes.InternalCall) != 0) { var delegateType = UnstripGenerator.CreateDelegateTypeForICallMethod(unityMethod, newMethod, imports); processedType.NewType.NestedTypes.Add(delegateType); delegateType.DeclaringType = processedType.NewType; processedType.NewType.Methods.Add(newMethod); var delegateField = UnstripGenerator.GenerateStaticCtorSuffix(processedType.NewType, delegateType, unityMethod, imports); UnstripGenerator.GenerateInvokerMethodBody(newMethod, delegateField, delegateType, processedType, imports); } else { Pass81FillUnstrippedMethodBodies.PushMethod(unityMethod, newMethod, processedType, imports); processedType.NewType.Methods.Add(newMethod); } if (unityMethod.IsGetter) { GetOrCreateProperty(unityMethod, newMethod).GetMethod = newMethod; } else if (unityMethod.IsSetter) { GetOrCreateProperty(unityMethod, newMethod).SetMethod = newMethod; } var paramsMethod = context.CreateParamsMethod(unityMethod, newMethod, imports, type => ResolveTypeInNewAssemblies(context, type, imports)); if (paramsMethod != null) { processedType.NewType.Methods.Add(paramsMethod); } methodsUnstripped++; } } } LogSupport.Info(""); // finish the progress line LogSupport.Info($"{methodsUnstripped} methods restored"); LogSupport.Info($"{methodsIgnored} methods failed to restore"); }
private static void AddDelegateConversions(RewriteGlobalContext context) { foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { if (typeContext.OriginalType.BaseType?.FullName != "System.MulticastDelegate") { continue; } var invokeMethod = typeContext.NewType.Methods.Single(it => it.Name == "Invoke"); if (invokeMethod.Parameters.Count > 8) { continue; // mscorlib only contains delegates of up to 8 parameters } // Don't generate implicit conversions for pointers and byrefs, as they can't be specified in generics if (invokeMethod.Parameters.Any(it => it.ParameterType.IsByReference || it.ParameterType.IsPointer)) { continue; } var implicitMethod = new MethodDefinition("op_Implicit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeContext.SelfSubstitutedRef); typeContext.NewType.Methods.Add(implicitMethod); var hasReturn = invokeMethod.ReturnType.FullName != "System.Void"; var hasParameters = invokeMethod.Parameters.Count > 0; TypeReference monoDelegateType; if (!hasReturn && !hasParameters) { monoDelegateType = typeContext.NewType.Module.ImportReference( assemblyContext.Imports.Type.Module.GetType("System.Action")); } else if (!hasReturn) { monoDelegateType = typeContext.NewType.Module.ImportReference( assemblyContext.Imports.Type.Module.GetType( "System.Action`" + invokeMethod.Parameters.Count)); } else { monoDelegateType = typeContext.NewType.Module.ImportReference( assemblyContext.Imports.Type.Module.GetType( "System.Func`" + (invokeMethod.Parameters.Count + 1))); } GenericInstanceType?genericInstanceType = null; if (hasParameters) { genericInstanceType = new GenericInstanceType(monoDelegateType); for (var i = 0; i < invokeMethod.Parameters.Count; i++) { genericInstanceType.GenericArguments.Add(invokeMethod.Parameters[i].ParameterType); } } if (hasReturn) { genericInstanceType ??= new GenericInstanceType(monoDelegateType); genericInstanceType.GenericArguments.Add(invokeMethod.ReturnType); } implicitMethod.Parameters.Add(new ParameterDefinition(genericInstanceType != null ? typeContext.NewType.Module.ImportReference(genericInstanceType) : monoDelegateType)); var bodyBuilder = implicitMethod.Body.GetILProcessor(); bodyBuilder.Emit(OpCodes.Ldarg_0); var delegateSupportTypeRef = typeContext.NewType.Module.ImportReference(typeof(DelegateSupport)); var genericConvertRef = new MethodReference(nameof(DelegateSupport.ConvertDelegate), assemblyContext.Imports.Void, delegateSupportTypeRef) { HasThis = false, Parameters = { new ParameterDefinition(assemblyContext.Imports.Delegate) } }; genericConvertRef.GenericParameters.Add(new GenericParameter(genericConvertRef)); genericConvertRef.ReturnType = genericConvertRef.GenericParameters[0]; var convertMethodRef = new GenericInstanceMethod(genericConvertRef) { GenericArguments = { typeContext.SelfSubstitutedRef } }; bodyBuilder.Emit(OpCodes.Call, typeContext.NewType.Module.ImportReference(convertMethodRef)); bodyBuilder.Emit(OpCodes.Ret); // public static T operator+(T lhs, T rhs) => Il2CppSystem.Delegate.Combine(lhs, rhs).Cast<T>(); var addMethod = new MethodDefinition("op_Addition", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeContext.SelfSubstitutedRef); typeContext.NewType.Methods.Add(addMethod); addMethod.Parameters.Add(new ParameterDefinition(typeContext.SelfSubstitutedRef)); addMethod.Parameters.Add(new ParameterDefinition(typeContext.SelfSubstitutedRef)); var addBody = addMethod.Body.GetILProcessor(); addBody.Emit(OpCodes.Ldarg_0); addBody.Emit(OpCodes.Ldarg_1); addBody.Emit(OpCodes.Call, assemblyContext.Imports.DelegateCombine); addBody.Emit(OpCodes.Call, assemblyContext.Imports.Module.ImportReference(new GenericInstanceMethod(assemblyContext.Imports.Il2CppObjectCast) { GenericArguments = { typeContext.SelfSubstitutedRef } })); addBody.Emit(OpCodes.Ret); // public static T operator-(T lhs, T rhs) => Il2CppSystem.Delegate.Remove(lhs, rhs)?.Cast<T>(); var subtractMethod = new MethodDefinition("op_Subtraction", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeContext.SelfSubstitutedRef); typeContext.NewType.Methods.Add(subtractMethod); subtractMethod.Parameters.Add(new ParameterDefinition(typeContext.SelfSubstitutedRef)); subtractMethod.Parameters.Add(new ParameterDefinition(typeContext.SelfSubstitutedRef)); var subtractBody = subtractMethod.Body.GetILProcessor(); subtractBody.Emit(OpCodes.Ldarg_0); subtractBody.Emit(OpCodes.Ldarg_1); subtractBody.Emit(OpCodes.Call, assemblyContext.Imports.DelegateRemove); subtractBody.Emit(OpCodes.Dup); var ret = subtractBody.Create(OpCodes.Ret); subtractBody.Emit(OpCodes.Brfalse_S, ret); subtractBody.Emit(OpCodes.Call, assemblyContext.Imports.Module.ImportReference(new GenericInstanceMethod(assemblyContext.Imports.Il2CppObjectCast) { GenericArguments = { typeContext.SelfSubstitutedRef } })); subtractBody.Append(ret); } } }
public static void DoPass(RewriteGlobalContext context) { var pdmNested0Caller = 0; var pdmNestedNZCaller = 0; var pdmTop0Caller = 0; var pdmTopNZCaller = 0; foreach (var assemblyContext in context.Assemblies) { foreach (var typeContext in assemblyContext.Types) { foreach (var methodContext in typeContext.Methods) { methodContext.CtorPhase2(); int callerCount = 0; if (Pass16ScanMethodRefs.MapOfCallers.TryGetValue(methodContext.Rva, out var callers)) { callerCount = callers.Count; } methodContext.NewMethod.CustomAttributes.Add( new CustomAttribute(assemblyContext.Imports.CallerCountAttributeCtor) { ConstructorArguments = { new CustomAttributeArgument(assemblyContext.Imports.Int, callerCount) } }); if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) { continue; } if (!methodContext.UnmangledName.Contains("_PDM_")) { continue; } TotalPotentiallyDeadMethods++; var hasZeroCallers = callerCount == 0; if (methodContext.DeclaringType.OriginalType.IsNested) { if (hasZeroCallers) { pdmNested0Caller++; } else { pdmNestedNZCaller++; } } else { if (hasZeroCallers) { pdmTop0Caller++; } else { pdmTopNZCaller++; } } } } } LogSupport.Trace(""); LogSupport.Trace($"Dead method statistics: 0t={pdmTop0Caller} mt={pdmTopNZCaller} 0n={pdmNested0Caller} mn={pdmNestedNZCaller}"); }