Compare commits

..

No commits in common. "a2add0369ee3f700c7ab14ee4eace522a255f8aa" and "0d533c18f8f80894c1f87f3cc001606c8dc3c656" have entirely different histories.

8 changed files with 23 additions and 180 deletions

View file

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Dalamud.Common;
using Dalamud.Configuration.Internal;
using Dalamud.Game;
using Dalamud.Hooking.Internal.Verification;
using Dalamud.Plugin.Internal;
using Dalamud.Storage;
using Dalamud.Utility;
@ -74,11 +73,6 @@ internal sealed unsafe class Dalamud : IServiceType
scanner,
Localization.FromAssets(info.AssetDirectory!, configuration.LanguageOverride));
using (Timings.Start("HookVerifier Init"))
{
HookVerifier.Initialize(scanner);
}
// Set up FFXIVClientStructs
this.SetupClientStructsResolver(cacheDir);

View file

@ -4,7 +4,6 @@ using System.Runtime.InteropServices;
using Dalamud.Configuration.Internal;
using Dalamud.Hooking.Internal;
using Dalamud.Hooking.Internal.Verification;
namespace Dalamud.Hooking;
@ -231,8 +230,6 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
if (EnvironmentConfiguration.DalamudForceMinHook)
useMinHook = true;
HookVerifier.Verify<T>(procAddress);
procAddress = HookManager.FollowJmp(procAddress);
if (useMinHook)
return new MinHookHook<T>(procAddress, detour, Assembly.GetCallingAssembly());

View file

@ -1,41 +0,0 @@
using System.Linq;
namespace Dalamud.Hooking.Internal.Verification;
/// <summary>
/// Exception thrown when a provided delegate for a hook does not match a known delegate.
/// </summary>
public class HookVerificationException : Exception
{
private HookVerificationException(string message)
: base(message)
{
}
/// <summary>
/// Create a new <see cref="HookVerificationException"/> exception.
/// </summary>
/// <param name="address">The address of the function that is being hooked.</param>
/// <param name="passed">The delegate passed by the user.</param>
/// <param name="enforced">The delegate we think is correct.</param>
/// <param name="message">Additional context to show to the user.</param>
/// <returns>The created exception.</returns>
internal static HookVerificationException Create(IntPtr address, Type passed, Type enforced, string message)
{
return new HookVerificationException(
$"Hook verification failed for address 0x{address.ToInt64():X}\n\n" +
$"Why: {message}\n" +
$"Passed Delegate: {GetSignature(passed)}\n" +
$"Correct Delegate: {GetSignature(enforced)}\n\n" +
"The hook delegate must exactly match the provided signature to prevent memory corruption and wrong data passed to originals.");
}
private static string GetSignature(Type delegateType)
{
var method = delegateType.GetMethod("Invoke");
if (method == null) return delegateType.Name;
var parameters = string.Join(", ", method.GetParameters().Select(p => p.ParameterType.Name));
return $"{method.ReturnType.Name} ({parameters})";
}
}

View file

