mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-02 05:43:40 +01:00
*shade: Support variations in offset for underlying objects
This commit is contained in:
parent
184463a056
commit
733f79b5cb
1 changed files with 126 additions and 13 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
|
|
@ -26,7 +27,7 @@ internal static unsafe class ReShadePeeler
|
||||||
/// <returns><c>true</c> if peeled.</returns>
|
/// <returns><c>true</c> if peeled.</returns>
|
||||||
public static bool PeelSwapChain<T>(ComPtr<T>* comptr)
|
public static bool PeelSwapChain<T>(ComPtr<T>* comptr)
|
||||||
where T : unmanaged, IDXGISwapChain.Interface =>
|
where T : unmanaged, IDXGISwapChain.Interface =>
|
||||||
PeelIUnknown(comptr, 0x10);
|
PeelIUnknown(comptr, sizeof(IDXGISwapChain.Vtbl<IDXGISwapChain>));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Peels <see cref="ID3D12Device"/> if it is wrapped by ReShade.
|
/// Peels <see cref="ID3D12Device"/> if it is wrapped by ReShade.
|
||||||
|
|
@ -36,7 +37,7 @@ internal static unsafe class ReShadePeeler
|
||||||
/// <returns><c>true</c> if peeled.</returns>
|
/// <returns><c>true</c> if peeled.</returns>
|
||||||
public static bool PeelD3D12Device<T>(ComPtr<T>* comptr)
|
public static bool PeelD3D12Device<T>(ComPtr<T>* comptr)
|
||||||
where T : unmanaged, ID3D12Device.Interface =>
|
where T : unmanaged, ID3D12Device.Interface =>
|
||||||
PeelIUnknown(comptr, 0x10);
|
PeelIUnknown(comptr, sizeof(ID3D12Device.Vtbl<ID3D12Device>));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Peels <see cref="ID3D12CommandQueue"/> if it is wrapped by ReShade.
|
/// Peels <see cref="ID3D12CommandQueue"/> if it is wrapped by ReShade.
|
||||||
|
|
@ -46,20 +47,58 @@ internal static unsafe class ReShadePeeler
|
||||||
/// <returns><c>true</c> if peeled.</returns>
|
/// <returns><c>true</c> if peeled.</returns>
|
||||||
public static bool PeelD3D12CommandQueue<T>(ComPtr<T>* comptr)
|
public static bool PeelD3D12CommandQueue<T>(ComPtr<T>* comptr)
|
||||||
where T : unmanaged, ID3D12CommandQueue.Interface =>
|
where T : unmanaged, ID3D12CommandQueue.Interface =>
|
||||||
PeelIUnknown(comptr, 0x10);
|
PeelIUnknown(comptr, sizeof(ID3D12Device.Vtbl<ID3D12Device>));
|
||||||
|
|
||||||
private static bool PeelIUnknown<T>(ComPtr<T>* comptr, nint offset)
|
private static bool PeelIUnknown<T>(ComPtr<T>* comptr, nint vtblSize)
|
||||||
where T : unmanaged, IUnknown.Interface
|
where T : unmanaged, IUnknown.Interface
|
||||||
{
|
{
|
||||||
if (comptr->Get() == null || !IsReShadedComObject(comptr->Get()))
|
var changed = false;
|
||||||
return false;
|
while (comptr->Get() != null && IsReShadedComObject(comptr->Get()))
|
||||||
|
{
|
||||||
|
// Expectation: the pointer to the underlying object should come early after the overriden vtable.
|
||||||
|
for (nint i = 8; i <= 0x20; i += 8)
|
||||||
|
{
|
||||||
|
var ppObjectBehind = (nint)comptr->Get() + i;
|
||||||
|
|
||||||
var punk = new ComPtr<IUnknown>(*(IUnknown**)((nint)comptr->Get() + offset));
|
// Is the thing directly pointed from the address an actual something in the memory?
|
||||||
using var comptr2 = default(ComPtr<T>);
|
if (!IsValidReadableMemoryAddress(ppObjectBehind, 8))
|
||||||
if (punk.As(&comptr2).FAILED)
|
continue;
|
||||||
return false;
|
|
||||||
comptr2.Swap(comptr);
|
var pObjectBehind = *(nint*)ppObjectBehind;
|
||||||
return true;
|
|
||||||
|
// Is the address of vtable readable?
|
||||||
|
if (!IsValidReadableMemoryAddress(pObjectBehind, sizeof(nint)))
|
||||||
|
continue;
|
||||||
|
var pObjectBehindVtbl = *(nint*)pObjectBehind;
|
||||||
|
|
||||||
|
// Is the vtable itself readable?
|
||||||
|
if (!IsValidReadableMemoryAddress(pObjectBehindVtbl, vtblSize))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Are individual functions in vtable executable?
|
||||||
|
var valid = true;
|
||||||
|
for (var j = 0; valid && j < vtblSize; j += sizeof(nint))
|
||||||
|
valid &= IsValidExecutableMemoryAddress(*(nint*)(pObjectBehindVtbl + j), 1);
|
||||||
|
if (!valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Interpret the object as an IUnknown.
|
||||||
|
// Note that `using` is not used, and `Attach` is used. We do not alter the reference count yet.
|
||||||
|
var punk = default(ComPtr<IUnknown>);
|
||||||
|
punk.Attach((IUnknown*)pObjectBehind);
|
||||||
|
|
||||||
|
// Is the IUnknown object also the type we want?
|
||||||
|
using var comptr2 = default(ComPtr<T>);
|
||||||
|
if (punk.As(&comptr2).FAILED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
comptr2.Swap(comptr);
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool BelongsInReShadeDll(nint ptr)
|
private static bool BelongsInReShadeDll(nint ptr)
|
||||||
|
|
@ -115,12 +154,21 @@ internal static unsafe class ReShadePeeler
|
||||||
private static bool IsReShadedComObject<T>(T* obj)
|
private static bool IsReShadedComObject<T>(T* obj)
|
||||||
where T : unmanaged, IUnknown.Interface
|
where T : unmanaged, IUnknown.Interface
|
||||||
{
|
{
|
||||||
|
if (!IsValidReadableMemoryAddress((nint)obj, sizeof(nint)))
|
||||||
|
return false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var vtbl = (nint**)Marshal.ReadIntPtr((nint)obj);
|
var vtbl = (nint**)Marshal.ReadIntPtr((nint)obj);
|
||||||
|
if (!IsValidReadableMemoryAddress((nint)vtbl, sizeof(nint) * 3))
|
||||||
|
return false;
|
||||||
|
|
||||||
for (var i = 0; i < 3; i++)
|
for (var i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
if (!BelongsInReShadeDll(Marshal.ReadIntPtr((nint)(vtbl + i))))
|
var pfn = Marshal.ReadIntPtr((nint)(vtbl + i));
|
||||||
|
if (!IsValidExecutableMemoryAddress(pfn, 1))
|
||||||
|
return false;
|
||||||
|
if (!BelongsInReShadeDll(pfn))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,4 +179,69 @@ internal static unsafe class ReShadePeeler
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsValidReadableMemoryAddress(nint p, nint size)
|
||||||
|
{
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
if (!IsValidUserspaceMemoryAddress(p))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
|
if (VirtualQuery((void*)p, &mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mbi is not
|
||||||
|
{
|
||||||
|
State: MEM.MEM_COMMIT,
|
||||||
|
Protect: PAGE.PAGE_READONLY or PAGE.PAGE_READWRITE or PAGE.PAGE_EXECUTE_READ
|
||||||
|
or PAGE.PAGE_EXECUTE_READWRITE,
|
||||||
|
})
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var regionSize = (nint)((mbi.RegionSize + 0xFFFUL) & ~0x1000UL);
|
||||||
|
var checkedSize = ((nint)mbi.BaseAddress + regionSize) - p;
|
||||||
|
size -= checkedSize;
|
||||||
|
p += checkedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidExecutableMemoryAddress(nint p, nint size)
|
||||||
|
{
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
if (!IsValidUserspaceMemoryAddress(p))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
|
if (VirtualQuery((void*)p, &mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mbi is not
|
||||||
|
{
|
||||||
|
State: MEM.MEM_COMMIT,
|
||||||
|
Protect: PAGE.PAGE_EXECUTE or PAGE.PAGE_EXECUTE_READ or PAGE.PAGE_EXECUTE_READWRITE
|
||||||
|
or PAGE.PAGE_EXECUTE_WRITECOPY,
|
||||||
|
})
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var regionSize = (nint)((mbi.RegionSize + 0xFFFUL) & ~0x1000UL);
|
||||||
|
var checkedSize = ((nint)mbi.BaseAddress + regionSize) - p;
|
||||||
|
size -= checkedSize;
|
||||||
|
p += checkedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool IsValidUserspaceMemoryAddress(nint p)
|
||||||
|
{
|
||||||
|
// https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/virtual-address-spaces
|
||||||
|
// A 64-bit process on 64-bit Windows has a virtual address space within the 128-terabyte range
|
||||||
|
// 0x000'00000000 through 0x7FFF'FFFFFFFF.
|
||||||
|
return p >= 0x10000 && p <= unchecked((nint)0x7FFF_FFFFFFFFUL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue