2022-10-29 00:10:08 -06:00
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
2023-03-09 11:52:28 -05:00
using BinaryObjectScanner.Interfaces ;
2023-03-07 16:59:14 -05:00
using BinaryObjectScanner.Matching ;
using BinaryObjectScanner.Wrappers ;
2022-06-22 13:40:28 -07:00
2023-03-09 23:19:27 -05:00
namespace BinaryObjectScanner.Protection
2022-06-22 13:40:28 -07:00
{
2022-10-23 21:46:58 -06:00
/// <summary>
/// Denuvo (https://irdeto.com/denuvo/) is a family of DRM originally created in 2014 by Denuvo Software Solutions, which was acquired by Irdeto in 2018 (https://www.gamesindustry.biz/irdeto-acquires-denuvo-in-bid-to-beef-up-security-in-the-games-industry).
/// Denuvo Anti-Tamper (https://irdeto.com/denuvo/anti-tamper/) is the most common, preventing game files from being modified and requiring online activation of games.
/// Denuvo Anti-Tamper datasheet: https://resources.irdeto.com/video-games/datasheet-anti-tamper
/// Lists of games with Denuvo Anti-Tamper:
/// https://store.steampowered.com/curator/26095454-Denuvo-Games/
/// https://www.pcgamingwiki.com/wiki/Denuvo
/// Denuvo Anti-Cheat (https://irdeto.com/denuvo/anti-cheat/) is a form of anti-cheat, though information on what games use it is rather sparse.
/// It was used briefly in Doom Eternal Update 1 (https://store.steampowered.com/news/app/782330/view/2187005925152148469), before being completely removed in Update 1.1 (https://store.steampowered.com/news/app/782330/view/2187006557874536446).
/// It's somewhat difficult to find other games that may use this, as quite a few sources seem to erroneously refer to Anti-Tamper as Anti-Cheat.
2022-10-29 00:10:08 -06:00
/// Support page for Denuvo Anti-Cheat: https://support.codefusion.technology/anti-cheat
2022-10-23 21:46:58 -06:00
/// Announcements for Denuvo Anti-Cheat:
/// https://irdeto.com/news/leveling-the-playing-field-with-denuvo-anti-cheat/
/// https://irdeto.com/news/denuvo-anti-cheat-now-available-on-steamworks/
/// https://irdeto.com/news/denuvo-joins-exclusive-playstation5-tools-and-middleware-program-to-offer-anti-cheat-technology-to-game-developers/
/// Denuvo Anti-Cheat datasheet: https://web.archive.org/web/20190512082146/https://resources.irdeto.com/video-games/datasheet-anti-cheat
/// Denuvo also has a number of products available, such as ones targeting mobile games:
/// https://irdeto.com/denuvo/mobile-games-protection/
/// https://resources.irdeto.com/video-games/datasheet-anti-tamper-and-anti-cheat-technology-for-mobile
/// Additional information and resources:
/// https://en.wikipedia.org/wiki/Denuvo
/// https://www.wired.com/story/empress-drm-cracking-denuvo-video-game-piracy/
/// </summary>
2022-10-29 00:10:08 -06:00
public class Denuvo : IPathCheck , IPortableExecutableCheck
2022-06-22 13:40:28 -07:00
{
2022-10-23 21:46:58 -06:00
// TODO: Investigate possible filename checks for Denuvo Anti-Tamper.
// https://www.pcgamingwiki.com/wiki/Denuvo#Redeem.exe
2022-06-22 13:40:28 -07:00
/// <inheritdoc/>
public string CheckPortableExecutable ( string file , PortableExecutable pex , bool includeDebug )
{
// Get the sections from the executable, if possible
var sections = pex ? . SectionTable ;
if ( sections = = null )
return null ;
2022-10-29 00:10:08 -06:00
// All current checks for Denuvo Anti-Cheat come from Doom Eternal Update 1 (Steam Depot 782332, Manifest 7064393210727378308).
// Found in "denuvo-anti-cheat.sys".
string name = pex . FileDescription ;
if ( name ? . Equals ( "Denuvo Anti-Cheat Driver" , StringComparison . OrdinalIgnoreCase ) = = true )
return $"Denuvo Anti-Cheat" ;
// Found in "denuvo-anti-cheat-update-service.exe"/"Denuvo Anti-Cheat Installer.exe".
if ( name ? . Equals ( "Denuvo Anti-Cheat Update Service" , StringComparison . OrdinalIgnoreCase ) = = true )
return $"Denuvo Anti-Cheat" ;
// Found in "denuvo-anti-cheat-runtime.dll".
if ( name ? . Equals ( "Denuvo Anti-Cheat Runtime" , StringComparison . OrdinalIgnoreCase ) = = true )
return $"Denuvo Anti-Cheat" ;
2022-10-23 21:46:58 -06:00
// Data sourced from:
// https://github.com/horsicq/Detect-It-Easy/blob/master/db/PE/Denuvo%20protector.2.sg
// https://github.com/horsicq/Detect-It-Easy/blob/master/db/PE/_denuvoComplete.2.sg
2022-12-03 22:17:48 -08:00
// TODO: Re-enable all Entry Point checks after implementing
2022-06-22 13:40:28 -07:00
// Denuvo Protector
2022-12-03 22:17:48 -08:00
// if (pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus && pex.EntryPointRaw != null)
// {
// byte?[] denuvoProtector = new byte?[]
// {
// 0x48, 0x8D, 0x0D, null, null, null, null, null,
// null, null, null, 0xE9, null, null, null, null,
// 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// };
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// if (pex.EntryPointRaw.StartsWith(denuvoProtector))
// return "Denuvo Protector";
// }
2022-06-22 13:40:28 -07:00
// Denuvo
var timingMatchers = new List < ContentMatchSet >
{
// Denuvo Timing
new ContentMatchSet (
new byte? [ ]
{
0x44 , 0x65 , 0x6E , 0x75 , 0x76 , 0x6F , 0x20 , 0x54 ,
0x69 , 0x6D , 0x69 , 0x6E , 0x67 ,
} , "Denuvo" )
} ;
2022-12-03 22:17:48 -08:00
// TODO: Re-enable all Entry Point checks after implementing
// if (pex.ContainsSection(".arch") || pex.ContainsSection(".srdata") || !string.IsNullOrWhiteSpace(MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, timingMatchers, includeDebug)))
// {
// if (pex.OH_Magic == OptionalHeaderType.PE32Plus)
// {
// var matchers = new List<ContentMatchSet>
// {
// // Mad Max, Metal Gear Solid: TPP, Rise of the Tomb Raider
// new ContentMatchSet(
// new ContentMatch(
// new byte?[]
// {
// 0x51, 0x52, 0x41, 0x50, 0x41, 0x51, 0x4C, 0x8D,
// null, null, null, null, null, 0x4C, 0x8D, null,
// null, null, null, null, 0x4D, 0x29, 0xC1,
// },
// end: 0
// ),
// "Denuvo v1.0 (x64)"),
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// // Lords of the Fallen, Batman: AK, Just Cause 3, Sherlock Holmes: TdD, Tales of Berseria etc
// new ContentMatchSet(
// new ContentMatch(
// new byte?[]
// {
// 0x48, 0x8D, 0x0D, null, null, null, null, 0xE9,
// null, null, null, null,
// },
// end: 0
// ),
// "Denuvo v2.0a (x64)"),
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// // Yesterday Origins
// new ContentMatchSet(
// new ContentMatch(
// new byte?[]
// {
// 0x48, 0x89, null, null, null, null, null, 0x48,
// 0x89, null, null, null, null, null, 0x4C, 0x89,
// null, null, null, null, null, 0x4C, 0x89, null,
// null, null, null, null, 0x48, 0x83, 0xFA, 0x01,
// },
// end: 0
// ),
// "Denuvo v2.0b (x64)"),
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// // Sniper Ghost Warrior 3 (beta), Dead Rising 4 (SteamStub-free)
// new ContentMatchSet(
// new ContentMatch(
// new byte?[]
// {
// null, null, null, null, null, null, null, null,
// 0x4C, 0x89, 0x1C, 0x24, 0x49, 0x89, 0xE3,
// },
// end: 0
// ),
// "Denuvo v3.0a (x64)"),
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// // Train Sim World CSX Heavy Haul
// new ContentMatchSet(
// new ContentMatch(
// new byte?[]
// {
// 0x4D, 0x8D, null, null, null, null, null, null,
// null, null, null, 0x48, 0x89, null, null, null,
// null, null, 0x48, 0x8D, null, null, 0x48, 0x89,
// null, 0x48, 0x89, null, 0x48, 0x89,
// },
// end: 0
// ),
// "Denuvo v3.0b (x64)"),
// };
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// string match = MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, matchers, includeDebug);
// if (!string.IsNullOrWhiteSpace(match))
// return match;
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// return "Denuvo (Unknown x64 Version)";
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// //// Check if steam_api64.dll present
// //if (PE.isLibraryPresent("steam_api64.dll"))
// //{
// // // Override additional info
// // sOptions = "x64 -> Steam";
// // bDetected = 1;
// //}
// //// Check if uplay_r1_loader64.dll present
// //if (PE.isLibraryPresent("uplay_r1_loader64.dll"))
// //{
// // // Override additional info
// // sOptions = "x64 -> uPlay";
// // bDetected = 1;
// //}
// //// Check if uplay_r2_loader64.dll present
// //if (PE.isLibraryPresent("uplay_r2_loader64.dll"))
// //{
// // // Override additional info
// // sOptions = "x64 -> uPlay";
// // bDetected = 1;
// //}
// //// Check if Core/Activation64.dll present
// //if (PE.isLibraryPresent("Core/Activation64.dll"))
// //{
// // // Override additional info
// // sOptions = "x64 -> Origin";
// // bDetected = 1;
// //}
// }
// else
// {
// var matchers = new List<ContentMatchSet>
// {
// // Pro Evolution Soccer 2017, Champions of Anteria
// new ContentMatchSet(
// new ContentMatch(
// new byte?[]
// {
// 0x55, 0x89, 0xE5, 0x8D, null, null, null, null,
// null, null, 0xE8, null, null, null, null, 0xE8,
// null, null, null, null, 0xE8, null, null, null,
// null, 0xE8, null, null, null, null,
// },
// end: 0
// ),
// "Denuvo v1.0 (x86)"),
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// // Romance of 13 Kingdoms, 2Dark
// new ContentMatchSet(
// new ContentMatch(
// new byte?[]
// {
// 0x8D, null, null, null, null, null, null, 0x89,
// 0x7C, 0x24, 0x04, 0x89, 0xE7,
// },
// end: 0
// ),
// "Denuvo v2.0 (x86)"),
// };
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// string match = MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, matchers, includeDebug);
// if (!string.IsNullOrWhiteSpace(match))
// return match;
2022-06-22 13:40:28 -07:00
2022-12-03 22:17:48 -08:00
// //// Check if steam_api64.dll present
// //if (PE.isLibraryPresent("steam_api.dll"))
// //{
// // // Override additional info
// // sOptions = "x86 -> Steam";
// // bDetected = 1;
// //}
// //// Check if uplay_r1_loader.dll present
// //if (PE.isLibraryPresent("uplay_r1_loader.dll"))
// //{
// // // Override additional info
// // sOptions = "x86 -> uPlay";
// // bDetected = 1;
// //}
// //// Check if Core/Activation.dll present
// //if (PE.isLibraryPresent("Core/Activation.dll"))
// //{
// // // Override additional info
// // sOptions = "x86 -> Origin";
// // bDetected = 1;
// //}
// }
// }
2022-06-22 13:40:28 -07:00
return null ;
}
2023-03-09 11:52:28 -05:00
2022-10-29 00:10:08 -06:00
/// <inheritdoc/>
public ConcurrentQueue < string > CheckDirectoryPath ( string path , IEnumerable < string > files )
{
var matchers = new List < PathMatchSet >
{
// Found in Doom Eternal Update 1 (Steam Depot 782332, Manifest 7064393210727378308).
// These files are automatically installed into an "Denuvo Anti-Cheat" folder when the game is installed.
new PathMatchSet ( new PathMatch ( "denuvo-anti-cheat.sys" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
new PathMatchSet ( new PathMatch ( "denuvo-anti-cheat-update-service.exe" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
new PathMatchSet ( new PathMatch ( "denuvo-anti-cheat-runtime.dll" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
// This file is a renamed copy of "denuvo-anti-cheat-update-service.exe" which is only seen in the folder of the main game executable after it has been run, but before Denuvo Anti-Cheat is finished installing.
new PathMatchSet ( new PathMatch ( "Denuvo Anti-Cheat Installer.exe" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
} ;
return MatchUtil . GetAllMatches ( files , matchers , any : false ) ;
}
/// <inheritdoc/>
public string CheckFilePath ( string path )
{
var matchers = new List < PathMatchSet >
{
// Found in Doom Eternal Update 1 (Steam Depot 782332, Manifest 7064393210727378308).
// These files are automatically installed into an "Denuvo Anti-Cheat" folder when the game is installed.
new PathMatchSet ( new PathMatch ( "denuvo-anti-cheat.sys" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
new PathMatchSet ( new PathMatch ( "denuvo-anti-cheat-update-service.exe" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
new PathMatchSet ( new PathMatch ( "denuvo-anti-cheat-runtime.dll" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
// This file is a renamed copy of "denuvo-anti-cheat-update-service.exe" which is only seen in the folder of the main game executable after it has been run, but before Denuvo Anti-Cheat is finished installing.
new PathMatchSet ( new PathMatch ( "Denuvo Anti-Cheat Installer.exe" , useEndsWith : true ) , "Denuvo Anti-Cheat" ) ,
} ;
return MatchUtil . GetFirstMatch ( path , matchers , any : true ) ;
}
2022-06-22 13:40:28 -07:00
}
}