Merge pull request #638 from 0x0ade/replace-harmony-with-runtimedetour

This commit is contained in:
goaaats 2021-10-11 15:50:51 +02:00 committed by GitHub
commit 85affedaa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 53 deletions

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@ -23,7 +23,6 @@ using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Ipc.Internal; using Dalamud.Plugin.Ipc.Internal;
using Dalamud.Support; using Dalamud.Support;
using HarmonyLib;
using Serilog; using Serilog;
using Serilog.Core; using Serilog.Core;
using Serilog.Events; using Serilog.Events;
@ -45,6 +44,7 @@ namespace Dalamud
private readonly ManualResetEvent unloadSignal; private readonly ManualResetEvent unloadSignal;
private readonly ManualResetEvent finishUnloadSignal; private readonly ManualResetEvent finishUnloadSignal;
private MonoMod.RuntimeDetour.Hook processMonoHook;
private bool hasDisposedPlugins = false; private bool hasDisposedPlugins = false;
#endregion #endregion
@ -357,6 +357,8 @@ namespace Dalamud
SerilogEventSink.Instance.LogLine -= SerilogOnLogLine; SerilogEventSink.Instance.LogLine -= SerilogOnLogLine;
this.processMonoHook?.Dispose();
Log.Debug("Dalamud::Dispose() OK!"); Log.Debug("Dalamud::Dispose() OK!");
} }
catch (Exception ex) catch (Exception ex)
@ -378,24 +380,6 @@ namespace Dalamud
Log.Debug("Reset ExceptionFilter, old: {0}", oldFilter); Log.Debug("Reset ExceptionFilter, old: {0}", oldFilter);
} }
/// <summary>
/// Patch method for the class Process.Handle. This patch facilitates fixing Reloaded so that it
/// uses pseudo-handles to access memory, to prevent permission errors.
/// It should never be called manually.
/// </summary>
/// <param name="__instance">The equivalent of `this`.</param>
/// <param name="__result">The result from the original method.</param>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Enforced naming for special injected parameters")]
private static void ProcessHandlePatch(Process __instance, ref IntPtr __result)
{
if (__instance.Id == Environment.ProcessId)
{
__result = (IntPtr)0xFFFFFFFF;
}
// Log.Verbose($"Process.Handle // {__instance.ProcessName} // {__result:X}");
}
private static void SerilogOnLogLine(object? sender, (string Line, LogEventLevel Level, DateTimeOffset TimeStamp, Exception? Exception) e) private static void SerilogOnLogLine(object? sender, (string Line, LogEventLevel Level, DateTimeOffset TimeStamp, Exception? Exception) e)
{ {
if (e.Exception == null) if (e.Exception == null)
@ -404,15 +388,34 @@ namespace Dalamud
Troubleshooting.LogException(e.Exception, e.Line); Troubleshooting.LogException(e.Exception, e.Line);
} }
/// <summary>
/// Patch method for the class Process.Handle. This patch facilitates fixing Reloaded so that it
/// uses pseudo-handles to access memory, to prevent permission errors.
/// It should never be called manually.
/// </summary>
/// <param name="orig">A delegate that acts as the original method.</param>
/// <param name="self">The equivalent of `this`.</param>
/// <returns>A pseudo-handle for the current process, or the result from the original method.</returns>
private static IntPtr ProcessHandlePatch(Func<Process, IntPtr> orig, Process self)
{
var result = orig(self);
if (self.Id == Environment.ProcessId)
{
result = (IntPtr)0xFFFFFFFF;
}
// Log.Verbose($"Process.Handle // {self.ProcessName} // {result:X}");
return result;
}
private void ApplyProcessPatch() private void ApplyProcessPatch()
{ {
var harmony = new Harmony("goatcorp.dalamud");
var targetType = typeof(Process); var targetType = typeof(Process);
var handleTarget = AccessTools.PropertyGetter(targetType, nameof(Process.Handle)); var handleTarget = targetType.GetProperty(nameof(Process.Handle)).GetGetMethod();
var handlePatch = AccessTools.Method(typeof(Dalamud), nameof(Dalamud.ProcessHandlePatch)); var handlePatch = typeof(Dalamud).GetMethod(nameof(Dalamud.ProcessHandlePatch), BindingFlags.NonPublic | BindingFlags.Static);
harmony.Patch(handleTarget, postfix: new(handlePatch)); this.processMonoHook = new MonoMod.RuntimeDetour.Hook(handleTarget, handlePatch);
} }
} }
} }

