private static IEnumerable <CodeInstruction> UnhollowerTranspiler(MethodBase method, IEnumerable <CodeInstruction> instructionsIn)
        {
            List <CodeInstruction> instructions = new List <CodeInstruction>(instructionsIn);
            PatchInfo patchInfo        = HarmonySharedState.GetPatchInfo(method);
            IntPtr    copiedMethodInfo = patchInfo.copiedMethodInfoPointer;

            bool found        = false;
            int  replaceIdx   = 0;
            int  replaceCount = 0;

            for (int i = instructions.Count - 2; i >= 0; --i)
            {
                if (instructions[i].opcode != OpCodes.Ldsfld)
                {
                    continue;
                }

                found = true;
                CodeInstruction next = instructions[i + 1];
                if (next.opcode == OpCodes.Call && ((MethodInfo)next.operand).Name == "il2cpp_object_get_virtual_method")
                {
                    // Virtual method: Replace the sequence
                    // - ldarg.0
                    // - call native int[UnhollowerBaseLib] UnhollowerBaseLib.IL2CPP::Il2CppObjectBaseToPtr(class [UnhollowerBaseLib] UnhollowerBaseLib.Il2CppObjectBase)
                    // - ldsfld native int SomeClass::NativeMethodInfoPtr_Etc
                    // - call native int[UnhollowerBaseLib] UnhollowerBaseLib.IL2CPP::il2cpp_object_get_virtual_method(native int, native int)

                    replaceIdx   = i - 2;
                    replaceCount = 4;
                }
                else
                {
                    // Everything else: Just replace the static load
                    replaceIdx   = i;
                    replaceCount = 1;
                }
                break;
            }

            if (!found)
            {
                MelonLogger.Error("Harmony transpiler could not rewrite Unhollower method. Expect a stack overflow.");
                return(instructions);
            }

            CodeInstruction[] replacement =
            {
                new CodeInstruction(OpCodes.Ldc_I8, copiedMethodInfo.ToInt64()),
                new CodeInstruction(OpCodes.Conv_I)
            };

            instructions.RemoveRange(replaceIdx, replaceCount);
            instructions.InsertRange(replaceIdx, replacement);

            return(instructions);
        }
Пример #2
0
		public static void AddTranspiler(PatchInfo patchInfo, string owner, HarmonyMethod info)
		{
			if (info == null || info.method == null) return;

			var priority = info.prioritiy == -1 ? Priority.Normal : info.prioritiy;
			var before = info.before ?? new string[0];
			var after = info.after ?? new string[0];

			patchInfo.AddTranspiler(info.method, owner, priority, before, after);
		}
Пример #3
0
        public static byte[] Serialize(this PatchInfo patchInfo)
        {
            byte[] buffer;
            using (var memoryStream = new MemoryStream())
            {
                new BinaryFormatter().Serialize(memoryStream, patchInfo);
                buffer = memoryStream.GetBuffer();
            }

            return(buffer);
        }
Пример #4
0
        public static byte[] Serialize(this PatchInfo patchInfo)
        {
#pragma warning disable XS0001
            using (var streamMemory = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(streamMemory, patchInfo);
                return(streamMemory.GetBuffer());
            }
#pragma warning restore XS0001
        }
 internal static PatchInfo GetPatchInfo(MethodBase method)
 {
     lock (patchInfos)
     {
         if (!patchInfos.TryGetValue(method, out var info))
         {
             patchInfos[method] = info = new PatchInfo();
         }
         return(info);
     }
 }
Пример #6
0
        public static void AddPostfix(PatchInfo patchInfo, string owner, HarmonyMethod info)
        {
            if (info?.method == null)
            {
                return;
            }
            var priority = info.prioritiy == -1 ? 400 : info.prioritiy;
            var before   = info.before ?? new string[0];
            var after    = info.after ?? new string[0];

            patchInfo.AddPostfix(info.method, owner, priority, before, after);
        }
        public static DynamicMethod UpdateWrapper(MethodBase original, PatchInfo patchInfo, string instanceID)
        {
            var  sortedPrefixes    = GetSortedPatchMethods(original, patchInfo.prefixes);
            var  sortedPostfixes   = GetSortedPatchMethods(original, patchInfo.postfixes);
            var  sortedTranspilers = GetSortedPatchMethods(original, patchInfo.transpilers);
            bool isIl2Cpp          = UnhollowerSupport.IsGeneratedAssemblyType(original.DeclaringType);

            if (isIl2Cpp)
            {
                if (sortedTranspilers.Count > 0)
                {
                    throw new NotSupportedException("IL2CPP patches cannot use transpilers (got " + sortedTranspilers.Count + ")");
                }

                if (patchInfo.copiedMethodInfoPointer == IntPtr.Zero)
                {
                    IntPtr origMethodPtr = UnhollowerSupport.MethodBaseToIl2CppMethodInfoPointer(original);
                    patchInfo.copiedMethodInfoPointer = CopyMethodInfoStruct(origMethodPtr);
                    HarmonySharedState.UpdatePatchInfo(original, patchInfo);
                }

                sortedTranspilers.Add(AccessTools.DeclaredMethod(typeof(PatchFunctions), "UnhollowerTranspiler"));
            }

            var replacement = MethodPatcher.CreatePatchedMethod(original, instanceID, sortedPrefixes, sortedPostfixes, sortedTranspilers);

            if (replacement == null)
            {
                throw new MissingMethodException("Cannot create dynamic replacement for " + original.FullDescription());
            }

            if (isIl2Cpp)
            {
                DynamicMethod il2CppShim = CreateIl2CppShim(replacement, original);
                InstallIl2CppPatch(patchInfo, il2CppShim);
                PatchTools.RememberObject(original, new PotatoTuple {
                    First = replacement, Second = il2CppShim
                });
            }
            else
            {
                var errorString = Memory.DetourMethod(original, replacement);
                if (errorString != null)
                {
                    throw new FormatException("Method " + original.FullDescription() + " cannot be patched. Reason: " + errorString);
                }

                PatchTools.RememberObject(original, replacement);                 // no gc for new value + release old value to gc
            }

            return(replacement);
        }
Пример #8
0
		private static void InstallIl2CppPatch(PatchInfo patchInfo, DynamicMethod il2CppShim)
		{
			IntPtr methodInfoPtr = patchInfo.copiedMethodInfoPointer;
			IntPtr oldDetourPtr = patchInfo.methodDetourPointer;
			IntPtr newDetourPtr = il2CppShim.MethodHandle.GetFunctionPointer();

			if (oldDetourPtr != IntPtr.Zero) {
				Imports.Unhook(methodInfoPtr, oldDetourPtr);
			}

			Imports.Hook(methodInfoPtr, newDetourPtr);
			patchInfo.methodDetourPointer = newDetourPtr;
		}
Пример #9
0
        /// <summary>Adds a postfix</summary>
        /// <param name="patchInfo">The patch info</param>
        /// <param name="owner">The owner (Harmony ID)</param>
        /// <param name="info">The annotation info</param>
        ///
        internal static void AddPostfix(PatchInfo patchInfo, string owner, HarmonyMethod info)
        {
            if (info == null || info.method == null)
            {
                return;
            }

            var priority = info.priority == -1 ? Priority.Normal : info.priority;
            var before   = info.before ?? new string[0];
            var after    = info.after ?? new string[0];

            patchInfo.AddPostfix(info.method, owner, priority, before, after);
        }
        private static void InstallIl2CppPatch(PatchInfo patchInfo, DynamicMethod il2CppShim)
        {
            IntPtr methodInfoPtr = patchInfo.copiedMethodInfoPointer;
            IntPtr oldDetourPtr  = patchInfo.methodDetourPointer;
            IntPtr newDetourPtr  = il2CppShim.MethodHandle.GetFunctionPointer();

            if (oldDetourPtr != IntPtr.Zero)
            {
                MelonUtils.NativeHookDetach(methodInfoPtr, oldDetourPtr);
            }

            MelonUtils.NativeHookAttach(methodInfoPtr, newDetourPtr);
            patchInfo.methodDetourPointer = newDetourPtr;
        }
Пример #11
0
        public static void AddInfix(PatchInfo patchInfo, string owner, HarmonyMethod info)
        {
            if (info == null || info.method == null)
            {
                return;
            }

            var priority = info.prioritiy == -1 ? Priority.Normal : info.prioritiy;
            var before   = info.before ?? new string[0];
            var after    = info.after ?? new string[0];

            var patch = new Patch(info.method, patchInfo.processors.Count() + 1, owner, priority, before, after);

            patchInfo.processors.Add(patch);
        }
