Improve parameter verification logic in HookVerifier (#2596)

* Improve parameter verification logic in HookVerifier

Refactor HookVerifier to enhance parameter type checking and add utility methods for size calculations.

* Reverse bool check

* Fix type size check on return type

* Fix non static member in static class

* Fix compiler errors

* Fix SizeOf calls

* Fix IsStruct call

* Cleanup some warnings
This commit is contained in:
wolfcomp 2026-01-27 17:30:58 +01:00 committed by GitHub
parent 8f8f4faa12
commit afa7b0c1f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,8 +1,13 @@
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Logging.Internal; using Dalamud.Logging.Internal;
using InteropGenerator.Runtime;
namespace Dalamud.Hooking.Internal.Verification; namespace Dalamud.Hooking.Internal.Verification;
/// <summary> /// <summary>
@ -19,11 +24,13 @@ internal static class HookVerifier
new( new(
"ActorControlSelf", "ActorControlSelf",
"E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64", "E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64",
typeof(ActorControlSelfDelegate), typeof(ActorControlSelfDelegate), // TODO: change this to CS delegate
"Signature changed in Patch 7.4") // 7.4 (new parameters) "Signature changed in Patch 7.4") // 7.4 (new parameters)
]; ];
private delegate void ActorControlSelfDelegate(uint category, uint eventId, uint param1, uint param2, uint param3, uint param4, uint param5, uint param6, uint param7, uint param8, ulong targetId, byte param9); private static readonly string ClientStructsInteropNamespacePrefix = string.Join(".", nameof(FFXIVClientStructs), nameof(FFXIVClientStructs.Interop));
private delegate void ActorControlSelfDelegate(uint category, uint eventId, uint param1, uint param2, uint param3, uint param4, uint param5, uint param6, uint param7, uint param8, ulong targetId, byte param9); // TODO: change this to CS delegate
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HookVerifier"/> class. /// Initializes a new instance of the <see cref="HookVerifier"/> class.
@ -71,7 +78,7 @@ internal static class HookVerifier
var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!; var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!;
// Compare Return Type // Compare Return Type
var mismatch = passedInvoke.ReturnType != enforcedInvoke.ReturnType; var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType);
// Compare Parameter Count // Compare Parameter Count
var passedParams = passedInvoke.GetParameters(); var passedParams = passedInvoke.GetParameters();
@ -86,7 +93,7 @@ internal static class HookVerifier
// Compare Parameter Types // Compare Parameter Types
for (var i = 0; i < passedParams.Length; i++) for (var i = 0; i < passedParams.Length; i++)
{ {
if (passedParams[i].ParameterType != enforcedParams[i].ParameterType) if (!CheckParam(passedParams[i].ParameterType, enforcedParams[i].ParameterType))
{ {
mismatch = true; mismatch = true;
break; break;
@ -100,6 +107,45 @@ internal static class HookVerifier
} }
} }
private static bool CheckParam(Type paramLeft, Type paramRight)
{
var sameType = paramLeft == paramRight;
return sameType || SizeOf(paramLeft) == SizeOf(paramRight);
}
private static int SizeOf(Type type)
{
return type switch {
_ when type == typeof(sbyte) || type == typeof(byte) || type == typeof(bool) => 1,
_ when type == typeof(char) || type == typeof(short) || type == typeof(ushort) || type == typeof(Half) => 2,
_ when type == typeof(int) || type == typeof(uint) || type == typeof(float) => 4,
_ when type == typeof(long) || type == typeof(ulong) || type == typeof(double) || type.IsPointer || type.IsFunctionPointer || type.IsUnmanagedFunctionPointer || (type.Name == "Pointer`1" && type.Namespace.AsSpan().SequenceEqual(ClientStructsInteropNamespacePrefix)) || type == typeof(CStringPointer) => 8,
_ when type.Name.StartsWith("FixedSizeArray") => SizeOf(type.GetGenericArguments()[0]) * int.Parse(type.Name[14..type.Name.IndexOf('`')]),
_ when type.GetCustomAttribute<InlineArrayAttribute>() is { Length: var length } => SizeOf(type.GetGenericArguments()[0]) * length,
_ when IsStruct(type) && !type.IsGenericType && (type.StructLayoutAttribute?.Value ?? LayoutKind.Sequential) != LayoutKind.Sequential => type.StructLayoutAttribute?.Size ?? (int?)typeof(Unsafe).GetMethod("SizeOf")?.MakeGenericMethod(type).Invoke(null, null) ?? 0,
_ when type.IsEnum => SizeOf(Enum.GetUnderlyingType(type)),
_ when type.IsGenericType => Marshal.SizeOf(Activator.CreateInstance(type)!),
_ => GetSizeOf(type),
};
}
private static int GetSizeOf(Type type)
{
try
{
return Marshal.SizeOf(Activator.CreateInstance(type)!);
}
catch
{
return 0;
}
}
private static bool IsStruct(Type type)
{
return type != typeof(decimal) && type is { IsValueType: true, IsPrimitive: false, IsEnum: false };
}
private record VerificationEntry(string Name, string Signature, Type TargetDelegateType, string Message) private record VerificationEntry(string Name, string Signature, Type TargetDelegateType, string Message)
{ {
public nint Address { get; set; } public nint Address { get; set; }