Browse Source

Bring back `HarmonyProtector` and make it more robust

pull/100/head
Meivyn 1 year ago
parent
commit
6a4d9ec922
No known key found for this signature in database GPG Key ID: 8BDD3E48158B2F71
2 changed files with 86 additions and 0 deletions
  1. +7
    -0
      IPA.Injector/Injector.cs
  2. +79
    -0
      IPA.Loader/Loader/HarmonyProtector.cs

+ 7
- 0
IPA.Injector/Injector.cs View File

@ -137,6 +137,11 @@ namespace IPA.Injector
Loader.LibLoader.Configure();
}
private static void InstallHarmonyProtections()
{ // proxy function to delay resolution
HarmonyProtectorProxy.ProtectNull();
}
private static void InstallBootstrapPatch()
{
var sw = Stopwatch.StartNew();
@ -312,6 +317,8 @@ namespace IPA.Injector
// need to reinit streams singe Unity seems to redirect stdout
StdoutInterceptor.RedirectConsole();
InstallHarmonyProtections();
var bootstrapper = new GameObject("NonDestructiveBootstrapper").AddComponent<Bootstrapper>();
bootstrapper.Destroyed += Bootstrapper_Destroyed;
}


+ 79
- 0
IPA.Loader/Loader/HarmonyProtector.cs View File

@ -0,0 +1,79 @@
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);
}
}

Loading…
Cancel
Save