using HarmonyLib; using IPA.Logging; using MonoMod.RuntimeDetour; using System; using System.Reflection; namespace IPA.Loader { internal static class HarmonyProtectorProxy { public static void ProtectNull() => HarmonyProtector.Protect(); } internal static class HarmonyProtector { public static void Protect() { var guid = Guid.NewGuid().ToString("N"); var id = guid.Remove(new Random().Next(7, guid.Length - 1)); var harmony = new Harmony(id); var unpatchByTypeOrId = AccessTools.Method(typeof(PatchProcessor), nameof(PatchProcessor.Unpatch), new[] { typeof(HarmonyPatchType), typeof(string) }); var unpatchMethod = AccessTools.Method(typeof(PatchProcessor), nameof(PatchProcessor.Unpatch), new[] { typeof(MethodInfo) }); var processPatchJob = AccessTools.Method(typeof(PatchClassProcessor), "ProcessPatchJob"); var patch = AccessTools.Method(typeof(PatchProcessor), nameof(PatchProcessor.Patch)); var unpatchPrefix = AccessTools.Method(typeof(HarmonyProtector), nameof(PatchProcessor_Unpatch_Prefix)); var processPatchJobPrefix = AccessTools.Method(typeof(HarmonyProtector), nameof(PatchClassProcessor_ProcessPatchJob_Prefix)); var patchPrefix = AccessTools.Method(typeof(HarmonyProtector), nameof(PatchProcessor_Patch_Prefix)); harmony.Patch(unpatchByTypeOrId, new HarmonyMethod(unpatchPrefix)); harmony.Patch(unpatchMethod, new HarmonyMethod(unpatchPrefix)); harmony.Patch(processPatchJob, new HarmonyMethod(processPatchJobPrefix)); harmony.Patch(patch, new HarmonyMethod(patchPrefix)); } private static bool ShouldBlockExecution(MethodBase methodBase) { var getIdentifiable = AccessTools.Method(typeof(DetourHelper), nameof(DetourHelper.GetIdentifiable)).GetIdentifiable(); var getValue = AccessTools.Method(typeof(FieldInfo), nameof(FieldInfo.GetValue)).GetIdentifiable(); var declaringTypeGetter = AccessTools.PropertyGetter(typeof(MemberInfo), nameof(MemberInfo.DeclaringType)).GetIdentifiable(); var methodBaseEquals = AccessTools.Method(typeof(MethodBase), nameof(MethodBase.Equals)).GetIdentifiable(); var assemblyEquals = AccessTools.Method(typeof(Assembly), nameof(Assembly.Equals)).GetIdentifiable(); var assemblyGetter = AccessTools.PropertyGetter(typeof(Type), nameof(Type.Assembly)).GetIdentifiable(); var getExecutingAssembly = AccessTools.Method(typeof(Assembly), nameof(Assembly.GetExecutingAssembly)).GetIdentifiable(); var method = methodBase.GetIdentifiable(); var assembly = method.DeclaringType!.Assembly; return method.Equals(getIdentifiable) || method.Equals(getValue) || method.Equals(declaringTypeGetter) || method.Equals(methodBaseEquals) || method.Equals(assemblyEquals) || method.Equals(assemblyGetter) || method.Equals(getExecutingAssembly) || assembly.Equals(Assembly.GetExecutingAssembly()) || assembly.Equals(typeof(Harmony).Assembly); } private static bool PatchProcessor_Patch_Prefix(MethodBase ___original, ref MethodInfo __result) { if (ShouldBlockExecution(___original)) { __result = ___original as MethodInfo; return false; } return true; } private static bool PatchClassProcessor_ProcessPatchJob_Prefix(object job) { var original = AccessTools.Field(job.GetType(), "original"); var methodBase = (MethodBase)original!.GetValue(job); return !ShouldBlockExecution(methodBase); } private static bool PatchProcessor_Unpatch_Prefix(MethodBase ___original) => !ShouldBlockExecution(___original); } }