You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

79 lines
3.9 KiB

  1. using HarmonyLib;
  2. using IPA.Logging;
  3. using MonoMod.RuntimeDetour;
  4. using System;
  5. using System.Reflection;
  6. namespace IPA.Loader
  7. {
  8. internal static class HarmonyProtectorProxy
  9. {
  10. public static void ProtectNull() => HarmonyProtector.Protect();
  11. }
  12. internal static class HarmonyProtector
  13. {
  14. public static void Protect()
  15. {
  16. var guid = Guid.NewGuid().ToString("N");
  17. var id = guid.Remove(new Random().Next(7, guid.Length - 1));
  18. var harmony = new Harmony(id);
  19. var unpatchByTypeOrId = AccessTools.Method(typeof(PatchProcessor), nameof(PatchProcessor.Unpatch), new[] { typeof(HarmonyPatchType), typeof(string) });
  20. var unpatchMethod = AccessTools.Method(typeof(PatchProcessor), nameof(PatchProcessor.Unpatch), new[] { typeof(MethodInfo) });
  21. var processPatchJob = AccessTools.Method(typeof(PatchClassProcessor), "ProcessPatchJob");
  22. var patch = AccessTools.Method(typeof(PatchProcessor), nameof(PatchProcessor.Patch));
  23. var unpatchPrefix = AccessTools.Method(typeof(HarmonyProtector), nameof(PatchProcessor_Unpatch_Prefix));
  24. var processPatchJobPrefix = AccessTools.Method(typeof(HarmonyProtector), nameof(PatchClassProcessor_ProcessPatchJob_Prefix));
  25. var patchPrefix = AccessTools.Method(typeof(HarmonyProtector), nameof(PatchProcessor_Patch_Prefix));
  26. harmony.Patch(unpatchByTypeOrId, new HarmonyMethod(unpatchPrefix));
  27. harmony.Patch(unpatchMethod, new HarmonyMethod(unpatchPrefix));
  28. harmony.Patch(processPatchJob, new HarmonyMethod(processPatchJobPrefix));
  29. harmony.Patch(patch, new HarmonyMethod(patchPrefix));
  30. }
  31. private static bool ShouldBlockExecution(MethodBase methodBase)
  32. {
  33. var getIdentifiable = AccessTools.Method(typeof(DetourHelper), nameof(DetourHelper.GetIdentifiable)).GetIdentifiable();
  34. var getValue = AccessTools.Method(typeof(FieldInfo), nameof(FieldInfo.GetValue)).GetIdentifiable();
  35. var declaringTypeGetter = AccessTools.PropertyGetter(typeof(MemberInfo), nameof(MemberInfo.DeclaringType)).GetIdentifiable();
  36. var methodBaseEquals = AccessTools.Method(typeof(MethodBase), nameof(MethodBase.Equals)).GetIdentifiable();
  37. var assemblyEquals = AccessTools.Method(typeof(Assembly), nameof(Assembly.Equals)).GetIdentifiable();
  38. var assemblyGetter = AccessTools.PropertyGetter(typeof(Type), nameof(Type.Assembly)).GetIdentifiable();
  39. var getExecutingAssembly = AccessTools.Method(typeof(Assembly), nameof(Assembly.GetExecutingAssembly)).GetIdentifiable();
  40. var method = methodBase.GetIdentifiable();
  41. var assembly = method.DeclaringType!.Assembly;
  42. return method.Equals(getIdentifiable) ||
  43. method.Equals(getValue) ||
  44. method.Equals(declaringTypeGetter) ||
  45. method.Equals(methodBaseEquals) ||
  46. method.Equals(assemblyEquals) ||
  47. method.Equals(assemblyGetter) ||
  48. method.Equals(getExecutingAssembly) ||
  49. assembly.Equals(Assembly.GetExecutingAssembly()) ||
  50. assembly.Equals(typeof(Harmony).Assembly);
  51. }
  52. private static bool PatchProcessor_Patch_Prefix(MethodBase ___original, ref MethodInfo __result)
  53. {
  54. if (ShouldBlockExecution(___original))
  55. {
  56. __result = ___original as MethodInfo;
  57. return false;
  58. }
  59. return true;
  60. }
  61. private static bool PatchClassProcessor_ProcessPatchJob_Prefix(object job)
  62. {
  63. var original = AccessTools.Field(job.GetType(), "original");
  64. var methodBase = (MethodBase)original!.GetValue(job);
  65. return !ShouldBlockExecution(methodBase);
  66. }
  67. private static bool PatchProcessor_Unpatch_Prefix(MethodBase ___original) => !ShouldBlockExecution(___original);
  68. }
  69. }