diff --git a/src/Pipe/Features/DenuvoAuth/DenuvoAuth.cpp b/src/Pipe/Features/DenuvoAuth/DenuvoAuth.cpp index cabeab9..a7bc8b4 100644 --- a/src/Pipe/Features/DenuvoAuth/DenuvoAuth.cpp +++ b/src/Pipe/Features/DenuvoAuth/DenuvoAuth.cpp @@ -151,7 +151,7 @@ namespace { return authIt == g_processAuth.end() ? nullptr : &authIt->second; } - void EnsureScanned(ProcessAuth& auth, const ProcessKey& process) { + void EnsureScanned(ProcessAuth& auth, const ProcessKey& process, AppId_t appId) { if (auth.scanned) { LOG_PIPE_TRACE("DenuvoAuth: reusing cached protection result {} denuvo={}", process.DebugString(), auth.denuvo); @@ -160,6 +160,16 @@ namespace { auth.scanned = true; auth.denuvo = ScanProtection(process.pid).denuvoDetected; + // Fallback when signature detection misses a real Denuvo title (some builds + // slip both the OEP and legacy-string heuristics — see linked issue). Per the + // README, setAppTicket/setETicket are only used for ticket-gated (Denuvo) + // games, so an injected EncryptedAppTicket for this app is a strong "the user + // intends Denuvo auth" signal; engage the auth path instead of giving up. + if (!auth.denuvo && + !AppTicket::GetEncryptedTicketFromCredentialStore(appId).empty()) { + LOG_PIPE_INFO("DenuvoAuth: scan missed but injected eticket present; treating as Denuvo appid={}", appId); + auth.denuvo = true; + } if (!auth.denuvo) auth.stage = Stage::None; } @@ -174,7 +184,7 @@ void Apply(const PipeContext& ctx) { ProcessAuth& auth = g_processAuth[ctx.process]; g_pipeProcess[pipeKey] = ctx.process; - EnsureScanned(auth, ctx.process); + EnsureScanned(auth, ctx.process, ctx.appId); auth.OnHandshake(ctx, pipeKey); } diff --git a/src/Pipe/Features/DenuvoAuth/ProtectionScan.cpp b/src/Pipe/Features/DenuvoAuth/ProtectionScan.cpp index bb46a6e..9c80d09 100644 --- a/src/Pipe/Features/DenuvoAuth/ProtectionScan.cpp +++ b/src/Pipe/Features/DenuvoAuth/ProtectionScan.cpp @@ -252,7 +252,12 @@ namespace { const bool executable = EndsWithInsensitive(module.path, ".exe"); const bool dll = EndsWithInsensitive(module.path, ".dll"); if (!executable && !dll) continue; - if (module.size < kMinPackedModuleBytes) { + // [PATCH #117] Always scan the main game executable regardless of size. + // Denuvo packs the .exe itself, and some titles (e.g. Sniper Elite 4 + // appid 312660, 71MB) fall under the 80MB floor and were silently + // skipped -> denuvo never detected -> auth never engaged -> 8850000A. + // Keep the size floor only for DLLs (the original perf optimization). + if (!executable && module.size < kMinPackedModuleBytes) { LOG_PIPE_TRACE("DenuvoAuth: module skipped below packed size floor path={} size={} ({:.2f} MB) min={} ({:.2f} MB)", module.path, module.size,