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:
srkizer 2024-07-25 02:52:57 +09:00 committed by GitHub
parent 4383a5747d
commit f1a1f176c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 127 additions and 42 deletions

View file

@ -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>

View file

@ -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
{

View file

@ -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>