From 70808560ccb278d87229159dba44c836230ae2dc Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 13 Jan 2025 20:52:06 -0500 Subject: [PATCH 01/10] Move Crunch to its own Packer, improve SmartE detection. --- BinaryObjectScanner/Packer/Crunch.cs | 26 ++++++ BinaryObjectScanner/Protection/SmartE.cs | 107 ++++++++++++++++++++--- 2 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 BinaryObjectScanner/Packer/Crunch.cs diff --git a/BinaryObjectScanner/Packer/Crunch.cs b/BinaryObjectScanner/Packer/Crunch.cs new file mode 100644 index 00000000..3b7c1960 --- /dev/null +++ b/BinaryObjectScanner/Packer/Crunch.cs @@ -0,0 +1,26 @@ +using BinaryObjectScanner.Interfaces; +using SabreTools.Serialization.Wrappers; + +namespace BinaryObjectScanner.Packer +{ + // Packer used by all known SmartE games, but also used by some other non-SmartE protected software as well. + // https://web.archive.org/web/20020806102129/http://www.bit-arts.com/windows_solutions.html + // TODO: Other BitArts products may also use this same string. No samples have yet been found. + public class Crunch : IExecutableCheck + { + /// + public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug) + { + // Get the last section strings, if they exist + var sections = pex.Model.SectionTable ?? []; + var strs = pex.GetSectionStrings(sections.Length - 1); + if (strs != null) + { + if (strs.Exists(s => s.Contains("BITARTS"))) + return "Crunch"; + } + + return null; + } + } +} diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index e04422be..ff6f3d8d 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -1,28 +1,84 @@ using System.Collections.Generic; using BinaryObjectScanner.Interfaces; using SabreTools.Matching; +using SabreTools.Matching.Content; using SabreTools.Matching.Paths; using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Protection { - public class SmartE : IExecutableCheck, IPathCheck + public class SmartE : IPathCheck, IExecutableCheck { - /// public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug) { - // Get the last section strings, if they exist - var sections = pex.Model.SectionTable ?? []; - var strs = pex.GetSectionStrings(sections.Length - 1); - if (strs != null) - { - if (strs.Exists(s => s.Contains("BITARTS"))) - return "SmartE"; - } + // Only works on stub generated from running the program yourself + if (pex.InternalName.OptionalEquals("SmarteSECURE")) + { + return "SmartE"; + } + var sections = pex.Model.SectionTable ?? []; - return null; - } + if (sections.Length > 0) + { + // Get the last section data, if it exists + var lastSectionData = pex.GetSectionData(sections.Length - 1); + if (lastSectionData != null) + { + // All sections seen so far are the last sections, so this is "technically" + // the only known needed check so far. Others kept as backups if this fails + // on some future entry + var matchers = GenerateMatchers(); + var match = MatchUtil.GetFirstMatch(file, lastSectionData, matchers, includeDebug); + if (!string.IsNullOrEmpty(match)) + return match; + } + } + + // Get the .bss section, if it exists + var bssSectionRaw = pex.GetFirstSectionData(".bss", exact: true); + if (bssSectionRaw != null) + { + //Rise of Nations + var matchers = GenerateMatchers(); + var match = MatchUtil.GetFirstMatch(file, bssSectionRaw, matchers, includeDebug); + if (!string.IsNullOrEmpty(match)) + return match; + } + // Get the .tls section, if it exists + var tlsSectionRaw = pex.GetFirstSectionData(".tls", exact: true); + if (tlsSectionRaw != null) + { + //Zoo Tycoon 2 + var matchers = GenerateMatchers(); + var match = MatchUtil.GetFirstMatch(file, tlsSectionRaw, matchers, includeDebug); + if (!string.IsNullOrEmpty(match)) + return match; + } + + // Get the .idata section, if it exists + var idataSectionRaw = pex.GetFirstSectionData(".idata", exact: true); + if (idataSectionRaw != null) + { + // http://redump.org/disc/58561/ and http://redump.org/disc/71983/ + var matchers = GenerateMatchers(); + var match = MatchUtil.GetFirstMatch(file, idataSectionRaw, matchers, includeDebug); + if (!string.IsNullOrEmpty(match)) + return match; + } + + // Get the .edata section, if it exists + var edataSectionRaw = pex.GetFirstSectionData(".edata", exact: true); + if (edataSectionRaw != null) + { + // http://redump.org/disc/36619/ + var matchers = GenerateMatchers(); + var match = MatchUtil.GetFirstMatch(file, edataSectionRaw, matchers, includeDebug); + if (!string.IsNullOrEmpty(match)) + return match; + } + return null; + } /// public List CheckDirectoryPath(string path, List? files) { @@ -49,5 +105,32 @@ public List CheckDirectoryPath(string path, List? files) return MatchUtil.GetFirstMatch(path, matchers, any: true); } + /// + /// Generate the set of matchers used for each section + /// + /// + private static List GenerateMatchers() + { + return + [ + // Matches most games, but a few like http://redump.org/disc/16541/ + // are only matched on the 00001/2.TMP files. PiD and other programs + // don't detect this game either, though (Aside from the stub) + new(new byte?[] + { + 0xEB, 0x15, 0x03, 0x00, 0x00, 0x00, null, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x55, + 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED, + 0x1D, 0x00, 0x00, 0x00, 0x8B, 0xC5, 0x55, 0x60, + 0x9C, 0x2B, 0x85, 0x8F, 0x07, 0x00, 0x00, 0x89, + 0x85, 0x83, 0x07, 0x00, 0x00, 0xFF, 0x74, 0x24, + 0x2C, 0xE8, 0xBB, 0x01, 0x00, 0x00, 0x0F, 0x82, + 0x2F, 0x06, 0x00, 0x00, 0xE8, 0x8E, 0x04, 0x00, + 0x00, 0x49, 0x0F, 0x88, 0x23, 0x06 + }, "SmartE"), + ]; + } } + } From fef5cc821e84fe441225a0e1ec0c8dd676d07af1 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Mon, 13 Jan 2025 22:49:26 -0500 Subject: [PATCH 02/10] Fix formatting by removing brackets Co-authored-by: Matt Nadareski --- BinaryObjectScanner/Protection/SmartE.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index ff6f3d8d..73135067 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -13,9 +13,8 @@ public class SmartE : IPathCheck, IExecutableCheck { // Only works on stub generated from running the program yourself if (pex.InternalName.OptionalEquals("SmarteSECURE")) - { return "SmartE"; - } + var sections = pex.Model.SectionTable ?? []; if (sections.Length > 0) From 7b23508a7a683e1e7702fa3a2c6587d2e0066ba3 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Mon, 13 Jan 2025 22:56:42 -0500 Subject: [PATCH 03/10] Remove named section checks, minor formatting fixes --- BinaryObjectScanner/Protection/SmartE.cs | 48 +++--------------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index 73135067..ba916e79 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -33,51 +33,15 @@ public class SmartE : IPathCheck, IExecutableCheck } } - // Get the .bss section, if it exists - var bssSectionRaw = pex.GetFirstSectionData(".bss", exact: true); - if (bssSectionRaw != null) - { - //Rise of Nations - var matchers = GenerateMatchers(); - var match = MatchUtil.GetFirstMatch(file, bssSectionRaw, matchers, includeDebug); - if (!string.IsNullOrEmpty(match)) - return match; - } - - // Get the .tls section, if it exists - var tlsSectionRaw = pex.GetFirstSectionData(".tls", exact: true); - if (tlsSectionRaw != null) - { - //Zoo Tycoon 2 - var matchers = GenerateMatchers(); - var match = MatchUtil.GetFirstMatch(file, tlsSectionRaw, matchers, includeDebug); - if (!string.IsNullOrEmpty(match)) - return match; - } + // Specific known named sections: + // .bss (Rise of Nations) + // .tls (Zoo Tycoon 2) + // .idata (http://redump.org/disc/58561/ and http://redump.org/disc/71983/) + // .edata (http://redump.org/disc/36619/) - // Get the .idata section, if it exists - var idataSectionRaw = pex.GetFirstSectionData(".idata", exact: true); - if (idataSectionRaw != null) - { - // http://redump.org/disc/58561/ and http://redump.org/disc/71983/ - var matchers = GenerateMatchers(); - var match = MatchUtil.GetFirstMatch(file, idataSectionRaw, matchers, includeDebug); - if (!string.IsNullOrEmpty(match)) - return match; - } - - // Get the .edata section, if it exists - var edataSectionRaw = pex.GetFirstSectionData(".edata", exact: true); - if (edataSectionRaw != null) - { - // http://redump.org/disc/36619/ - var matchers = GenerateMatchers(); - var match = MatchUtil.GetFirstMatch(file, edataSectionRaw, matchers, includeDebug); - if (!string.IsNullOrEmpty(match)) - return match; - } return null; } + /// public List CheckDirectoryPath(string path, List? files) { From 066cbe89d1a8941d3027d023e44875800153bfe1 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Mon, 13 Jan 2025 22:58:41 -0500 Subject: [PATCH 04/10] Add newline before summary. --- BinaryObjectScanner/Protection/SmartE.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index ba916e79..a245a8a2 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -68,6 +68,7 @@ public List CheckDirectoryPath(string path, List? files) return MatchUtil.GetFirstMatch(path, matchers, any: true); } + /// /// Generate the set of matchers used for each section /// From 91fdb9e274c452fd3eaafa7f1e73688f71e71c72 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:00:36 -0500 Subject: [PATCH 05/10] Remove empty returns. --- BinaryObjectScanner/Protection/SmartE.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index a245a8a2..4b4c38de 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -72,7 +72,6 @@ public List CheckDirectoryPath(string path, List? files) /// /// Generate the set of matchers used for each section /// - /// private static List GenerateMatchers() { return From 4e5c25843ff0f205dfaced1c895c7174f43fb094 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:01:26 -0500 Subject: [PATCH 06/10] Remove unnecessary newline --- BinaryObjectScanner/Protection/SmartE.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index 4b4c38de..06990b88 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -95,5 +95,4 @@ private static List GenerateMatchers() ]; } } - } From 967f14fc42669420cc0c031823dc65bfe3e953f0 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:03:35 -0500 Subject: [PATCH 07/10] Change Crunch to use IExtractableExecutable --- BinaryObjectScanner/Packer/Crunch.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/BinaryObjectScanner/Packer/Crunch.cs b/BinaryObjectScanner/Packer/Crunch.cs index 3b7c1960..f30488ff 100644 --- a/BinaryObjectScanner/Packer/Crunch.cs +++ b/BinaryObjectScanner/Packer/Crunch.cs @@ -6,7 +6,7 @@ namespace BinaryObjectScanner.Packer // Packer used by all known SmartE games, but also used by some other non-SmartE protected software as well. // https://web.archive.org/web/20020806102129/http://www.bit-arts.com/windows_solutions.html // TODO: Other BitArts products may also use this same string. No samples have yet been found. - public class Crunch : IExecutableCheck + public class Crunch : IExtractableExecutable { /// public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -22,5 +22,11 @@ public class Crunch : IExecutableCheck return null; } + + /// + public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug) + { + return false; + } } } From b12ba0e3f31f3d9431cb39c97acc49127b363dd7 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:11:19 -0500 Subject: [PATCH 08/10] Remove unnecessary whitespace. --- BinaryObjectScanner/Protection/SmartE.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index 06990b88..24702b14 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -41,7 +41,7 @@ public class SmartE : IPathCheck, IExecutableCheck return null; } - + /// public List CheckDirectoryPath(string path, List? files) { From ae8d89df89c34f27fadcb933bd2a714f7a1076cd Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 13 Jan 2025 23:15:11 -0500 Subject: [PATCH 09/10] Add tests for Crunch. --- .../Packer/CrunchTests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 BinaryObjectScanner.Test/Packer/CrunchTests.cs diff --git a/BinaryObjectScanner.Test/Packer/CrunchTests.cs b/BinaryObjectScanner.Test/Packer/CrunchTests.cs new file mode 100644 index 00000000..2870f1dd --- /dev/null +++ b/BinaryObjectScanner.Test/Packer/CrunchTests.cs @@ -0,0 +1,36 @@ +using System.IO; +using BinaryObjectScanner.Packer; +using Xunit; + +namespace BinaryObjectScanner.Test.Packer +{ + public class CrunchTests + { + [Fact] + public void CheckPortableExecutableTest() + { + string file = "filename"; + SabreTools.Models.PortableExecutable.Executable model = new(); + Stream source = new MemoryStream(); + SabreTools.Serialization.Wrappers.PortableExecutable pex = new(model, source); + + var checker = new Crunch(); + string? actual = checker.CheckExecutable(file, pex, includeDebug: false); + Assert.Null(actual); + } + + [Fact] + public void ExtractPortableExecutableTest() + { + string file = "filename"; + SabreTools.Models.PortableExecutable.Executable model = new(); + Stream source = new MemoryStream(); + SabreTools.Serialization.Wrappers.PortableExecutable pex = new(model, source); + string outputDir = string.Empty; + + var checker = new Crunch(); + bool actual = checker.Extract(file, pex, outputDir, includeDebug: false); + Assert.False(actual); + } + } +} \ No newline at end of file From de4e9cfc9c39a0602533255278754356f49ad4fc Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:13:46 -0400 Subject: [PATCH 10/10] Add rcpacker detection Some late securom Release Control games call what'd usually be the matrosch section "rcpacker", as in Release Control packer. Not detected at the moment because of this, and given the name, should only refer to Securom Release Control. Games affected by this include all currently dumped in redump versions of FIFA 13 and Mass Effect 3 with Release Control. --- BinaryObjectScanner/Protection/SecuROM.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BinaryObjectScanner/Protection/SecuROM.cs b/BinaryObjectScanner/Protection/SecuROM.cs index af8bcfe2..01b9e828 100644 --- a/BinaryObjectScanner/Protection/SecuROM.cs +++ b/BinaryObjectScanner/Protection/SecuROM.cs @@ -38,6 +38,10 @@ public class SecuROM : IExecutableCheck, IPathCheck if (pex.ContainsSection("matrosch", exact: true)) return $"SecuROM Matroschka Package"; + // Get the rcpacker section, if it exists + if (pex.ContainsSection("rcpacker", exact: true)) + return $"SecuROM Release Control"; + if (pex.ContainsSection(".dsstext", exact: true)) return $"SecuROM 8.03.03+";