mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-30 12:23:39 +01:00
100 lines
2.8 KiB
C#
100 lines
2.8 KiB
C#
using System.Runtime.InteropServices;
|
|
|
|
using Reloaded.Hooks.Definitions;
|
|
|
|
namespace Dalamud.Hooking.Internal;
|
|
|
|
/// <summary>
|
|
/// This class represents a callsite hook. Only the specific address's instructions are replaced with this hook.
|
|
/// This is a destructive operation, no other callsite hooks can coexist at the same address.
|
|
///
|
|
/// There's no .Original for this hook type.
|
|
/// This is only intended for be for functions where the parameters provided allow you to invoke the original call.
|
|
///
|
|
/// This class was specifically added for hooking virtual function callsites.
|
|
/// Only the specific callsite hooked is modified, if the game calls the virtual function from other locations this hook will not be triggered.
|
|
/// </summary>
|
|
/// <typeparam name="T">Delegate signature for this hook.</typeparam>
|
|
internal class CallHook<T> : IDalamudHook where T : Delegate
|
|
{
|
|
private readonly Reloaded.Hooks.AsmHook asmHook;
|
|
|
|
private T? detour;
|
|
private bool activated;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="CallHook{T}"/> class.
|
|
/// </summary>
|
|
/// <param name="address">Address of the instruction to replace.</param>
|
|
/// <param name="detour">Delegate to invoke.</param>
|
|
internal CallHook(nint address, T detour)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(detour);
|
|
|
|
this.detour = detour;
|
|
this.Address = address;
|
|
|
|
var detourPtr = Marshal.GetFunctionPointerForDelegate(this.detour);
|
|
var code = new[]
|
|
{
|
|
"use64",
|
|
$"mov rax, 0x{detourPtr:X8}",
|
|
"call rax",
|
|
};
|
|
|
|
var opt = new AsmHookOptions
|
|
{
|
|
PreferRelativeJump = true,
|
|
Behaviour = Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour.DoNotExecuteOriginal,
|
|
MaxOpcodeSize = 5,
|
|
};
|
|
|
|
this.asmHook = new Reloaded.Hooks.AsmHook(code, (nuint)address, opt);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the hook is enabled.
|
|
/// </summary>
|
|
public bool IsEnabled => this.asmHook.IsEnabled;
|
|
|
|
/// <inheritdoc/>
|
|
public IntPtr Address { get; }
|
|
|
|
/// <inheritdoc/>
|
|
public string BackendName => "Reloaded AsmHook";
|
|
|
|
/// <inheritdoc/>
|
|
public bool IsDisposed => this.detour == null;
|
|
|
|
/// <summary>
|
|
/// Starts intercepting a call to the function.
|
|
/// </summary>
|
|
public void Enable()
|
|
{
|
|
if (!this.activated)
|
|
{
|
|
this.activated = true;
|
|
this.asmHook.Activate();
|
|
return;
|
|
}
|
|
|
|
this.asmHook.Enable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops intercepting a call to the function.
|
|
/// </summary>
|
|
public void Disable()
|
|
{
|
|
this.asmHook.Disable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a hook from the current process.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
this.asmHook.Disable();
|
|
this.detour = null;
|
|
}
|
|
}
|