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); }
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); }
public static byte[] Serialize(this PatchInfo patchInfo) { byte[] buffer; using (var memoryStream = new MemoryStream()) { new BinaryFormatter().Serialize(memoryStream, patchInfo); buffer = memoryStream.GetBuffer(); } return(buffer); }
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); } }
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); }
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; }
/// <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; }
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); }
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); } }
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); } }
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(); } } }
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(); } }
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); }
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); } } }
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); }
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 }
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 }
/// <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); }
public static void RemovePatch(PatchInfo patchInfo, MethodInfo patch) { patchInfo.RemovePatch(patch); }
public static void RemoveTranspiler(PatchInfo patchInfo, string owner) { patchInfo.RemoveTranspiler(owner); }
public static void RemovePostfix(PatchInfo patchInfo, string owner) { patchInfo.RemovePostfix(owner); }
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(); }
/// <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); }
/// <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); }