View file

@ -65,10 +65,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CheapLoc" Version="1.1.6" /> <PackageReference Include="CheapLoc" Version="1.1.6" />
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" /> <PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
<PackageReference Include="Lib.Harmony" Version="2.1.1" />
<PackageReference Include="Lumina" Version="3.3.0" /> <PackageReference Include="Lumina" Version="3.3.0" />
<PackageReference Include="Lumina.Excel" Version="5.50.0" /> <PackageReference Include="Lumina.Excel" Version="5.50.0" />
<PackageReference Include="MonoMod.Common" Version="21.10.8.11" /> <PackageReference Include="MonoMod.RuntimeDetour" Version="21.10.10.01" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" /> <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />

View file

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
@ -20,7 +19,6 @@ using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Exceptions; using Dalamud.Plugin.Internal.Exceptions;
using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Internal.Types;
using Dalamud.Utility; using Dalamud.Utility;
using HarmonyLib;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Dalamud.Plugin.Internal namespace Dalamud.Plugin.Internal
@ -137,6 +135,9 @@ namespace Dalamud.Plugin.Internal
Log.Error(ex, $"Error disposing {plugin.Name}"); Log.Error(ex, $"Error disposing {plugin.Name}");
} }
} }
this.assemblyLocationMonoHook?.Dispose();
this.assemblyCodeBaseMonoHook?.Dispose();
} }
/// <summary> /// <summary>
@ -1063,6 +1064,9 @@ namespace Dalamud.Plugin.Internal
/// </summary> /// </summary>
internal partial class PluginManager internal partial class PluginManager
{ {
private MonoMod.RuntimeDetour.Hook assemblyLocationMonoHook;
private MonoMod.RuntimeDetour.Hook assemblyCodeBaseMonoHook;
/// <summary> /// <summary>
/// A mapping of plugin assembly name to patch data. Used to fill in missing data due to loading /// A mapping of plugin assembly name to patch data. Used to fill in missing data due to loading
/// plugins via byte[]. /// plugins via byte[].
@ -1074,26 +1078,29 @@ namespace Dalamud.Plugin.Internal
/// This patch facilitates resolving the assembly location for plugins that are loaded via byte[]. /// This patch facilitates resolving the assembly location for plugins that are loaded via byte[].
/// It should never be called manually. /// It should never be called manually.
/// </summary> /// </summary>
/// <param name="__instance">The equivalent of `this`.</param> /// <param name="orig">A delegate that acts as the original method.</param>
/// <param name="__result">The result from the original method.</param> /// <param name="self">The equivalent of `this`.</param>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Enforced naming for special injected parameters")] /// <returns>The plugin location, or the result from the original method.</returns>
private static void AssemblyLocationPatch(Assembly __instance, ref string __result) private static string AssemblyLocationPatch(Func<Assembly, string?> orig, Assembly self)
{ {
if (string.IsNullOrEmpty(__result)) var result = orig(self);
if (string.IsNullOrEmpty(result))
{ {
foreach (var assemblyName in GetStackFrameAssemblyNames()) foreach (var assemblyName in GetStackFrameAssemblyNames())
{ {
if (PluginLocations.TryGetValue(assemblyName, out var data)) if (PluginLocations.TryGetValue(assemblyName, out var data))
{ {
__result = data.Location; result = data.Location;
break; break;
} }
} }
} }
__result ??= string.Empty; result ??= string.Empty;
Log.Verbose($"Assembly.Location // {__instance.FullName} // {__result}"); Log.Verbose($"Assembly.Location // {self.FullName} // {result}");
return result;
} }
/// <summary> /// <summary>
@ -1101,26 +1108,29 @@ namespace Dalamud.Plugin.Internal
/// This patch facilitates resolving the assembly location for plugins that are loaded via byte[]. /// This patch facilitates resolving the assembly location for plugins that are loaded via byte[].
/// It should never be called manually. /// It should never be called manually.
/// </summary> /// </summary>
/// <param name="__instance">The equivalent of `this`.</param> /// <param name="orig">A delegate that acts as the original method.</param>
/// <param name="__result">The result from the original method.</param> /// <param name="self">The equivalent of `this`.</param>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Enforced naming for special injected parameters")] /// <returns>The plugin code base, or the result from the original method.</returns>
private static void AssemblyCodeBasePatch(Assembly __instance, ref string __result) private static string AssemblyCodeBasePatch(Func<Assembly, string?> orig, Assembly self)
{ {
if (string.IsNullOrEmpty(__result)) var result = orig(self);
if (string.IsNullOrEmpty(result))
{ {
foreach (var assemblyName in GetStackFrameAssemblyNames()) foreach (var assemblyName in GetStackFrameAssemblyNames())
{ {
if (PluginLocations.TryGetValue(assemblyName, out var data)) if (PluginLocations.TryGetValue(assemblyName, out var data))
{ {
__result = data.CodeBase; result = data.CodeBase;
break; break;
} }
} }
} }
__result ??= string.Empty; result ??= string.Empty;
Log.Verbose($"Assembly.CodeBase // {__instance.FullName} // {__result}"); Log.Verbose($"Assembly.CodeBase // {self.FullName} // {result}");
return result;
} }
private static IEnumerable<string> GetStackFrameAssemblyNames() private static IEnumerable<string> GetStackFrameAssemblyNames()
@ -1140,18 +1150,16 @@ namespace Dalamud.Plugin.Internal
private void ApplyPatches() private void ApplyPatches()
{ {
var harmony = new Harmony("goatcorp.dalamud.pluginmanager");
var targetType = typeof(PluginManager).Assembly.GetType(); var targetType = typeof(PluginManager).Assembly.GetType();
var locationTarget = AccessTools.PropertyGetter(targetType, nameof(Assembly.Location)); var locationTarget = targetType.GetProperty(nameof(Assembly.Location)).GetGetMethod();
var locationPatch = AccessTools.Method(typeof(PluginManager), nameof(PluginManager.AssemblyLocationPatch)); var locationPatch = typeof(PluginManager).GetMethod(nameof(PluginManager.AssemblyLocationPatch), BindingFlags.NonPublic | BindingFlags.Static);
harmony.Patch(locationTarget, postfix: new(locationPatch)); this.assemblyLocationMonoHook = new MonoMod.RuntimeDetour.Hook(locationTarget, locationPatch);
#pragma warning disable SYSLIB0012 // Type or member is obsolete #pragma warning disable SYSLIB0012 // Type or member is obsolete
var codebaseTarget = AccessTools.PropertyGetter(targetType, nameof(Assembly.CodeBase)); var codebaseTarget = targetType.GetProperty(nameof(Assembly.CodeBase)).GetGetMethod();
var codebasePatch = AccessTools.Method(typeof(PluginManager), nameof(PluginManager.AssemblyCodeBasePatch)); var codebasePatch = typeof(PluginManager).GetMethod(nameof(PluginManager.AssemblyCodeBasePatch), BindingFlags.NonPublic | BindingFlags.Static);
harmony.Patch(codebaseTarget, postfix: new(codebasePatch)); this.assemblyCodeBaseMonoHook = new MonoMod.RuntimeDetour.Hook(codebaseTarget, codebasePatch);
#pragma warning restore SYSLIB0012 // Type or member is obsolete #pragma warning restore SYSLIB0012 // Type or member is obsolete
} }