Пример #12
0
 public void Patch()
 {
     lock (locker)
     {
         var patchInfo = HarmonySharedState.GetPatchInfo(original);
         if (patchInfo == null)
         {
             patchInfo = new PatchInfo();
         }
         PatchFunctions.AddPrefix(patchInfo, instance.Id, prefix);
         PatchFunctions.AddPostfix(patchInfo, instance.Id, postfix);
         PatchFunctions.AddInfix(patchInfo, instance.Id, infix);
         PatchFunctions.UpdateWrapper(original, patchInfo);
         HarmonySharedState.UpdatePatchInfo(original, patchInfo);
     }
 }
Пример #13
0
        public List <DynamicMethod> Patch()
        {
            lock (locker)
            {
                var dynamicMethods = new List <DynamicMethod>();
                foreach (var original in originals)
                {
                    if (original == null)
                    {
                        throw new NullReferenceException("original");
                    }

                    if ((original.DeclaringType.Assembly.GetCustomAttributes(typeof(HarmonyShield), false).Count() > 0) ||
                        (original.DeclaringType.GetCustomAttributes(typeof(HarmonyShield), false).Count() > 0) ||
                        (original.GetCustomAttributes(typeof(HarmonyShield), false).Count() > 0))
                    {
                        continue;
                    }

                    if (MelonDebug.IsEnabled() && UnhollowerSupport.IsGeneratedAssemblyType(original.DeclaringType))
                    {
                        WarnIfTargetMethodInlined(original);
                    }

                    var individualPrepareResult = RunMethod <HarmonyPrepare, bool>(true, original);
                    if (individualPrepareResult)
                    {
                        var patchInfo = HarmonySharedState.GetPatchInfo(original);
                        if (patchInfo == null)
                        {
                            patchInfo = new PatchInfo();
                        }

                        PatchFunctions.AddPrefix(patchInfo, instance.Id, prefix);
                        PatchFunctions.AddPostfix(patchInfo, instance.Id, postfix);
                        PatchFunctions.AddTranspiler(patchInfo, instance.Id, transpiler);
                        dynamicMethods.Add(PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id));

                        HarmonySharedState.UpdatePatchInfo(original, patchInfo);

                        RunMethod <HarmonyCleanup>(original);
                    }
                }
                return(dynamicMethods);
            }
        }
Пример #14
0
        public void Unpatch(MethodInfo patch)
        {
            lock (locker)
            {
                foreach (var original in originals)
                {
                    var patchInfo = HarmonySharedState.GetPatchInfo(original);
                    if (patchInfo == null)
                    {
                        patchInfo = new PatchInfo();
                    }

                    PatchFunctions.RemovePatch(patchInfo, patch);
                    PatchHandler.Get(original).Apply();
                }
            }
        }
Пример #15
0
        public void Patch()
        {
            lock (locker)
            {
                var patchInfo = HarmonySharedState.GetPatchInfo(original);
                if (patchInfo == null)
                {
                    patchInfo = new PatchInfo();
                }

                PatchFunctions.AddPrefix(patchInfo, instance.Id, prefix);
                PatchFunctions.AddPostfix(patchInfo, instance.Id, postfix);
                PatchFunctions.AddTranspiler(patchInfo, instance.Id, transpiler);

                PatchHandler.Get(original).Apply();
            }
        }
