Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 137 additions & 8 deletions BinaryObjectScanner/Protection/SecuROM.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Text;
using BinaryObjectScanner.Interfaces;
Expand All @@ -13,6 +13,89 @@ namespace BinaryObjectScanner.Protection
// TODO: Investigate SecuROM for Macintosh
public class SecuROM : IExecutableCheck<PortableExecutable>, IPathCheck
{
/// <summary>
/// Matches hash of the Release Control-encrypted executable to known hashes
/// </summary>
// Allegedly, some version of Runaway: A Twist of Fate has RC
private static readonly Dictionary<string, string> MatroschkaHashDictionary = new Dictionary<string, string>()
{
{"C6DFF6B08EE126893840E107FD4EC9F6", "Alice - Madness Returns (USA)+(Europe)"},
{"D7703D32B72185358D58448B235BD55E", "Arcania - Gothic 4 - Not in redump yet"},
{"FAF6DD75DDB335101CB77A714793DC28", "Batman - Arkham City - Game of the Year Edition (UK)"},
{"77999579EE4378BDFAC9438CC9CDB44E", "Batman - Arkham City (USA)+(Europe)"},
{"73114CF3DEEDD0FA2BF52ACB70B048BC", "Battlefield - Bad Company 2 (GFWM)"},
{"56C23D930F885BA5BF026FEABFC31856", "Battlefield 3 (USA)+(Europe, Asia)"},
{"631C0ACE596722488E3393BD1AFCE731", "Battlefield 3 (Russia)"},
{"6E481CDEBDB30B8889340CEC3300C931", "Battlefield 3 (UK)"},
{"C5AB3931A3CBB0141CC5A4638C391F4F", "BioShock 2 (Argentina)+(Europe, Australia)+(Europe)+(Europe) (Alt)+(Netherlands)+(USA) - Multiplayer executable"},
{"73DB35419A651CB69E78A641BBC88A4C", "BioShock 2 (Argentina)+(Europe, Australia)+(Europe)+(Europe) (Alt)+(Netherlands)+(USA) - Singleplayer executable"},
{"E5D63D369023A1D1074E7B13952FA0F2", "BioShock 2 (Russia) - Multiplayer executable"},
{"C39F3BCB74EA8E1215D39AC308F64229", "BioShock 2 (Russia) - Singleplayer executable"},
{"3C340B2D4DA25039C136FEE1DC2DDE17", "Borderlands (USA)+(Europe) (En,Fr,De,Es,It)"},
{"D35122E0E3F7B35C98BEFD706C260F83", "Crysis Warhead (Europe)+(Russia)+(USA)+(USA) (Alt)"},
{"D9254D3353AB229806A806FCFCEABDBD", "Crysis Warhead (Japan)"},
{"D69798C9198A6DB6A265833B350AC544", "Crysis Warhead (Turkey)"},
{"9F574D56F1A4D7847C6A258DC2AF61A5", "Crysis Wars (Europe)+(Japan)+(Russia)+(Turkey)+(USA)+(USA) (Rerelease)"},
{"C200ABC342A56829A5356AA0BEA5F2DF", "Dead Space 2 (Europe)+(Russia)+(USA)"},
{"81B3415AF21C8691A1CD55A422BA64D5", "Disney TRON - Evolution (Europe) (En,Fr,De,Es,It,Nl)"},
{"DF9609EDE95A1F89F7A39A08778CC3B8", "Disney Tron - Evolution (Europe) (Pl,Cs)"},
{"B8698C7C05D7F9E049DC038B9868FCF7", "Disney TRON - Evolution (Russia) (En,Ru)"},
{"0D5800F94643633CD3F025CFFD968DF2", "Dragon Age II (Europe)+(USA) - PC executable"},
{"3F1AFA4783F9001AACF0379A2A432A13", "Dragon Age II (Europe)+(USA) - Mac executable"},
{"530A3EB454570EEE5519ABE6BAE0187C", "Far Cry 2 (Europe)+(USA) (En,Fr,De,Es,It)"},
{"4B3B130A70F3711BFA8AF06195FE4250", "FIFA 12 (Europe)"},
{"F43F777696B0FAD3A331298C48104B31", "FIFA 13 (Europe)"},
{"1DF0E096068839C12E4B353AC50E41FA", "Grand Theft Auto - Episodes from Liberty City (Russia)"},
{"F3ADC6D08BEC42FB988F2F62B5C731FA", "Grand Theft Auto - Episodes from Liberty City (USA)"},
{"5B90D42A650A8F08095984AEE3D961B9", "Grand Theft Auto IV (Europe, Asia)+(Europe)+(Latin America)+(USA) (Rev 1)"},
{"4510F0BDD58D30D072952E225E294F9B", "Grand Theft Auto IV (USA)"},
{"2AC9616A7FE46D142F653D798EAA07FD", "Harry Potter and the Deathly Hallows Part 2 (GFWM)"},
{"AE144755FB12062780E4E4CCD29B5296", "Kingdoms of Amalur - Reckoning (Germany)"},
{"6E4AB6416D91F85954150BC50D02688E", "Kingdoms of Amalur - Reckoning (USA) (En,Fr,Es,It,Nl)"},
{"935103B1600F1C743AF892A0DD761913", "Mass Effect 2 (GFWM)"},
{"EEB2AE163AEEF6BE54C5A9BDD38C600E", "Mass Effect 3 (Europe, Australia)+(USA)"},
{"2D08B73217B722A4F9E01523F07E118E", "Mass Effect 3 (UK)"},
{"4EA3CE0670DECD0A74FA312714C22025", "Need for Speed - The Run (Europe)"},
{"88AB0D4A4EE7867F740AD063400FCDB5", "Need for Speed - The Run (Russia)"},
{"EAD8E224D0F44706BA92BD9B27FEBA7D", "Need for Speed - The Run (USA)"},
{"316FF217BD129F9EEBD05A321A8FBE60", "Syndicate (USA)+(Europe) (En,Fr,De,Es,It,Ru)"},
};

/// <summary>
/// If hash isn't currently known, check size and pathname of the encrypted executable to determine if alt or entirely missing
/// </summary>
private static readonly Dictionary<uint, string> MatroschkaSizeFilenameDictionary = new Dictionary<uint, string>()
{
{4646091, "hp8.aec"},
{5124592, "output\\LaunchGTAIV.aec"},
{5445032, "output\\Crysis.aec"},
{5531004, "output\\FarCry2.aec"},
{6716108, "LaunchEFLC.aec"},
{6728396, "./Bioshock2Launcher.aec"},
{6732492, "./BioShock2Launcher.aec"},
{7150283, "GridGameLauncher.aec"},
{7154379, "GridGameLauncher.aec"},
{8705763, "temp0.aec"},
{12137051, "dragonage2.aec"},
{12896904, "output\\crysis.aec"},
{12917384, "output\\crysis.aec"},
{12925576, "output\\crysis.aec"},
{16415836, "output\\MassEffect2.aec"},
{17199339, "AliceMadnessReturns.aec"},
{22357747, "MassEffect3.aec"},
{23069931, "fifa.aec"},
{25823091, "Arcania.aec"},
{27564780, "output\\BFBC2Game.aec"},
{30470419, "temp0.aec"},
{32920811, "temp0.aec"},
{35317996, "output\\ShippingPC-WillowGame-SecuROM.aec"},
{35610875, "temp0.aec"},
{37988075, "temp0.aec"},
{43612419, "BatmanAC.aec"},
{45211355, "BatmanAC.aec"},
{48093043, "deadspace_f.aec"},
};

/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
Expand All @@ -21,14 +104,16 @@ public class SecuROM : IExecutableCheck<PortableExecutable>, IPathCheck
if (paModule != null)
return paModule;

// Get the matrosch section, if it exists
if (exe.ContainsSection("matrosch", exact: true))
return $"SecuROM Matroschka Package";

// Get the rcpacker section, if it exists
if (exe.ContainsSection("rcpacker", exact: true))
return $"SecuROM Release Control";
// Check if executable contains a SecuROM Matroschka Package

var package = exe.MatroschkaPackage;
if (package != null)
{
var packageType = CheckMatroschkaPackage(package, includeDebug);
if (packageType != null)
return packageType;
}

if (exe.ContainsSection(".dsstext", exact: true))
return $"SecuROM 8.03.03+";

Expand Down Expand Up @@ -341,5 +426,49 @@ private static string GetV8WhiteLabelVersion(PortableExecutable exe)

return null;
}

/// <summary>
/// Helper method to run checks on a SecuROM Matroschka Package
/// </summary>
private static string? CheckMatroschkaPackage(SecuROMMatroschkaPackage package, bool includeDebug)
{
// Check for all 0x00 required, as at least one known non-RC matroschka has the field, just empty.
if (package.KeyHexString == null || package.KeyHexString.Trim('\0').Length == 0)
return "SecuROM Matroschka Package";

if (package.Entries == null || package.Entries.Length == 0)
return "SecuROM Matroschka Package - No Entries? Please report";

// The second entry in a Release Control matroschka package is always the encrypted executable
var entry = package.Entries[1];

if (entry.MD5 == null || entry.MD5.Length == 0)
return "SecuROM Matroschka Package - No MD5? Please report";

string md5String = BitConverter.ToString(entry.MD5!);
md5String = md5String.ToUpperInvariant().Replace("-", string.Empty);

// Not used yet, but will be in the future
var fileData = package.ReadFileData(entry, includeDebug);

// Check if encrypted executable is known via hash
if (MatroschkaHashDictionary.TryGetValue(md5String, out var gameName))
{
// Returning "SecuROM Matroschka Package" technically redundant since implied.
return $"SecuROM Release Control - {gameName}";
}

// If not known, check if encrypted executable is likely an alt signing of a known executable
// Filetime could be checked here, but if it was signed at a different time, the time will vary anyways
var readPathBytes = entry.Path;
if (readPathBytes == null || readPathBytes.Length == 0)
return $"SecuROM Release Control - Unknown executable {md5String},{entry.Size}, please report to us on Github!";

var readPathName = Encoding.ASCII.GetString(readPathBytes).TrimEnd('\0');
if (MatroschkaSizeFilenameDictionary.TryGetValue(entry.Size, out var pathName) && pathName == readPathName)
return $"SecuROM Release Control - Unknown possible alt executable of size {entry.Size}, please report to us on Github!";

return $"SecuROM Release Control - Unknown executable {readPathName},{md5String},{entry.Size}, please report to us on Github!";
}
}
}