Massively overhaul TAGES detection (#87)

* Massively overhaul TAGES detection

* Address TAGES PR comments

* Address further PR comments
This commit is contained in:
SilasLaspada
2022-03-09 15:00:33 -07:00
committed by GitHub
parent ec83669d7d
commit 30c249ce74

View File

@@ -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<ContentMatchSet>
{
// 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<ContentMatchSet>
{
// 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
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
var protections = new ConcurrentQueue<string>();
var matchers = new List<PathMatchSet>
{
/*
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<string>
{
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<string>
{
"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<string>
{
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);
}
/// <inheritdoc/>
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<PathMatchSet>
{
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<int> positions)