2024-11-21 01:32:30 -05:00
|
|
|
|
using System.Collections.Generic;
|
2020-10-26 23:30:35 -07:00
|
|
|
|
using System.Text;
|
2023-03-09 11:52:28 -05:00
|
|
|
|
using BinaryObjectScanner.Interfaces;
|
2023-09-16 22:08:18 -04:00
|
|
|
|
using SabreTools.Matching;
|
2024-10-03 12:01:08 -04:00
|
|
|
|
using SabreTools.Matching.Content;
|
|
|
|
|
|
using SabreTools.Matching.Paths;
|
2023-09-16 02:04:47 -04:00
|
|
|
|
using SabreTools.Serialization.Wrappers;
|
2019-09-27 23:52:24 -07:00
|
|
|
|
|
2023-03-09 23:19:27 -05:00
|
|
|
|
namespace BinaryObjectScanner.Protection
|
2019-09-27 23:52:24 -07:00
|
|
|
|
{
|
2021-09-10 16:10:15 -07:00
|
|
|
|
// TODO: Investigate SecuROM for Macintosh
|
2024-11-04 23:21:12 -05:00
|
|
|
|
public class SecuROM : IExecutableCheck<PortableExecutable>, IPathCheck
|
2019-09-27 23:52:24 -07:00
|
|
|
|
{
|
2021-09-02 22:32:06 -07:00
|
|
|
|
/// <inheritdoc/>
|
2024-11-04 23:21:12 -05:00
|
|
|
|
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
2021-03-23 09:52:09 -07:00
|
|
|
|
{
|
2021-09-02 22:32:06 -07:00
|
|
|
|
// Get the sections from the executable, if possible
|
2023-09-17 22:37:01 -04:00
|
|
|
|
var sections = pex.Model.SectionTable;
|
2021-09-02 22:32:06 -07:00
|
|
|
|
if (sections == null)
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
2023-09-17 22:37:01 -04:00
|
|
|
|
var name = pex.FileDescription;
|
2024-12-02 00:35:04 -05:00
|
|
|
|
if (name.OptionalContains("SecuROM PA"))
|
2023-03-09 23:19:27 -05:00
|
|
|
|
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
|
2024-08-08 08:55:46 -04:00
|
|
|
|
|
|
|
|
|
|
name = pex.InternalName;
|
2024-12-02 00:35:04 -05:00
|
|
|
|
if (name.OptionalEquals("paul.dll"))
|
2024-08-08 08:55:46 -04:00
|
|
|
|
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
|
2024-12-02 00:35:04 -05:00
|
|
|
|
else if (name.OptionalEquals("paul_dll_activate_and_play.dll"))
|
2024-08-08 08:55:46 -04:00
|
|
|
|
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
|
2024-12-02 00:35:04 -05:00
|
|
|
|
else if (name.OptionalEquals("paul_dll_preview_and_review.dll"))
|
2024-08-08 08:55:46 -04:00
|
|
|
|
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
|
2022-12-08 16:38:56 -08:00
|
|
|
|
|
|
|
|
|
|
name = pex.OriginalFilename;
|
2024-12-02 00:35:04 -05:00
|
|
|
|
if (name.OptionalEquals("paul_dll_activate_and_play.dll"))
|
2023-03-09 23:19:27 -05:00
|
|
|
|
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
|
2022-12-08 16:38:56 -08:00
|
|
|
|
|
|
|
|
|
|
name = pex.ProductName;
|
2024-12-02 00:35:04 -05:00
|
|
|
|
if (name.OptionalContains("SecuROM Activate & Play"))
|
2023-03-09 23:19:27 -05:00
|
|
|
|
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
|
2022-03-15 22:09:28 -07:00
|
|
|
|
|
|
|
|
|
|
// Get the matrosch section, if it exists
|
2024-11-21 01:32:30 -05:00
|
|
|
|
if (pex.ContainsSection("matrosch", exact: true))
|
2022-03-15 22:09:28 -07:00
|
|
|
|
return $"SecuROM Matroschka Package";
|
|
|
|
|
|
|
2024-11-21 01:32:30 -05:00
|
|
|
|
if (pex.ContainsSection(".dsstext", exact: true))
|
2022-06-22 09:35:41 -07:00
|
|
|
|
return $"SecuROM 8.03.03+";
|
|
|
|
|
|
|
2021-09-02 22:32:06 -07:00
|
|
|
|
// Get the .securom section, if it exists
|
2024-11-21 01:32:30 -05:00
|
|
|
|
if (pex.ContainsSection(".securom", exact: true))
|
2022-03-14 23:44:17 -07:00
|
|
|
|
return $"SecuROM {GetV7Version(pex)}";
|
2021-09-02 22:32:06 -07:00
|
|
|
|
|
2022-03-15 22:11:37 -07:00
|
|
|
|
// Get the .sll section, if it exists
|
2024-11-21 01:32:30 -05:00
|
|
|
|
if (pex.ContainsSection(".sll", exact: true))
|
2022-03-15 22:09:28 -07:00
|
|
|
|
return $"SecuROM SLL Protected (for SecuROM v8.x)";
|
|
|
|
|
|
|
2021-09-02 22:32:06 -07:00
|
|
|
|
// Search after the last section
|
2022-12-10 20:10:25 -08:00
|
|
|
|
if (pex.OverlayStrings != null)
|
2021-07-17 23:40:16 -07:00
|
|
|
|
{
|
2024-11-12 23:17:48 -05:00
|
|
|
|
if (pex.OverlayStrings.Exists(s => s == "AddD"))
|
2022-12-10 20:10:25 -08:00
|
|
|
|
return $"SecuROM {GetV4Version(pex)}";
|
2021-09-02 22:32:06 -07:00
|
|
|
|
}
|
2021-03-23 09:52:09 -07:00
|
|
|
|
|
2021-09-02 22:32:06 -07:00
|
|
|
|
// Get the sections 5+, if they exist (example names: .fmqyrx, .vcltz, .iywiak)
|
|
|
|
|
|
for (int i = 4; i < sections.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var nthSection = sections[i];
|
2023-09-17 22:37:01 -04:00
|
|
|
|
if (nthSection == null)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2024-11-21 01:32:30 -05:00
|
|
|
|
string nthSectionName = Encoding.ASCII.GetString(nthSection.Name ?? []).TrimEnd('\0');
|
2023-09-17 22:37:01 -04:00
|
|
|
|
if (nthSectionName != ".idata" && nthSectionName != ".rsrc")
|
2021-07-17 23:40:16 -07:00
|
|
|
|
{
|
2022-12-03 22:17:48 -08:00
|
|
|
|
var nthSectionData = pex.GetFirstSectionData(nthSectionName);
|
2023-09-17 22:37:01 -04:00
|
|
|
|
if (nthSectionData == null)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
var matchers = new List<ContentMatchSet>
|
2021-09-02 22:32:06 -07:00
|
|
|
|
{
|
2023-09-17 22:37:01 -04:00
|
|
|
|
// (char)0xCA + (char)0xDD + (char)0xDD + (char)0xAC + (char)0x03
|
2023-11-22 13:28:13 -05:00
|
|
|
|
new(new byte?[] { 0xCA, 0xDD, 0xDD, 0xAC, 0x03 }, GetV5Version, "SecuROM"),
|
2023-09-17 22:37:01 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var match = MatchUtil.GetFirstMatch(file, nthSectionData, matchers, includeDebug);
|
2023-11-22 12:22:01 -05:00
|
|
|
|
if (!string.IsNullOrEmpty(match))
|
2023-09-17 22:37:01 -04:00
|
|
|
|
return match;
|
2021-09-02 22:32:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-09 21:22:55 -08:00
|
|
|
|
// Get the .rdata section strings, if they exist
|
2023-09-17 22:37:01 -04:00
|
|
|
|
var strs = pex.GetFirstSectionStrings(".rdata");
|
2022-12-09 21:22:55 -08:00
|
|
|
|
if (strs != null)
|
2021-09-02 22:32:06 -07:00
|
|
|
|
{
|
2022-12-09 21:22:55 -08:00
|
|
|
|
// Both have the identifier found within `.rdata` but the version is within `.data`
|
2024-11-12 23:17:48 -05:00
|
|
|
|
if (strs.Exists(s => s.Contains("/secuexp")))
|
2022-12-09 21:22:55 -08:00
|
|
|
|
return $"SecuROM {GetV8WhiteLabelVersion(pex)} (White Label)";
|
2024-11-12 23:17:48 -05:00
|
|
|
|
else if (strs.Exists(s => s.Contains("SecuExp.exe")))
|
2022-12-08 16:38:56 -08:00
|
|
|
|
return $"SecuROM {GetV8WhiteLabelVersion(pex)} (White Label)";
|
2021-09-02 22:32:06 -07:00
|
|
|
|
}
|
2021-07-17 23:06:11 -07:00
|
|
|
|
|
2021-09-02 22:32:06 -07:00
|
|
|
|
// Get the .cms_d and .cms_t sections, if they exist -- TODO: Confirm if both are needed or either/or is fine
|
2024-11-21 01:32:30 -05:00
|
|
|
|
if (pex.ContainsSection(".cmd_d", true) || pex.ContainsSection(".cms_t", true))
|
2021-09-02 22:32:06 -07:00
|
|
|
|
return $"SecuROM 1-3";
|
2021-03-23 09:52:09 -07:00
|
|
|
|
|
2021-09-02 22:32:06 -07:00
|
|
|
|
return null;
|
2021-08-25 19:37:32 -07:00
|
|
|
|
}
|
2021-03-23 09:52:09 -07:00
|
|
|
|
|
2021-02-26 00:32:09 -08:00
|
|
|
|
/// <inheritdoc/>
|
2024-11-20 17:10:03 -05:00
|
|
|
|
public List<string> CheckDirectoryPath(string path, List<string>? files)
|
2019-09-27 23:52:24 -07:00
|
|
|
|
{
|
2021-03-23 10:36:14 -07:00
|
|
|
|
var matchers = new List<PathMatchSet>
|
2019-09-27 23:52:24 -07:00
|
|
|
|
{
|
2021-03-23 10:36:14 -07:00
|
|
|
|
// TODO: Verify if these are OR or AND
|
2023-11-22 13:28:13 -05:00
|
|
|
|
new(new FilePathMatch("CMS16.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS_95.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS_NT.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS32_95.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS32_NT.DLL"), "SecuROM"),
|
2021-03-23 10:36:14 -07:00
|
|
|
|
|
|
|
|
|
|
// TODO: Verify if these are OR or AND
|
2023-11-22 13:28:13 -05:00
|
|
|
|
new(new FilePathMatch("SINTF32.DLL"), "SecuROM New"),
|
|
|
|
|
|
new(new FilePathMatch("SINTF16.DLL"), "SecuROM New"),
|
|
|
|
|
|
new(new FilePathMatch("SINTFNT.DLL"), "SecuROM New"),
|
2022-03-14 09:03:43 -07:00
|
|
|
|
|
|
|
|
|
|
// TODO: Find more samples of this for different versions
|
2023-11-22 13:28:13 -05:00
|
|
|
|
new(new List<PathMatch>
|
2022-03-14 09:03:43 -07:00
|
|
|
|
{
|
2023-11-22 13:28:13 -05:00
|
|
|
|
new FilePathMatch("securom_v7_01.bak"),
|
|
|
|
|
|
new FilePathMatch("securom_v7_01.dat"),
|
|
|
|
|
|
new FilePathMatch("securom_v7_01.tmp"),
|
2022-03-14 09:03:43 -07:00
|
|
|
|
}, "SecuROM 7.01"),
|
2021-03-23 10:36:14 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-14 16:10:10 -05:00
|
|
|
|
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
2021-03-19 15:41:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
2023-09-17 22:37:01 -04:00
|
|
|
|
public string? CheckFilePath(string path)
|
2021-03-19 15:41:49 -07:00
|
|
|
|
{
|
2021-03-23 10:36:14 -07:00
|
|
|
|
var matchers = new List<PathMatchSet>
|
2021-03-19 15:41:49 -07:00
|
|
|
|
{
|
2023-11-22 13:28:13 -05:00
|
|
|
|
new(new FilePathMatch("CMS16.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS_95.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS_NT.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS32_95.DLL"), "SecuROM"),
|
|
|
|
|
|
new(new FilePathMatch("CMS32_NT.DLL"), "SecuROM"),
|
|
|
|
|
|
|
|
|
|
|
|
new(new FilePathMatch("SINTF32.DLL"), "SecuROM New"),
|
|
|
|
|
|
new(new FilePathMatch("SINTF16.DLL"), "SecuROM New"),
|
|
|
|
|
|
new(new FilePathMatch("SINTFNT.DLL"), "SecuROM New"),
|
|
|
|
|
|
|
|
|
|
|
|
new(new FilePathMatch("securom_v7_01.bak"), "SecuROM 7.01"),
|
|
|
|
|
|
new(new FilePathMatch("securom_v7_01.dat"), "SecuROM 7.01"),
|
|
|
|
|
|
new(new FilePathMatch("securom_v7_01.tmp"), "SecuROM 7.01"),
|
2021-03-23 10:36:14 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
2019-09-27 23:52:24 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-10 20:10:25 -08:00
|
|
|
|
private static string GetV4Version(PortableExecutable pex)
|
2019-09-27 23:52:24 -07:00
|
|
|
|
{
|
2022-12-10 20:10:25 -08:00
|
|
|
|
int index = 8; // Begin reading after "AddD"
|
2024-10-31 22:16:51 -04:00
|
|
|
|
char major = (char)pex.OverlayData![index];
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index += 2;
|
2019-09-30 11:08:44 -07:00
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
string minor = Encoding.ASCII.GetString(pex.OverlayData, index, 2);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index += 3;
|
|
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
string patch = Encoding.ASCII.GetString(pex.OverlayData, index, 2);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index += 3;
|
|
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
string revision = Encoding.ASCII.GetString(pex.OverlayData, index, 4);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
if (!char.IsNumber(major))
|
2020-09-10 21:10:32 -07:00
|
|
|
|
return "(very old, v3 or less)";
|
|
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
return $"{major}.{minor}.{patch}.{revision}";
|
2019-09-27 23:52:24 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-04 23:21:12 -05:00
|
|
|
|
private static string? GetV5Version(string file, byte[]? fileContent, List<int> positions)
|
2019-09-27 23:52:24 -07:00
|
|
|
|
{
|
2023-09-18 15:53:24 -04:00
|
|
|
|
// If we have no content
|
|
|
|
|
|
if (fileContent == null)
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
2021-03-22 11:43:51 -07:00
|
|
|
|
int index = positions[0] + 8; // Begin reading after "ÊÝݬ"
|
2024-10-31 22:16:51 -04:00
|
|
|
|
byte major = (byte)(fileContent[index] & 0x0F);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index += 2;
|
|
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
byte[] minor = new byte[2];
|
|
|
|
|
|
minor[0] = (byte)(fileContent[index] ^ 36);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index++;
|
2024-10-31 22:16:51 -04:00
|
|
|
|
minor[1] = (byte)(fileContent[index] ^ 28);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index += 2;
|
|
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
byte[] patch = new byte[2];
|
|
|
|
|
|
patch[0] = (byte)(fileContent[index] ^ 42);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index++;
|
2024-10-31 22:16:51 -04:00
|
|
|
|
patch[1] = (byte)(fileContent[index] ^ 8);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index += 2;
|
|
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
byte[] revision = new byte[4];
|
|
|
|
|
|
revision[0] = (byte)(fileContent[index] ^ 16);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index++;
|
2024-10-31 22:16:51 -04:00
|
|
|
|
revision[1] = (byte)(fileContent[index] ^ 116);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index++;
|
2024-10-31 22:16:51 -04:00
|
|
|
|
revision[2] = (byte)(fileContent[index] ^ 34);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
index++;
|
2024-10-31 22:16:51 -04:00
|
|
|
|
revision[3] = (byte)(fileContent[index] ^ 22);
|
2020-09-10 21:10:32 -07:00
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
if (major == 0 || major > 9)
|
2021-03-22 16:25:40 -07:00
|
|
|
|
return string.Empty;
|
2020-09-10 21:10:32 -07:00
|
|
|
|
|
2024-10-31 22:16:51 -04:00
|
|
|
|
return $"{major}.{minor[0]}{minor[1]}.{patch[0]}{patch[1]}.{revision[0]}{revision[1]}{revision[2]}{revision[3]}";
|
2019-09-27 23:52:24 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-14 23:44:17 -07:00
|
|
|
|
// These live in the MS-DOS stub, for some reason
|
|
|
|
|
|
private static string GetV7Version(PortableExecutable pex)
|
2019-09-27 23:52:24 -07:00
|
|
|
|
{
|
2024-11-20 23:41:09 -05:00
|
|
|
|
// If SecuROM is stripped, the MS-DOS stub might be shorter.
|
|
|
|
|
|
// We then know that SecuROM -was- there, but we don't know what exact version.
|
|
|
|
|
|
if (pex.StubExecutableData == null)
|
|
|
|
|
|
return "7 remnants";
|
|
|
|
|
|
|
|
|
|
|
|
//SecuROM 7 new and 8 -- 64 bytes for DOS stub, 236 bytes in total
|
|
|
|
|
|
int index = 172;
|
|
|
|
|
|
if (pex.StubExecutableData.Length >= 176 && pex.StubExecutableData[index + 3] == 0x5C)
|
2024-04-04 21:00:09 +02:00
|
|
|
|
{
|
2024-11-20 23:41:09 -05:00
|
|
|
|
int major = pex.StubExecutableData[index + 0] ^ 0xEA;
|
|
|
|
|
|
int minor = pex.StubExecutableData[index + 1] ^ 0x2C;
|
|
|
|
|
|
int patch = pex.StubExecutableData[index + 2] ^ 0x08;
|
2019-09-30 11:08:44 -07:00
|
|
|
|
|
2024-11-20 23:41:09 -05:00
|
|
|
|
return $"{major}.{minor:00}.{patch:0000}";
|
2024-04-04 21:00:09 +02:00
|
|
|
|
}
|
2024-11-20 23:41:09 -05:00
|
|
|
|
|
|
|
|
|
|
// SecuROM 7 old -- 64 bytes for DOS stub, 122 bytes in total
|
|
|
|
|
|
index = 58;
|
|
|
|
|
|
if (pex.StubExecutableData.Length >= 62)
|
2024-04-04 21:00:09 +02:00
|
|
|
|
{
|
2024-11-20 23:41:09 -05:00
|
|
|
|
int minor = pex.StubExecutableData[index + 0] ^ 0x10;
|
|
|
|
|
|
int patch = pex.StubExecutableData[index + 1] ^ 0x10;
|
|
|
|
|
|
|
|
|
|
|
|
//return "7.01-7.10"
|
|
|
|
|
|
return $"7.{minor:00}.{patch:0000}";
|
2019-09-27 23:52:24 -07:00
|
|
|
|
}
|
2024-11-20 23:41:09 -05:00
|
|
|
|
|
|
|
|
|
|
// If SecuROM is stripped, the MS-DOS stub might be shorter.
|
|
|
|
|
|
// We then know that SecuROM -was- there, but we don't know what exact version.
|
|
|
|
|
|
return "7 remnants";
|
2019-09-27 23:52:24 -07:00
|
|
|
|
}
|
2022-07-11 22:38:02 -07:00
|
|
|
|
|
|
|
|
|
|
private static string GetV8WhiteLabelVersion(PortableExecutable pex)
|
|
|
|
|
|
{
|
2023-09-17 22:37:01 -04:00
|
|
|
|
// Get the .data/DATA section, if it exists
|
2022-12-03 22:17:48 -08:00
|
|
|
|
var dataSectionRaw = pex.GetFirstSectionData(".data") ?? pex.GetFirstSectionData("DATA");
|
2023-09-17 22:37:01 -04:00
|
|
|
|
if (dataSectionRaw == null)
|
2022-07-11 22:38:02 -07:00
|
|
|
|
return "8";
|
|
|
|
|
|
|
|
|
|
|
|
// Search .data for the version indicator
|
2023-11-22 13:28:13 -05:00
|
|
|
|
var matcher = new ContentMatch(
|
|
|
|
|
|
[
|
2022-07-11 22:38:02 -07:00
|
|
|
|
0x29, null, null, null, null, null, null, null,
|
|
|
|
|
|
null, null, null, null, null, null, null, null,
|
|
|
|
|
|
null, null, null, null, null, null, null, null,
|
|
|
|
|
|
null, null, null, null, null, null, null, null,
|
|
|
|
|
|
null, null, null, null, null, null, null, null,
|
|
|
|
|
|
0x82, 0xD8, 0x0C, 0xAC
|
2023-11-22 13:28:13 -05:00
|
|
|
|
]);
|
2022-07-11 22:38:02 -07:00
|
|
|
|
|
2024-10-03 12:01:08 -04:00
|
|
|
|
int position = matcher.Match(dataSectionRaw);
|
2022-07-11 22:38:02 -07:00
|
|
|
|
|
|
|
|
|
|
// If we can't find the string, we default to generic
|
2024-10-03 12:01:08 -04:00
|
|
|
|
if (position < 0)
|
2022-07-11 22:38:02 -07:00
|
|
|
|
return "8";
|
|
|
|
|
|
|
2024-11-20 23:41:09 -05:00
|
|
|
|
int major = dataSectionRaw[position + 0xAC + 0] ^ 0xCA;
|
|
|
|
|
|
int minor = dataSectionRaw[position + 0xAC + 1] ^ 0x39;
|
|
|
|
|
|
int patch = dataSectionRaw[position + 0xAC + 2] ^ 0x51;
|
2023-11-14 16:10:10 -05:00
|
|
|
|
|
2024-11-20 23:41:09 -05:00
|
|
|
|
return $"{major}.{minor:00}.{patch:0000}";
|
2022-07-11 22:38:02 -07:00
|
|
|
|
}
|
2019-09-27 23:52:24 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|