From 30c249ce74f3f357a023a53be75253ec3cb315cb Mon Sep 17 00:00:00 2001 From: SilasLaspada Date: Wed, 9 Mar 2022 15:00:33 -0700 Subject: [PATCH] Massively overhaul TAGES detection (#87) * Massively overhaul TAGES detection * Address TAGES PR comments * Address further PR comments --- BurnOutSharp/ProtectionType/Tages.cs | 186 +++++++++++++++++---------- 1 file changed, 118 insertions(+), 68 deletions(-) diff --git a/BurnOutSharp/ProtectionType/Tages.cs b/BurnOutSharp/ProtectionType/Tages.cs index d795ef3d..1c8fb6da 100644 --- a/BurnOutSharp/ProtectionType/Tages.cs +++ b/BurnOutSharp/ProtectionType/Tages.cs @@ -20,33 +20,32 @@ namespace BurnOutSharp.ProtectionType if (sections == null) return null; - // Get the last section and read after it - var lastSection = sections.LastOrDefault(); - if (lastSection != null) - { - int sectionAddr = (int)lastSection.PointerToRawData; - int sectionEnd = sectionAddr + (int)lastSection.VirtualSize; - var matchers = new List - { - // tagesprotection.com - new ContentMatchSet( - new ContentMatch(new byte?[] - { - 0x74, 0x61, 0x67, 0x65, 0x73, 0x70, 0x72, 0x6F, - 0x74, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2E, - 0x63, 0x6F, 0x6D - }, start: sectionEnd), - Utilities.GetFileVersion, "TAGES"), - }; + /* + Expected files to contain "TagesSetup"/"Application TagesSetup": + DrvSetup.exe + DrvSetup_x64.exe + TagesSetup.exe + TagesSetup_x64.exe + + Expected files to contain "Tages activation client"/"T@GES": + TagesClient.exe + (There is generally a TagesClient.dat accompanying the TagesClient.exe.) + */ + string name = Utilities.GetFileDescription(pex); + if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("TagesSetup", StringComparison.OrdinalIgnoreCase)) + return $"TAGES Driver Setup {GetVersion(pex)}"; + else if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Tagès activation client", StringComparison.OrdinalIgnoreCase)) + return $"TAGES Activation Client {GetVersion(pex)}"; - string match = MatchUtil.GetFirstMatch(file, fileContent, matchers, includeDebug); - if (!string.IsNullOrWhiteSpace(match)) - return match; - } + name = Utilities.GetProductName(pex); + if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Application TagesSetup", StringComparison.OrdinalIgnoreCase)) + return $"TAGES Driver Setup {GetVersion(pex)}"; + else if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("T@GES", StringComparison.OrdinalIgnoreCase)) + return $"TAGES Activation Client {GetVersion(pex)}"; - // TODO: Obtain a sample to find where this string is in a typical executable if (includeDebug) { + // TODO: Obtain a sample to find where this string is in a typical executable var contentMatchSets = new List { // protected-tages-runtime.exe @@ -56,12 +55,12 @@ namespace BurnOutSharp.ProtectionType 0x64, 0x2D, 0x74, 0x61, 0x67, 0x65, 0x73, 0x2D, 0x72, 0x75, 0x6E, 0x74, 0x69, 0x6D, 0x65, 0x2E, 0x65, 0x78, 0x65 - }, Utilities.GetFileVersion, "TAGES"), + }, Utilities.GetFileVersion, "TAGES [DEBUG]"), + // This check seems to currently be broken, as files that appear to have this string aren't being detected. // (char)0xE8 + u + (char)0x00 + (char)0x00 + (char)0x00 + (char)0xE8 - new ContentMatchSet(new byte?[] { 0xE8, 0x75, 0x00, 0x00, 0x00, 0xE8 }, GetVersion, "TAGES"), + new ContentMatchSet(new byte?[] { 0xE8, 0x75, 0x00, 0x00, 0x00, 0xE8 }, GetVersion, "TAGES [DEBUG]"), }; - return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug); } @@ -71,57 +70,108 @@ namespace BurnOutSharp.ProtectionType /// public ConcurrentQueue CheckDirectoryPath(string path, IEnumerable files) { - var protections = new ConcurrentQueue(); + var matchers = new List + { + /* + So far, only known to exist in early versions of "Moto Racer 3". + Expected information about these checks (all of these are expected to be found together): + ./sys/Devx.sys (d37f70489207014d7d0fbaa43b081a93e8030498) + ./sys/VtPr.sys (a0acbc2f8e321e4f30c913c095e28af444058249) + ./Wave.aif (SHA-1 is variable, file size is 81,920 bytes) + ./Wave.alf (f82339d797be6da92f5d9dadeae9025385159057) + ./Wave.apt (0351d0f3d4166362a1a9d838c9390a3d92945a44) + ./Wave.axt (SHA-1 is variable, file size is 61,440 bytes) + */ + new PathMatchSet(new List + { + Path.Combine("Sys", "Devx.sys").Replace("\\", "/"), + Path.Combine("Sys", "VtPr.sys").Replace("\\", "/"), + "Wave.aif", + "Wave.alf", + "Wave.apt", + "Wave.axt", + }, "TAGES"), - // "Wave.aif" was a former check here, but it over-matched - if (files.Any(f => Path.GetFileName(f).Equals("Tages.dll", StringComparison.OrdinalIgnoreCase))) - { - protections.Enqueue("TAGES"); - } - if (files.Any(f => Path.GetFileName(f).Equals("tagesclient.exe", StringComparison.OrdinalIgnoreCase))) - { - string file = files.First(f => Path.GetFileName(f).Equals("tagesclient.exe", StringComparison.OrdinalIgnoreCase)); - protections.Enqueue("TAGES Activation Client " + Utilities.GetFileVersion(file)); - } - if (files.Any(f => Path.GetFileName(f).Equals("TagesSetup.exe", StringComparison.OrdinalIgnoreCase))) - { - string file = files.First(f => Path.GetFileName(f).Equals("TagesSetup.exe", StringComparison.OrdinalIgnoreCase)); - protections.Enqueue("TAGES Setup " + Utilities.GetFileVersion(file)); - } - if (files.Any(f => Path.GetFileName(f).Equals("TagesSetup_x64.exe", StringComparison.OrdinalIgnoreCase))) - { - string file = files.First(f => Path.GetFileName(f).Equals("TagesSetup_x64.exe", StringComparison.OrdinalIgnoreCase)); - protections.Enqueue("TAGES Setup " + Utilities.GetFileVersion(file)); - } + // Currently only known to exist in "XIII" and (presumably) "Beyond Good & Evil". + new PathMatchSet(new List + { + "enodpl.sys", + "ENODPL.VXD", + "tandpl.sys", + "TANDPL.VXD", + }, "TAGES"), - if (protections.Count == 0) - return null; - else - return protections; + /* + The directory of these files has been seen to be named two different things, with two different accompanying executables in the root of the directory. + In the example where the directory is named "Drivers", the executable is named "Silent.exe". + In the example where the directory is named "ELBdrivers", the executable is name "ELBDrivers.exe". + + Expected information about these checks (all of these are expected to be found together): + ./9x/hwpsgt.vxd (40826e95f3ad8031b6debe15aca052c701288e04) + ./9x/lemsgt.vxd (f82339d797be6da92f5d9dadeae9025385159057) + ./NT/hwpsgt.sys (43f407ecdc0d87a3713126b757ccaad07ade285f) + ./NT/lemsgt.sys (548dd6359abbcc8c84ce346d078664eeedc716f7) + (The name and file size of the included executable vary, but there should always be one here.) + */ + new PathMatchSet(new List + { + Path.Combine("9x", "hwpsgt.vxd").Replace("\\", "/"), + Path.Combine("9x", "lemsgt.vxd").Replace("\\", "/"), + Path.Combine("NT", "hwpsgt.sys").Replace("\\", "/"), + Path.Combine("NT", "lemsgt.sys").Replace("\\", "/"), + }, "TAGES"), + }; + + return MatchUtil.GetAllMatches(files, matchers, any: true); } /// public string CheckFilePath(string path) { - // "Wave.aif" was a former check here, but it over-matched - if (Path.GetFileName(path).Equals("Tages.dll", StringComparison.OrdinalIgnoreCase)) + var matchers = new List { - return "TAGES"; - } - else if (Path.GetFileName(path).Equals("tagesclient.exe", StringComparison.OrdinalIgnoreCase)) - { - return "TAGES Activation Client " + Utilities.GetFileVersion(path); - } - else if (Path.GetFileName(path).Equals("TagesSetup.exe", StringComparison.OrdinalIgnoreCase)) - { - return "TAGES Setup " + Utilities.GetFileVersion(path); - } - else if (Path.GetFileName(path).Equals("TagesSetup_x64.exe", StringComparison.OrdinalIgnoreCase)) - { - return "TAGES Setup " + Utilities.GetFileVersion(path); - } + // There are Wave.XXX files associated with the Devx.sys and VtPr.sys drivers, but the names over-match too easily in a file path check. + new PathMatchSet(new PathMatch("Devx.sys", useEndsWith: true), "TAGES Driver"), + new PathMatchSet(new PathMatch("VtPr.sys", useEndsWith: true), "TAGES Driver"), + new PathMatchSet(new PathMatch("ENODPL.VXD", useEndsWith: true), "TAGES 9x Driver"), + new PathMatchSet(new PathMatch("TANDPL.VXD", useEndsWith: true), "TAGES 9x Driver"), + new PathMatchSet(new PathMatch("enodpl.sys", useEndsWith: true), "TAGES NT Driver"), + new PathMatchSet(new PathMatch("tandpl.sys", useEndsWith: true), "TAGES NT Driver"), + new PathMatchSet(new PathMatch("hwpsgt.vxd", useEndsWith: true), "TAGES 9x Driver"), + new PathMatchSet(new PathMatch("lemsgt.vxd", useEndsWith: true), "TAGES 9x Driver"), + new PathMatchSet(new PathMatch("hwpsgt.sys", useEndsWith: true), "TAGES NT Driver"), + new PathMatchSet(new PathMatch("lemsgt.sys", useEndsWith: true), "TAGES NT Driver"), - return null; + // The following files are supposed to only be found inside the driver setup executables. + new PathMatchSet(new PathMatch("ithsgt.sys", useEndsWith: true), "TAGES Driver"), + new PathMatchSet(new PathMatch("lilsgt.sys", useEndsWith: true), "TAGES Driver"), + new PathMatchSet(new PathMatch("atksgt.sys", useEndsWith: true), "TAGES Driver"), + new PathMatchSet(new PathMatch("lirsgt.sys", useEndsWith: true), "TAGES Driver"), + + // The following files appear to be container formats for TAGES, but little is currently known about them. + new PathMatchSet(new PathMatch("GameModule.elb", useEndsWith: true), "TAGES/SolidShield Game Executable Container"), + new PathMatchSet(new PathMatch("InstallModule.elb", useEndsWith: true), "TAGES/SolidShield Installer Container"), + + // Not much is known about this file, but it seems to be related to what PiD reports as "protection level: Tages BASIC". Seems to always be found with other KWN files. + new PathMatchSet(new PathMatch("GAME.KWN", useEndsWith: true), "TAGES (BASIC?)"), + }; + + return MatchUtil.GetFirstMatch(path, matchers, any: true); + } + + private string GetVersion(PortableExecutable pex) + { + // Check the file version first + string version = Utilities.GetFileVersion(pex); + if (!string.IsNullOrEmpty(version)) + return version; + + // Then check the manifest version + version = Utilities.GetManifestVersion(pex); + if (!string.IsNullOrEmpty(version)) + return version; + + return "(Unknown Version)"; } public static string GetVersion(string file, byte[] fileContent, List positions)