mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +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,
|
||||
this.SetCursorDetour);
|
||||
|
||||
if (ReShadeAddonInterface.ReShadeHasSignature)
|
||||
if (ReShadeAddonInterface.ReShadeIsSignedByReShade)
|
||||
{
|
||||
Log.Warning("Signed ReShade binary detected");
|
||||
Service<NotificationManager>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
|
@ -30,30 +33,19 @@ internal sealed unsafe partial class ReShadeAddonInterface
|
|||
!GetProcAddressInto(m, nameof(e.ReShadeUnregisterEvent), &e.ReShadeUnregisterEvent))
|
||||
continue;
|
||||
|
||||
fixed (void* pwszFile = m.FileName)
|
||||
fixed (Guid* pguid = &WINTRUST_ACTION_GENERIC_VERIFY_V2)
|
||||
try
|
||||
{
|
||||
var wtfi = new WINTRUST_FILE_INFO
|
||||
{
|
||||
cbStruct = (uint)sizeof(WINTRUST_FILE_INFO),
|
||||
pcwszFilePath = (ushort*)pwszFile,
|
||||
hFile = default,
|
||||
pgKnownSubject = null,
|
||||
};
|
||||
var wtd = new WINTRUST_DATA
|
||||
{
|
||||
cbStruct = (uint)sizeof(WINTRUST_DATA),
|
||||
pPolicyCallbackData = null,
|
||||
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;
|
||||
var signerName = GetSignatureSignerNameWithoutVerification(m.FileName);
|
||||
ReShadeIsSignedByReShade = signerName == "ReShade";
|
||||
Log.Information(
|
||||
"ReShade DLL is signed by {signerName}. {vn}={v}",
|
||||
signerName,
|
||||
nameof(ReShadeIsSignedByReShade),
|
||||
ReShadeIsSignedByReShade);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Information(ex, "ReShade DLL did not had a valid signature.");
|
||||
}
|
||||
|
||||
ReShadeModule = m;
|
||||
|
|
@ -78,7 +70,98 @@ internal sealed unsafe partial class ReShadeAddonInterface
|
|||
|
||||
/// <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>
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,26 +69,28 @@ internal static unsafe class SwapChainHelper
|
|||
/// <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
|
||||
{
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
|
||||
// For any given COM object (also known as a COM component), a specific query for the IUnknown interface on any
|
||||
// of the object's interfaces must always return the same pointer value.
|
||||
using var psc = default(ComPtr<IDXGISwapChain>);
|
||||
fixed (Guid* piid = &IID.IID_IDXGISwapChain)
|
||||
{
|
||||
if (punk->QueryInterface(piid, (void**)psc.GetAddressOf()).FAILED)
|
||||
return false;
|
||||
}
|
||||
|
||||
var gdsc = GameDeviceSwapChain;
|
||||
if (gdsc is null || punk is null)
|
||||
return IsGameDeviceSwapChain(psc.Get());
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
fixed (Guid* iid = &IID.IID_IUnknown)
|
||||
{
|
||||
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();
|
||||
}
|
||||
return desc1.OutputWindow == desc2.OutputWindow;
|
||||
}
|
||||
|
||||
/// <summary>Wait for the game to have finished initializing the IDXGISwapChain.</summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue