From 3d3275e3cbcc67904ea0673c6daf5924b85a2af7 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 21 Jul 2025 09:11:37 -0400 Subject: [PATCH] Add SecuROM Matroshka models All research thanks to HeroponRikiBestest --- SabreTools.Models/SecuROM/Constants.cs | 8 +++ SabreTools.Models/SecuROM/DFAFile.cs | 2 +- SabreTools.Models/SecuROM/Enums.cs | 36 ++++++++++ SabreTools.Models/SecuROM/MatroshkaEntry.cs | 57 +++++++++++++++ SabreTools.Models/SecuROM/MatroshkaPackage.cs | 70 +++++++++++++++++++ 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 SabreTools.Models/SecuROM/Enums.cs create mode 100644 SabreTools.Models/SecuROM/MatroshkaEntry.cs create mode 100644 SabreTools.Models/SecuROM/MatroshkaPackage.cs diff --git a/SabreTools.Models/SecuROM/Constants.cs b/SabreTools.Models/SecuROM/Constants.cs index 362307e..f5b7efd 100644 --- a/SabreTools.Models/SecuROM/Constants.cs +++ b/SabreTools.Models/SecuROM/Constants.cs @@ -110,5 +110,13 @@ namespace SabreTools.Models.SecuROM #endregion #endregion + + #region Matroshka + + public const string MatroshkaMagicString = "MatR"; + + public static readonly byte[] MatroshkaMagicBytes = [0x4D, 0x61, 0x74, 0x52]; + + #endregion } } \ No newline at end of file diff --git a/SabreTools.Models/SecuROM/DFAFile.cs b/SabreTools.Models/SecuROM/DFAFile.cs index 1a87f9a..69dbfd4 100644 --- a/SabreTools.Models/SecuROM/DFAFile.cs +++ b/SabreTools.Models/SecuROM/DFAFile.cs @@ -8,7 +8,7 @@ namespace SabreTools.Models.SecuROM public class DFAFile { /// - /// "SDFA" 0x04 0x00 0x00 0x00; + /// "SDFA" 0x04 0x00 0x00 0x00 /// /// 8 bytes public byte[]? Signature { get; set; } diff --git a/SabreTools.Models/SecuROM/Enums.cs b/SabreTools.Models/SecuROM/Enums.cs new file mode 100644 index 0000000..ab05174 --- /dev/null +++ b/SabreTools.Models/SecuROM/Enums.cs @@ -0,0 +1,36 @@ +namespace SabreTools.Models.SecuROM +{ + public enum MatroshkaEntryType : uint + { + /// + /// Helper or activation executable + /// + Helper = 0x01, + + /// + /// Main executable, usually one of the following: + /// - RC-encrypted executable to be decrypted later + /// - Main game program executable + /// - Revoker executable + /// + /// Usually the second entry + Main = 0x02, + + /// + /// Required libraries for the main executable + /// + /// + /// Examples include: + /// - DFA.dll for RC-encrypted executables + /// - paul.dll for PA-protected games + /// - remover.exe for revocation + /// executables. + /// + Dependency = 0x04, + + /// + /// Similar use to + /// + Unknown0x08 = 0x08, + } +} \ No newline at end of file diff --git a/SabreTools.Models/SecuROM/MatroshkaEntry.cs b/SabreTools.Models/SecuROM/MatroshkaEntry.cs new file mode 100644 index 0000000..ae0da16 --- /dev/null +++ b/SabreTools.Models/SecuROM/MatroshkaEntry.cs @@ -0,0 +1,57 @@ +namespace SabreTools.Models.SecuROM +{ + public class MatroshkaEntry + { + /// + /// File entry path + /// - Older versions are always 256 bytes + /// - Newer versions are always 512 bytes + /// + /// Length may be tied to unknown values in header + public byte[]? Path { get; set; } + + /// + /// Type of the entry data + /// + public MatroshkaEntryType EntryType { get; set; } + + /// + /// Data size + /// + public uint Size { get; set; } + + /// + /// Data offset within the package + /// + public uint Offset { get; set; } + + /// + /// Unknown value only seen in later versions + /// + public uint? Unknown { get; set; } + + /// + /// File modification time, stored in NTFS filetime. + /// + /// + public ulong ModifiedTime { get; set; } + + /// + /// File creation time, stored in NTFS filetime. + /// + /// + public ulong CreatedTime { get; set; } + + /// + /// File access time, stored in NTFS filetime. + /// + /// + public ulong AccessedTime { get; set; } + + /// + /// MD5 hash of the data + /// + /// 16 bytes + public byte[]? MD5 { get; set; } + } +} \ No newline at end of file diff --git a/SabreTools.Models/SecuROM/MatroshkaPackage.cs b/SabreTools.Models/SecuROM/MatroshkaPackage.cs new file mode 100644 index 0000000..a0a80e7 --- /dev/null +++ b/SabreTools.Models/SecuROM/MatroshkaPackage.cs @@ -0,0 +1,70 @@ +namespace SabreTools.Models.SecuROM +{ + /// + /// Securom Matroschka Package, a package contained in an executable as + /// a section. Offered by SecuROM, its main purpose seems to be managing + /// some sort of SecuROM-related operation involving multiple temporary + /// files contained within the package. Observed in Release Control + /// executables, Product Activation Revocation executables, and in some + /// regular Product-Activation-protected releases (such as the digital + /// download releases of Neverwinter Nights 2 and Test Drive Unlimited) + /// where the game executable, paul.dll and other PA-related files are + /// stored in the matroschka package. + /// + public class MatroshkaPackage + { + /// + /// "MatR" + /// + /// 4 bytes + public string? Signature { get; set; } + + /// + /// Number of internal entries + /// + public uint EntryCount { get; set; } + + #region Release Control only + + /// + /// One of four unknown values only observed on RC matroschka sections + /// + /// Only values of 0 or 1 have been found + public uint? UnknownRCValue1 { get; set; } + + /// + /// One of four unknown values only observed on RC matroschka sections + /// + /// Only values of 0 or 1 have been found + public uint? UnknownRCValue2 { get; set; } + + /// + /// One of four unknown values only observed on RC matroschka sections + /// + /// Only values of 0 or 1 have been found + public uint? UnknownRCValue3 { get; set; } + + /// + /// Unknown 32-character string only observed on RC matroschka sections. + /// Due to encryption on later DFA-encrypted RC executables, this is the + /// most reliable way to identify which executables are using the same or + /// a different key (even if the encrypted executables inside can be + /// different). + /// + public string? UnknownRCString { get; set; } + + /// + /// One of four unknown values only observed on RC matroschka sections, + /// possibly padding for alignment + /// + /// Only a value of 0 have been found + public uint? UnknownRCValue4 { get; set; } + + #endregion + + /// + /// Entries array whose length is given by + /// + public MatroshkaEntry[]? Entries { get; set; } + } +} \ No newline at end of file