Files
BinaryObjectScanner/BinaryObjectScanner/Protection/SecuROM.cs

284 lines
12 KiB
C#
Raw Normal View History

2024-11-21 01:32:30 -05:00
using System.Collections.Generic;
2020-10-26 23:30:35 -07:00
using System.Text;
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;
namespace BinaryObjectScanner.Protection
{
// TODO: Investigate SecuROM for Macintosh
2024-11-04 23:21:12 -05:00
public class SecuROM : IExecutableCheck<PortableExecutable>, IPathCheck
{
/// <inheritdoc/>
2024-11-04 23:21:12 -05:00
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
2023-09-17 22:37:01 -04:00
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
2023-09-17 22:37:01 -04:00
var name = pex.FileDescription;
if (name.OptionalContains("SecuROM PA"))
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
name = pex.InternalName;
if (name.OptionalEquals("paul.dll"))
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
else if (name.OptionalEquals("paul_dll_activate_and_play.dll"))
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
else if (name.OptionalEquals("paul_dll_preview_and_review.dll"))
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
2022-12-08 16:38:56 -08:00
name = pex.OriginalFilename;
if (name.OptionalEquals("paul_dll_activate_and_play.dll"))
return $"SecuROM Product Activation v{pex.GetInternalVersion()}";
2022-12-08 16:38:56 -08:00
name = pex.ProductName;
if (name.OptionalContains("SecuROM Activate & Play"))
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+";
// Get the .securom section, if it exists
2024-11-21 01:32:30 -05:00
if (pex.ContainsSection(".securom", exact: true))
return $"SecuROM {GetV7Version(pex)}";
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)";
// Search after the last section
2022-12-10 20:10:25 -08:00
if (pex.OverlayStrings != null)
{
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)}";
}
// 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")
{
var nthSectionData = pex.GetFirstSectionData(nthSectionName);
2023-09-17 22:37:01 -04:00
if (nthSectionData == null)
continue;
var matchers = new List<ContentMatchSet>
{
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;
}
}
// Get the .rdata section strings, if they exist
2023-09-17 22:37:01 -04:00
var strs = pex.GetFirstSectionStrings(".rdata");
if (strs != null)
{
// 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")))
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-07-17 23:06:11 -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))
return $"SecuROM 1-3";
return null;
}
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)
{
var matchers = new List<PathMatchSet>
{
// 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"),
// 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"),
};
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
{
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"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
2022-12-10 20:10:25 -08:00
private static string GetV4Version(PortableExecutable pex)
{
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];
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);
index += 3;
2024-10-31 22:16:51 -04:00
string patch = Encoding.ASCII.GetString(pex.OverlayData, index, 2);
index += 3;
2024-10-31 22:16:51 -04:00
string revision = Encoding.ASCII.GetString(pex.OverlayData, index, 4);
2024-10-31 22:16:51 -04:00
if (!char.IsNumber(major))
return "(very old, v3 or less)";
2024-10-31 22:16:51 -04:00
return $"{major}.{minor}.{patch}.{revision}";
}
2024-11-04 23:21:12 -05:00
private static string? GetV5Version(string file, byte[]? fileContent, List<int> positions)
{
2023-09-18 15:53:24 -04:00
// If we have no content
if (fileContent == null)
return null;
int index = positions[0] + 8; // Begin reading after "ÊÝݬ"
2024-10-31 22:16:51 -04:00
byte major = (byte)(fileContent[index] & 0x0F);
index += 2;
2024-10-31 22:16:51 -04:00
byte[] minor = new byte[2];
minor[0] = (byte)(fileContent[index] ^ 36);
index++;
2024-10-31 22:16:51 -04:00
minor[1] = (byte)(fileContent[index] ^ 28);
index += 2;
2024-10-31 22:16:51 -04:00
byte[] patch = new byte[2];
patch[0] = (byte)(fileContent[index] ^ 42);
index++;
2024-10-31 22:16:51 -04:00
patch[1] = (byte)(fileContent[index] ^ 8);
index += 2;
2024-10-31 22:16:51 -04:00
byte[] revision = new byte[4];
revision[0] = (byte)(fileContent[index] ^ 16);
index++;
2024-10-31 22:16:51 -04:00
revision[1] = (byte)(fileContent[index] ^ 116);
index++;
2024-10-31 22:16:51 -04:00
revision[2] = (byte)(fileContent[index] ^ 34);
index++;
2024-10-31 22:16:51 -04:00
revision[3] = (byte)(fileContent[index] ^ 22);
2024-10-31 22:16:51 -04:00
if (major == 0 || major > 9)
2021-03-22 16:25:40 -07:00
return string.Empty;
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]}";
}
// These live in the MS-DOS stub, for some reason
private static string GetV7Version(PortableExecutable pex)
{
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-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-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-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}";
}
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";
}
private static string GetV8WhiteLabelVersion(PortableExecutable pex)
{
2023-09-17 22:37:01 -04:00
// Get the .data/DATA section, if it exists
var dataSectionRaw = pex.GetFirstSectionData(".data") ?? pex.GetFirstSectionData("DATA");
2023-09-17 22:37:01 -04:00
if (dataSectionRaw == null)
return "8";
// Search .data for the version indicator
2023-11-22 13:28:13 -05:00
var matcher = new ContentMatch(
[
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
]);
2024-10-03 12:01:08 -04:00
int position = matcher.Match(dataSectionRaw);
// If we can't find the string, we default to generic
2024-10-03 12:01:08 -04:00
if (position < 0)
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}";
}
}
}