Пример #16
0
        public static DynamicMethod UpdateWrapper(MethodBase original, PatchInfo patchInfo, string instanceID)
        {
            var sortedPrefixes    = GetSortedPatchMethods(original, patchInfo.prefixes);
            var sortedPostfixes   = GetSortedPatchMethods(original, patchInfo.postfixes);
            var sortedTranspilers = GetSortedPatchMethods(original, patchInfo.transpilers);

            var replacement = MethodPatcher.CreatePatchedMethod(original, instanceID, sortedPrefixes, sortedPostfixes, sortedTranspilers);

            if (replacement == null)
            {
                throw new MissingMethodException("Cannot create dynamic replacement for " + original.FullDescription());
            }

            var errorString = Memory.DetourMethod(original, replacement);

            if (errorString != null)
            {
                throw new FormatException("Method " + original.FullDescription() + " cannot be patched. Reason: " + errorString);
            }

            if (UnhollowerSupport.IsGeneratedAssemblyType(original.DeclaringType))
            {
                var il2CppShim    = CreateIl2CppShim(replacement, original);
                var origMethodPtr = UnhollowerSupport.MethodBaseToIntPtr(original);
                var oldDetourPtr  = patchInfo.methodDetourPointer;
                var newDetourPtr  = il2CppShim.MethodHandle.GetFunctionPointer();

                if (oldDetourPtr != IntPtr.Zero)
                {
                    Imports.Unhook(origMethodPtr, oldDetourPtr);
                }
                Imports.Hook(origMethodPtr, newDetourPtr);
                patchInfo.methodDetourPointer = newDetourPtr;

                PatchTools.RememberObject(original, new PotatoTuple {
                    First = replacement, Second = il2CppShim
                });
            }
            else
            {
                PatchTools.RememberObject(original, replacement);                 // no gc for new value + release old value to gc
            }
            return(replacement);
        }
Пример #17
0
        public void Unpatch(MethodInfo patch)
        {
            lock (locker)
            {
                foreach (var original in originals)
                {
                    var patchInfo = HarmonySharedState.GetPatchInfo(original);
                    if (patchInfo == null)
                    {
                        patchInfo = new PatchInfo();
                    }

                    PatchFunctions.RemovePatch(patchInfo, patch);
                    PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id);

                    HarmonySharedState.UpdatePatchInfo(original, patchInfo);
                }
            }
        }
Пример #18
0
        public static void UpdateWrapper(MethodBase original, PatchInfo patchInfo)
        {
            var sortedPrefixes    = GetSortedPatchMethods(original, patchInfo.prefixes);
            var sortedPostfixes   = GetSortedPatchMethods(original, patchInfo.postfixes);
            var sortedTranspilers = GetSortedPatchMethods(original, patchInfo.transpilers);

            var originalCodeStart = Memory.GetMethodStart(original);

            // If we're overwriting an old patch, restore the original 12 (or 6) bytes of the method beforehand
            object oldHandle;

            if (PatchTools.RecallObject(original, out oldHandle))
            {
                var oldPatchHandle = (PatchHandle)oldHandle;
                Memory.WriteBytes(originalCodeStart, oldPatchHandle.OverwrittenCode);
            }

            if (patchInfo.postfixes.Length + patchInfo.prefixes.Length + patchInfo.transpilers.Length == 0)
            {
                // No patches, can just leave the original method intact
                PatchTools.ForgetObject(originalCodeStart);
                return;
            }

            var replacement = MethodPatcher.CreatePatchedMethod(original, sortedPrefixes, sortedPostfixes, sortedTranspilers);

            if (replacement == null)
            {
                throw new MissingMethodException("Cannot create dynamic replacement for " + original);
            }
            var patchCodeStart = Memory.GetMethodStart(replacement);

            // This part effectively corrupts the original compiled method, so we should prepare to restore the overwritten part later
            // (It doesn't look like it breaks something, but... better safe than sorry?)
            var oldBytes = new byte[(IntPtr.Size == sizeof(long)) ? 12 : 6];

            Marshal.Copy((IntPtr)originalCodeStart, oldBytes, 0, oldBytes.Length);
            // Store code being overwritten by the jump for the restoration
            PatchTools.RememberObject(original, new PatchHandle {
                PatchedMethod = replacement, OverwrittenCode = oldBytes
            });
            Memory.WriteJump(originalCodeStart, patchCodeStart);
        }
Пример #19
0
        public static void UpdateWrapper(MethodBase original, PatchInfo patchInfo)
        {
            var sortedPrefixes   = GetSortedPatchMethods(original, patchInfo.prefixes);
            var sortedPostfixes  = GetSortedPatchMethods(original, patchInfo.postfixes);
            var sortedProcessors = GetSortedProcessors(original, patchInfo.processors);

            var replacement = MethodPatcher.CreatePatchedMethod(original, sortedPrefixes, sortedPostfixes, sortedProcessors);

            if (replacement == null)
            {
                throw new MissingMethodException("Cannot create dynamic replacement for " + original);
            }

            var originalCodeStart = Memory.GetMethodStart(original);
            var patchCodeStart    = Memory.GetMethodStart(replacement);

            Memory.WriteJump(originalCodeStart, patchCodeStart);

            PatchTools.RememberObject(original, replacement);             // no gc for new value + release old value to gc
        }