@ -1,107 +0,0 @@
using System.Linq;
using Dalamud.Game;
using Dalamud.Logging.Internal;
namespace Dalamud.Hooking.Internal.Verification;
/// <summary>
/// Global utility that can verify whether hook delegates are correctly declared.
/// Initialized out-of-band, since Hook is instantiated all over the place without a service, so this cannot be
/// a service either.
/// </summary>
internal static class HookVerifier
{
private static readonly ModuleLog Log = new("HookVerifier");
private static readonly VerificationEntry[] ToVerify =
[
new(
"ActorControlSelf",
"E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64",
typeof(ActorControlSelfDelegate),
"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);
/// <summary>
/// Initializes a new instance of the <see cref="HookVerifier"/> class.
/// </summary>
/// <param name="scanner">Process to scan in.</param>
public static void Initialize(TargetSigScanner scanner)
{
foreach (var entry in ToVerify)
{
if (!scanner.TryScanText(entry.Signature, out var address))
{
Log.Error("Could not resolve signature for hook {Name} ({Sig})", entry.Name, entry.Signature);
continue;
}
entry.Address = address;
}
}
/// <summary>
/// Verify the hook with the provided address and exception.
/// </summary>
/// <param name="address">The address of the function we are hooking.</param>
/// <typeparam name="T">The delegate type passed by the creator of the hook.</typeparam>
/// <exception cref="HookVerificationException">Exception thrown when we think the hook is not correctly declared.</exception>
public static void Verify<T>(IntPtr address) where T : Delegate
{
var entry = ToVerify.FirstOrDefault(x => x.Address == address);
// Nothing to verify for this hook?
if (entry == null)
{
return;
}
var passedType = typeof(T);
// Directly compare delegates
if (passedType == entry.TargetDelegateType)
{
return;
}
var passedInvoke = passedType.GetMethod("Invoke")!;
var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!;
// Compare Return Type
var mismatch = passedInvoke.ReturnType != enforcedInvoke.ReturnType;
// Compare Parameter Count
var passedParams = passedInvoke.GetParameters();
var enforcedParams = enforcedInvoke.GetParameters();
if (passedParams.Length != enforcedParams.Length)
{
mismatch = true;
}
else
{
// Compare Parameter Types
for (var i = 0; i < passedParams.Length; i++)
{
if (passedParams[i].ParameterType != enforcedParams[i].ParameterType)
{
mismatch = true;
break;
}
}
}
if (mismatch)
{
throw HookVerificationException.Create(address, passedType, entry.TargetDelegateType, entry.Message);
}
}
private record VerificationEntry(string Name, string Signature, Type TargetDelegateType, string Message)
{
public nint Address { get; set; }
}
}

View file

@ -420,13 +420,13 @@ internal unsafe class UiDebug
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->AtkComponentInputBase.RawString);
// ImGui.Text("Text1: "u8);
// ImGui.SameLine();
// Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText01);
//
// ImGui.Text("Text2: "u8);
// ImGui.SameLine();
// Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText02);
ImGui.Text("Text1: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText01);
ImGui.Text("Text2: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText02);
ImGui.Text("AvailableLines: "u8);
ImGui.SameLine();

View file

@ -89,14 +89,20 @@ internal unsafe class ComponentNodeTree : ResNodeTree
{
case TextInput:
var textInputComponent = (AtkComponentTextInput*)this.Component;
ImGui.Text($"InputBase Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.EvaluatedString.StringPtr))}");
ImGui.Text($"InputBase Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.RawString.StringPtr))}");
// TODO: Reenable when unknowns have been unprivated / named
// ImGui.Text($"Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText01.StringPtr))}");
// ImGui.Text($"Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText02.StringPtr))}");
ImGui.Text($"AvailableLines: {Marshal.PtrToStringAnsi(new(textInputComponent->AvailableLines.StringPtr))}");
ImGui.Text($"HighlightedAutoTranslateOptionColorPrefix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorPrefix.StringPtr))}");
ImGui.Text($"HighlightedAutoTranslateOptionColorSuffix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorSuffix.StringPtr))}");
ImGui.Text(
$"InputBase Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.EvaluatedString.StringPtr))}");
ImGui.Text(
$"InputBase Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.RawString.StringPtr))}");
ImGui.Text(
$"Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText01.StringPtr))}");
ImGui.Text(
$"Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText02.StringPtr))}");
ImGui.Text(
$"AvailableLines: {Marshal.PtrToStringAnsi(new(textInputComponent->AvailableLines.StringPtr))}");
ImGui.Text(
$"HighlightedAutoTranslateOptionColorPrefix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorPrefix.StringPtr))}");
ImGui.Text(
$"HighlightedAutoTranslateOptionColorSuffix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorSuffix.StringPtr))}");
break;
case List:
case TreeList:

View file

@ -3,7 +3,6 @@ using System.Runtime.CompilerServices;
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.Text;
using Lumina.Excel.Sheets;
using Lumina.Text;
using Lumina.Text.ReadOnly;
@ -126,15 +125,10 @@ public static class ItemUtil
if (IsEventItem(itemId))
{
// Only English, German, and French have a Name field.
// For other languages, the Name is an empty string, and the Singular field should be used instead.
language ??= dataManager.Language;
var useSingular = language is not (ClientLanguage.English or ClientLanguage.German or ClientLanguage.French);
return dataManager
.GetExcelSheet<EventItem>(language)
.TryGetRow(itemId, out var eventItem)
? (useSingular ? eventItem.Singular : eventItem.Name)
? eventItem.Name
: default;
}

@ -1 +1 @@
Subproject commit b309d1ee1f5445678a9ffffc51555a3a089f72d8
Subproject commit df206b5f61855e3ba73f93fd57bc07056698ac4a