From 19a238c808c422c0c787f1b562a3bfb6c2b33186 Mon Sep 17 00:00:00 2001 From: cake Date: Mon, 19 Jan 2026 19:24:44 +0100 Subject: [PATCH] Removal of unsafe and check if PTR is correctly aligning, checking in virtual query instead of memory --- .../PlayerData/Factories/PlayerDataFactory.cs | 85 ++++++++++++++----- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/LightlessSync/PlayerData/Factories/PlayerDataFactory.cs b/LightlessSync/PlayerData/Factories/PlayerDataFactory.cs index 9fda2bd..69647a2 100644 --- a/LightlessSync/PlayerData/Factories/PlayerDataFactory.cs +++ b/LightlessSync/PlayerData/Factories/PlayerDataFactory.cs @@ -1,6 +1,5 @@ using Dalamud.Utility; -using FFXIVClientStructs.FFXIV.Client.Game.Character; -using LightlessSync.API.Data.Enum; +using FFXIVClientStructs.FFXIV.Client.Game.Object; using LightlessSync.FileCache; using LightlessSync.Interop.Ipc; using LightlessSync.LightlessConfiguration; @@ -14,6 +13,7 @@ using Microsoft.Extensions.Logging; using System.Collections.Concurrent; using System.Diagnostics; using System.Runtime.InteropServices; +using ObjectKind = LightlessSync.API.Data.Enum.ObjectKind; namespace LightlessSync.PlayerData.Factories; @@ -119,39 +119,48 @@ public class PlayerDataFactory return null; } + private static readonly int _drawObjectOffset = + (int)Marshal.OffsetOf(nameof(GameObject.DrawObject)); + private async Task CheckForNullDrawObject(IntPtr playerPointer) - => await _dalamudUtil.RunOnFrameworkThread(() => CheckForNullDrawObjectUnsafe(playerPointer)).ConfigureAwait(false); + => await _dalamudUtil.RunOnFrameworkThread(() => + { + nint basePtr = playerPointer; - private unsafe static bool CheckForNullDrawObjectUnsafe(IntPtr playerPointer) + if (!LooksLikeUserPtr(basePtr)) + return true; + + nint drawObjAddr = basePtr + _drawObjectOffset; + + if (!TryReadIntPtr(drawObjAddr, out var drawObj)) + return true; + + return drawObj == 0; + }).ConfigureAwait(false); + + private static bool LooksLikeUserPtr(nint p) { - if (playerPointer == IntPtr.Zero) - return true; + if (p == 0) return false; - if (!IsPointerValid(playerPointer)) - return true; + ulong u = (ulong)p; - var character = (Character*)playerPointer; - if (character == null) - return true; + if (u < 0x0000_0001_0000UL) return false; + if (u > 0x0000_7FFF_FFFF_FFFFUL) return false; + if ((u & 0x7UL) != 0) return false; - var gameObject = &character->GameObject; - if (gameObject == null) - return true; - - if (!IsPointerValid((IntPtr)gameObject)) - return true; - - return gameObject->DrawObject == null; + return true; } - private static bool IsPointerValid(IntPtr ptr) + private static bool TryReadIntPtr(nint addr, out nint value) { - if (ptr == IntPtr.Zero) + value = 0; + + if (!VirtualReadable(addr)) return false; try { - _ = Marshal.ReadByte(ptr); + value = Marshal.ReadIntPtr(addr); return true; } catch @@ -160,6 +169,38 @@ public class PlayerDataFactory } } + private static bool VirtualReadable(nint addr) + { + if (VirtualQuery(addr, out var mbi, (nuint)Marshal.SizeOf()) == 0) + return false; + + const uint MEM_COMMIT = 0x1000; + const uint PAGE_NOACCESS = 0x01; + const uint PAGE_GUARD = 0x100; + + if (mbi.State != MEM_COMMIT) return false; + if ((mbi.Protect & PAGE_GUARD) != 0) return false; + if (mbi.Protect == PAGE_NOACCESS) return false; + + return true; + } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern nuint VirtualQuery(nint lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, nuint dwLength); + + + [StructLayout(LayoutKind.Sequential)] + private struct MEMORY_BASIC_INFORMATION + { + public nint BaseAddress; + public nint AllocationBase; + public uint AllocationProtect; + public nuint RegionSize; + public uint State; + public uint Protect; + public uint Type; + } + private static bool IsCacheFresh(CacheEntry entry) => (DateTime.UtcNow - entry.CreatedUtc) <= _characterCacheTtl;