public static void DoPass(RewriteGlobalContext context) { var typesUnstripped = 0; foreach (var unityAssembly in context.UnityAssemblies.Assemblies) { var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name.Name); if (processedAssembly == null) { var newAssembly = new AssemblyRewriteContext(context, unityAssembly, AssemblyDefinition.CreateAssembly(unityAssembly.Name, unityAssembly.MainModule.Name, ModuleKind.Dll)); context.AddAssemblyContext(unityAssembly.Name.Name, newAssembly); processedAssembly = newAssembly; } var imports = processedAssembly.Imports; foreach (var unityType in unityAssembly.MainModule.Types) { ProcessType(processedAssembly, unityType, null, imports, ref typesUnstripped); } } LogSupport.Trace(""); // end the progress message LogSupport.Trace($"{typesUnstripped} types restored"); }
public IEnumerable <IntPtr> JumpTargets() { var formatter = new IntelFormatter(); var builder = new StringBuilder(); while (true) { myDecoder.Decode(out var instruction); builder.Clear(); formatter.Format(in instruction, new StringOutput(builder)); LogSupport.Trace($"Decoded instruction: {builder}"); if (myDecoder.InvalidNoMoreBytes) { yield break; } if (instruction.FlowControl == FlowControl.Return) { yield break; } if (instruction.FlowControl == FlowControl.UnconditionalBranch || instruction.FlowControl == FlowControl.Call) { yield return((IntPtr)ExtractTargetAddress(in instruction)); if (instruction.FlowControl == FlowControl.UnconditionalBranch) { yield break; } } } }
public static void DoPass(RewriteGlobalContext context) { var unityAssemblyFiles = Directory.EnumerateFiles(context.Options.UnityBaseLibsDir, "*.dll"); var loadedAssemblies = unityAssemblyFiles.Select(it => AssemblyDefinition.ReadAssembly(it, new ReaderParameters(ReadingMode.Deferred))).ToList(); var typesUnstripped = 0; foreach (var unityAssembly in loadedAssemblies) { var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name.Name); if (processedAssembly == null) { continue; } var imports = processedAssembly.Imports; foreach (var unityType in unityAssembly.MainModule.Types) { ProcessType(processedAssembly, unityType, null, imports, ref typesUnstripped); } } LogSupport.Trace(""); // end the progress message LogSupport.Trace($"{typesUnstripped} types restored"); }
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 unsafe Unity2018_0NativeClassStructHandler() { Il2CppClassU2018_0 ex = new Il2CppClassU2018_0(); byte *addr = (byte *)&ex; LogSupport.Trace($"Size: {sizeof(Il2CppClassU2018_0)}"); LogSupport.Trace($"typeHierarchyDepth Offset: {&ex.typeHierarchyDepth - addr}"); LogSupport.Trace($"genericRecursionDepth Offset: {&ex.genericRecursionDepth - addr}"); LogSupport.Trace($"rank Offset: {&ex.rank - addr}"); LogSupport.Trace($"minimumAlignment Offset: {&ex.minimumAlignment - addr}"); //LogSupport.Trace($"naturalAligment Offset: {&ex.Part2.naturalAligment - addr}"); LogSupport.Trace($"packingSize Offset: {&ex.packingSize - addr}"); LogSupport.Trace($"bitfield_1 Offset: {(byte*)&ex.bitfield_1 - addr}"); LogSupport.Trace($"bitfield_2 Offset: {(byte*)&ex.bitfield_2 - addr}"); }
private static void HookClassFromType() { var lib = LoadLibrary("GameAssembly.dll"); var classFromTypeEntryPoint = GetProcAddress(lib, nameof(IL2CPP.il2cpp_class_from_il2cpp_type)); LogSupport.Trace($"il2cpp_class_from_il2cpp_type entry address: {classFromTypeEntryPoint}"); var targetMethod = XrefScannerLowLevel.JumpTargets(classFromTypeEntryPoint).Single(); LogSupport.Trace($"Xref scan target: {targetMethod}"); if (targetMethod == IntPtr.Zero) { return; } ourOriginalTypeToClassMethod = Detour.Detour(targetMethod, new TypeToClassDelegate(ClassFromTypePatch)); LogSupport.Trace("il2cpp_class_from_il2cpp_type patched"); }
public static void GenerateInvokerMethodBody(MethodDefinition newMethod, FieldDefinition delegateField, TypeDefinition delegateType, TypeRewriteContext enclosingType, AssemblyKnownImports imports) { var body = newMethod.Body.GetILProcessor(); body.Emit(OpCodes.Ldsfld, delegateField); if (newMethod.HasThis) { body.Emit(OpCodes.Ldarg_0); body.Emit(OpCodes.Call, imports.Il2CppObjectBaseToPointerNotNull); } var argOffset = newMethod.HasThis ? 1 : 0; for (var i = 0; i < newMethod.Parameters.Count; i++) { var param = newMethod.Parameters[i]; var paramType = param.ParameterType; if (paramType.IsValueType || paramType.IsByReference && paramType.GetElementType().IsValueType) { body.Emit(OpCodes.Ldarg, i + argOffset); } else { body.EmitObjectToPointer(param.ParameterType, param.ParameterType, enclosingType, i + argOffset, false, true, true, out var refVar); if (refVar != null) { LogSupport.Trace($"Method {newMethod} has a reference-typed ref parameter, this will be ignored"); } } } body.Emit(OpCodes.Call, delegateType.Methods.Single(it => it.Name == "Invoke")); if (!newMethod.ReturnType.IsValueType) { var pointerVar = new VariableDefinition(imports.IntPtr); newMethod.Body.Variables.Add(pointerVar); body.Emit(OpCodes.Stloc, pointerVar); var loadInstr = body.Create(OpCodes.Ldloc, pointerVar); body.EmitPointerToObject(newMethod.ReturnType, newMethod.ReturnType, enclosingType, loadInstr, false, false); } body.Emit(OpCodes.Ret); }
/// <summary> /// Initializes Unity interface for specified Unity version. /// </summary> /// <example>For Unity 2018.4.20, call <c>Initialize(2018, 4, 20)</c></example> public static void Initialize(int majorVersion, int minorVersion, int patchVersion) { if (majorVersion <= 2018) { if (minorVersion < 4) { LogSupport.Trace("Using Unity2018.0 handler"); ourHandler = new Unity2018_0NativeClassStructHandler(); } else { LogSupport.Trace("Using Unity2018.4 handler"); ourHandler = new Unity2018_4NativeClassStructHandler(); } } else { LogSupport.Trace("Using Unity2019 handler"); ourHandler = new Unity2019NativeClassStructHandler(); } }
public unsafe Unity2019NativeClassStructHandler() { Il2CppClassU2019_32 ex = new Il2CppClassU2019_32(); byte *addr = (byte *)&ex; LogSupport.Trace($"Size: {sizeof(Il2CppClassU2019_32)}"); LogSupport.Trace($"klass Offset: {(byte*)&ex.Part1.klass - addr}"); LogSupport.Trace($"typeHierarchy Offset: {(byte*)&ex.Part1.typeHierarchy - addr}"); LogSupport.Trace($"unity_user_data Offset: {(byte*)&ex.unity_user_data - addr}"); LogSupport.Trace($"cctor_finished Offset: {(byte*)&ex.Part2.cctor_finished - addr}"); LogSupport.Trace($"cctor_thread Offset: {(byte*)&ex.Part2.cctor_thread - addr}"); LogSupport.Trace($"genericContainerIndex Offset: {(byte*)&ex.Part2.genericContainerIndex - addr}"); LogSupport.Trace($"field_count Offset: {(byte*)&ex.Part2.field_count - addr}"); LogSupport.Trace($"interface_offsets_count Offset: {(byte*)&ex.Part2.interface_offsets_count - addr}"); LogSupport.Trace($"typeHierarchyDepth Offset: {&ex.typeHierarchyDepth - addr}"); LogSupport.Trace($"genericRecursionDepth Offset: {&ex.genericRecursionDepth - addr}"); LogSupport.Trace($"rank Offset: {&ex.rank - addr}"); LogSupport.Trace($"minimumAlignment Offset: {&ex.minimumAlignment - addr}"); LogSupport.Trace($"naturalAligmnent Offset: {&ex.naturalAlignment - addr}"); LogSupport.Trace($"packingSize Offset: {&ex.packingSize - addr}"); LogSupport.Trace($"bitfield_1 Offset: {(byte*)&ex.bitfield_1 - addr}"); LogSupport.Trace($"bitfield_2 Offset: {(byte*)&ex.bitfield_2 - addr}"); }
private static void HookClassFromType() { var lib = LoadLibrary("UserAssembly.dll"); var classFromTypeEntryPoint = GetProcAddress(lib, nameof(IL2CPP.il2cpp_class_from_il2cpp_type)); LogSupport.Trace($"il2cpp_class_from_il2cpp_type entry address: {classFromTypeEntryPoint}"); var targetMethod = XrefScannerLowLevel.JumpTargets(classFromTypeEntryPoint).Single(); LogSupport.Trace($"Xref scan target: {targetMethod}"); if (targetMethod == IntPtr.Zero) { return; } IntPtr *targetVarPointer = &targetMethod; DoHook((IntPtr)targetVarPointer, Marshal.GetFunctionPointerForDelegate(new TypeToClassDelegate(ClassFromTypePatch))); ourOriginalTypeToClassMethod = Marshal.GetDelegateForFunctionPointer <TypeToClassDelegate>(targetMethod); LogSupport.Trace("il2cpp_class_from_il2cpp_type patched"); }
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"); }
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}"); }
public static void DoPass(RewriteGlobalContext context) { int fieldsUnstripped = 0; int fieldsIgnored = 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; } if (!unityType.IsValueType || unityType.IsEnum) { continue; } foreach (var unityField in unityType.Fields) { if (unityField.IsStatic && !unityField.HasConstant) { continue; } if (processedType.NewType.IsExplicitLayout && !unityField.IsStatic) { continue; } var processedField = processedType.TryGetFieldByUnityAssemblyField(unityField); if (processedField != null) { continue; } var fieldType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(context, unityField.FieldType, imports); if (fieldType == null) { LogSupport.Trace($"Field {unityField} on type {unityType.FullName} has unsupported type {unityField.FieldType}, the type will be unusable"); fieldsIgnored++; continue; } var newField = new FieldDefinition(unityField.Name, unityField.Attributes & ~FieldAttributes.FieldAccessMask | FieldAttributes.Public, fieldType); if (unityField.HasConstant) { newField.Constant = unityField.Constant; } processedType.NewType.Fields.Add(newField); fieldsUnstripped++; } } } LogSupport.Info(""); // finish the progress line LogSupport.Info($"{fieldsUnstripped} fields restored"); LogSupport.Info($"{fieldsIgnored} fields failed to restore"); }
public static void DoPass(RewriteGlobalContext context) { var unityAssemblyFiles = Directory.EnumerateFiles(context.Options.UnityBaseLibsDir, "*.dll"); var loadedAssemblies = unityAssemblyFiles.Select(it => AssemblyDefinition.ReadAssembly(it, new ReaderParameters(ReadingMode.Deferred))).ToList(); int methodsUnstripped = 0; foreach (var unityAssembly in loadedAssemblies) { 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.ImplAttributes & MethodImplAttributes.InternalCall) == 0) { continue; } var processedMethod = processedType.TryGetMethodByName(unityMethod.Name); if (processedMethod != null) { continue; } var returnType = ResolveTypeInNewAssemblies(context, unityMethod.ReturnType, imports); if (returnType == null) { LogSupport.Trace($"Method {unityMethod} has unsupported return type {unityMethod.ReturnType}"); 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) { continue; } 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); if (unityMethod.IsGetter) { GetOrCreateProperty(unityMethod, newMethod).GetMethod = newMethod; } else if (unityMethod.IsSetter) { GetOrCreateProperty(unityMethod, newMethod).SetMethod = newMethod; } methodsUnstripped++; } } } LogSupport.Trace(""); // finish the progress line LogSupport.Trace($"{methodsUnstripped} methods restored"); }
public static void DoPass(RewriteGlobalContext context) { var unityAssemblyFiles = Directory.EnumerateFiles(context.Options.UnityBaseLibsDir, "*.dll"); var loadedAssemblies = unityAssemblyFiles.Select(it => AssemblyDefinition.ReadAssembly(it, new ReaderParameters(ReadingMode.Deferred))).ToList(); int fieldsUnstripped = 0; int fieldsIgnored = 0; foreach (var unityAssembly in loadedAssemblies) { 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; } if (!unityType.IsValueType || unityType.IsEnum) { continue; } foreach (var unityField in unityType.Fields) { if (unityField.IsStatic) { continue; } var processedField = processedType.TryGetFieldByUnityAssemblyField(unityField); if (processedField != null) { continue; } var fieldType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(context, unityField.FieldType, imports); if (fieldType == null) { LogSupport.Trace($"Field {unityField} on type {unityType.FullName} has unsupported type {unityField.FieldType}, the type will be unusable"); fieldsIgnored++; continue; } var newMethod = new FieldDefinition(unityField.Name, unityField.Attributes & ~FieldAttributes.FieldAccessMask | FieldAttributes.Public, fieldType); processedType.NewType.Fields.Add(newMethod); fieldsUnstripped++; } } } LogSupport.Info(""); // finish the progress line LogSupport.Info($"{fieldsUnstripped} fields restored"); LogSupport.Info($"{fieldsIgnored} fields failed to restore"); }