Пример #20
0
        public static void UpdateWrapper(MethodBase original, PatchInfo patchInfo, PatchFlags flags)
        {
            var sortedPrefixes    = GetSortedPatchMethods(original, patchInfo.prefixes);
            var sortedPostfixes   = GetSortedPatchMethods(original, patchInfo.postfixes);
            var sortedTranspilers = GetSortedPatchMethods(original, patchInfo.transpilers).Select(m => TranspilerImpl.From(m)).ToList();

            var replacement = MethodPatcher.CreatePatchedMethod(original, sortedPrefixes, sortedPostfixes, sortedTranspilers, flags);

            if (replacement == null)
            {
                throw new MissingMethodException("Cannot create dynamic replacement for " + original);
            }

            patchInfo.patchdata.orgaddress = Memory.GetMethodStart(original);
            patchInfo.patchdata.jmpaddress = Memory.GetMethodStart(replacement);

            Memory.WriteJump(patchInfo.patchdata.orgaddress, patchInfo.patchdata.jmpaddress, out patchInfo.patchdata.orgbytes, out patchInfo.patchdata.jmpbytes);

            PatchTools.RememberObject(original, replacement);             // no gc for new value + release old value to gc
        }
Пример #21
0
        /// <summary>Creates new dynamic method with the latest patches and detours the original method</summary>
        /// <param name="original">The original method</param>
        /// <param name="patchInfo">Information describing the patches</param>
        /// <param name="instanceID">Harmony ID</param>
        /// <returns>The newly created dynamic method</returns>
        ///
        internal static DynamicMethod UpdateWrapper(MethodBase original, PatchInfo patchInfo, string instanceID)
        {
            var sortedPrefixes    = GetSortedPatchMethods(original, patchInfo.prefixes);
            var sortedPostfixes   = GetSortedPatchMethods(original, patchInfo.postfixes);
            var sortedTranspilers = GetSortedPatchMethods(original, patchInfo.transpilers);

            var replacement = MethodPatcher.CreatePatchedMethod(original, instanceID, sortedPrefixes, sortedPostfixes, sortedTranspilers);

            if (replacement == null)
            {
                throw new MissingMethodException("Cannot create dynamic replacement for " + original.FullDescription());
            }

            var errorString = Memory.DetourMethod(original, replacement);

            if (errorString != null)
            {
                throw new FormatException("Method " + original.FullDescription() + " cannot be patched. Reason: " + errorString);
            }

            PatchTools.RememberObject(original, replacement);             // no gc for new value + release old value to gc

            return(replacement);
        }
Пример #22
0
 public static void RemovePatch(PatchInfo patchInfo, MethodInfo patch)
 {
     patchInfo.RemovePatch(patch);
 }
Пример #23
0
 public static void RemoveTranspiler(PatchInfo patchInfo, string owner)
 {
     patchInfo.RemoveTranspiler(owner);
 }
Пример #24
0
 public static void RemovePostfix(PatchInfo patchInfo, string owner)
 {
     patchInfo.RemovePostfix(owner);
 }
Пример #25
0
 public static DynamicMethod UpdateWrapper(MethodBase original, PatchInfo patchInfo, string instanceID)
 {
     throw new NotImplementedException("PatchFunctions.UpdateWrapper not supported");
 }
 internal static void UpdatePatchInfo(MethodBase method, PatchInfo patchInfo)
 {
     GetState()[method] = patchInfo.Serialize();
 }
Пример #27
0
 /// <summary>Removes a patch method</summary>
 /// <param name="patchInfo">The patch info</param>
 /// <param name="patch">The patch method</param>
 ///
 internal static void RemovePatch(PatchInfo patchInfo, MethodInfo patch)
 {
     patchInfo.RemovePatch(patch);
 }
Пример #28
0
 /// <summary>Removes a prefix</summary>
 /// <param name="patchInfo">The patch info</param>
 /// <param name="owner">The owner (Harmony ID)</param>
 ///
 internal static void RemovePrefix(PatchInfo patchInfo, string owner)
 {
     patchInfo.RemovePrefix(owner);
 }