mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 05:04:15 +01:00
Change ReShade related detections (#1965)
* Changed ReShade w/o addon support detection to compare the name of the signer to the string "ReShade", so that any false positives stemming from use of other injector do not trigger warnings. * Changed main SwapChain detection to be done by comparing the HWND of window that the SwapChain is attached to.
This commit is contained in:
parent
4383a5747d
commit
f1a1f176c3
3 changed files with 127 additions and 42 deletions
|
|
@ -772,7 +772,7 @@ internal partial class InterfaceManager : IInternalDisposableService
|
||||||
0,
|
0,
|
||||||
this.SetCursorDetour);
|
this.SetCursorDetour);
|
||||||
|
|
||||||
if (ReShadeAddonInterface.ReShadeHasSignature)
|
if (ReShadeAddonInterface.ReShadeIsSignedByReShade)
|
||||||
{
|
{
|
||||||
Log.Warning("Signed ReShade binary detected");
|
Log.Warning("Signed ReShade binary detected");
|
||||||
Service<NotificationManager>
|
Service<NotificationManager>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
using static TerraFX.Interop.Windows.Windows;
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
@ -30,30 +33,19 @@ internal sealed unsafe partial class ReShadeAddonInterface
|
||||||
!GetProcAddressInto(m, nameof(e.ReShadeUnregisterEvent), &e.ReShadeUnregisterEvent))
|
!GetProcAddressInto(m, nameof(e.ReShadeUnregisterEvent), &e.ReShadeUnregisterEvent))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
fixed (void* pwszFile = m.FileName)
|
try
|
||||||
fixed (Guid* pguid = &WINTRUST_ACTION_GENERIC_VERIFY_V2)
|
|
||||||
{
|
{
|
||||||
var wtfi = new WINTRUST_FILE_INFO
|
var signerName = GetSignatureSignerNameWithoutVerification(m.FileName);
|
||||||
{
|
ReShadeIsSignedByReShade = signerName == "ReShade";
|
||||||
cbStruct = (uint)sizeof(WINTRUST_FILE_INFO),
|
Log.Information(
|
||||||
pcwszFilePath = (ushort*)pwszFile,
|
"ReShade DLL is signed by {signerName}. {vn}={v}",
|
||||||
hFile = default,
|
signerName,
|
||||||
pgKnownSubject = null,
|
nameof(ReShadeIsSignedByReShade),
|
||||||
};
|
ReShadeIsSignedByReShade);
|
||||||
var wtd = new WINTRUST_DATA
|
}
|
||||||
{
|
catch (Exception ex)
|
||||||
cbStruct = (uint)sizeof(WINTRUST_DATA),
|
{
|
||||||
pPolicyCallbackData = null,
|
Log.Information(ex, "ReShade DLL did not had a valid signature.");
|
||||||
pSIPClientData = null,
|
|
||||||
dwUIChoice = WTD.WTD_UI_NONE,
|
|
||||||
fdwRevocationChecks = WTD.WTD_REVOKE_NONE,
|
|
||||||
dwUnionChoice = WTD.WTD_STATEACTION_VERIFY,
|
|
||||||
hWVTStateData = default,
|
|
||||||
pwszURLReference = null,
|
|
||||||
dwUIContext = 0,
|
|
||||||
pFile = &wtfi,
|
|
||||||
};
|
|
||||||
ReShadeHasSignature = WinVerifyTrust(default, pguid, &wtd) != TRUST.TRUST_E_NOSIGNATURE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReShadeModule = m;
|
ReShadeModule = m;
|
||||||
|
|
@ -78,7 +70,98 @@ internal sealed unsafe partial class ReShadeAddonInterface
|
||||||
|
|
||||||
/// <summary>Gets a value indicating whether the loaded ReShade has signatures.</summary>
|
/// <summary>Gets a value indicating whether the loaded ReShade has signatures.</summary>
|
||||||
/// <remarks>ReShade without addon support is signed, but may not pass signature verification.</remarks>
|
/// <remarks>ReShade without addon support is signed, but may not pass signature verification.</remarks>
|
||||||
public static bool ReShadeHasSignature { get; private set; }
|
public static bool ReShadeIsSignedByReShade { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the name of the signer of a file that has a certificate embedded within, without verifying if the
|
||||||
|
/// file has a valid signature.</summary>
|
||||||
|
/// <param name="path">Path to the file.</param>
|
||||||
|
/// <returns>Name of the signer.</returns>
|
||||||
|
// https://learn.microsoft.com/en-us/previous-versions/troubleshoot/windows/win32/get-information-authenticode-signed-executables
|
||||||
|
private static string GetSignatureSignerNameWithoutVerification(ReadOnlySpan<char> path)
|
||||||
|
{
|
||||||
|
var hCertStore = default(HCERTSTORE);
|
||||||
|
var hMsg = default(HCRYPTMSG);
|
||||||
|
var pCertContext = default(CERT_CONTEXT*);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fixed (void* pwszFile = path)
|
||||||
|
{
|
||||||
|
uint dwMsgAndCertEncodingType;
|
||||||
|
uint dwContentType;
|
||||||
|
uint dwFormatType;
|
||||||
|
void* pvContext;
|
||||||
|
if (!CryptQueryObject(
|
||||||
|
CERT.CERT_QUERY_OBJECT_FILE,
|
||||||
|
pwszFile,
|
||||||
|
CERT.CERT_QUERY_CONTENT_FLAG_ALL,
|
||||||
|
CERT.CERT_QUERY_FORMAT_FLAG_ALL,
|
||||||
|
0,
|
||||||
|
&dwMsgAndCertEncodingType,
|
||||||
|
&dwContentType,
|
||||||
|
&dwFormatType,
|
||||||
|
&hCertStore,
|
||||||
|
&hMsg,
|
||||||
|
&pvContext))
|
||||||
|
{
|
||||||
|
throw new Win32Exception("CryptQueryObject");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pcb = 0u;
|
||||||
|
if (!CryptMsgGetParam(hMsg, CMSG.CMSG_SIGNER_INFO_PARAM, 0, null, &pcb))
|
||||||
|
throw new Win32Exception("CryptMsgGetParam(1)");
|
||||||
|
|
||||||
|
var signerInfo = GC.AllocateArray<byte>((int)pcb, true);
|
||||||
|
var pSignerInfo = (CMSG_SIGNER_INFO*)Unsafe.AsPointer(ref signerInfo[0]);
|
||||||
|
if (!CryptMsgGetParam(hMsg, CMSG.CMSG_SIGNER_INFO_PARAM, 0, pSignerInfo, &pcb))
|
||||||
|
throw new Win32Exception("CryptMsgGetParam(2)");
|
||||||
|
|
||||||
|
var certInfo = new CERT_INFO
|
||||||
|
{
|
||||||
|
Issuer = pSignerInfo->Issuer,
|
||||||
|
SerialNumber = pSignerInfo->SerialNumber,
|
||||||
|
};
|
||||||
|
pCertContext = CertFindCertificateInStore(
|
||||||
|
hCertStore,
|
||||||
|
X509.X509_ASN_ENCODING | PKCS.PKCS_7_ASN_ENCODING,
|
||||||
|
0,
|
||||||
|
CERT.CERT_FIND_SUBJECT_CERT,
|
||||||
|
&certInfo,
|
||||||
|
null);
|
||||||
|
if (pCertContext == default)
|
||||||
|
throw new Win32Exception("CertFindCertificateInStore");
|
||||||
|
|
||||||
|
pcb = CertGetNameStringW(
|
||||||
|
pCertContext,
|
||||||
|
CERT.CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
||||||
|
CERT.CERT_NAME_ISSUER_FLAG,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
pcb);
|
||||||
|
if (pcb == 0)
|
||||||
|
throw new Win32Exception("CertGetNameStringW(1)");
|
||||||
|
|
||||||
|
var issuerName = GC.AllocateArray<char>((int)pcb, true);
|
||||||
|
pcb = CertGetNameStringW(
|
||||||
|
pCertContext,
|
||||||
|
CERT.CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
||||||
|
CERT.CERT_NAME_ISSUER_FLAG,
|
||||||
|
null,
|
||||||
|
(ushort*)Unsafe.AsPointer(ref issuerName[0]),
|
||||||
|
pcb);
|
||||||
|
if (pcb == 0)
|
||||||
|
throw new Win32Exception("CertGetNameStringW(2)");
|
||||||
|
|
||||||
|
// The string is null-terminated.
|
||||||
|
return new(issuerName.AsSpan()[..^1]);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (pCertContext != default) CertFreeCertificateContext(pCertContext);
|
||||||
|
if (hCertStore != default) CertCloseStore(hCertStore, 0);
|
||||||
|
if (hMsg != default) CryptMsgClose(hMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct ExportsStruct
|
private struct ExportsStruct
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -69,26 +69,28 @@ internal static unsafe class SwapChainHelper
|
||||||
/// <returns><c>true</c> if the object is the game's swap chain.</returns>
|
/// <returns><c>true</c> if the object is the game's swap chain.</returns>
|
||||||
public static bool IsGameDeviceSwapChain<T>(T* punk) where T : unmanaged, IUnknown.Interface
|
public static bool IsGameDeviceSwapChain<T>(T* punk) where T : unmanaged, IUnknown.Interface
|
||||||
{
|
{
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
|
using var psc = default(ComPtr<IDXGISwapChain>);
|
||||||
// For any given COM object (also known as a COM component), a specific query for the IUnknown interface on any
|
fixed (Guid* piid = &IID.IID_IDXGISwapChain)
|
||||||
// of the object's interfaces must always return the same pointer value.
|
{
|
||||||
|
if (punk->QueryInterface(piid, (void**)psc.GetAddressOf()).FAILED)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var gdsc = GameDeviceSwapChain;
|
return IsGameDeviceSwapChain(psc.Get());
|
||||||
if (gdsc is null || punk is null)
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IsGameDeviceSwapChain{T}"/>
|
||||||
|
public static bool IsGameDeviceSwapChain(IDXGISwapChain* punk)
|
||||||
|
{
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc1;
|
||||||
|
if (punk->GetDesc(&desc1).FAILED)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc2;
|
||||||
|
if (GameDeviceSwapChain->GetDesc(&desc2).FAILED)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
fixed (Guid* iid = &IID.IID_IUnknown)
|
return desc1.OutputWindow == desc2.OutputWindow;
|
||||||
{
|
|
||||||
using var u1 = default(ComPtr<IUnknown>);
|
|
||||||
if (gdsc->QueryInterface(iid, (void**)u1.GetAddressOf()).FAILED)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
using var u2 = default(ComPtr<IUnknown>);
|
|
||||||
if (punk->QueryInterface(iid, (void**)u2.GetAddressOf()).FAILED)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return u1.Get() == u2.Get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Wait for the game to have finished initializing the IDXGISwapChain.</summary>
|
/// <summary>Wait for the game to have finished initializing the IDXGISwapChain.</summary>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue