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 diff --git a/BinaryObjectScanner/Packer/Crunch.cs b/BinaryObjectScanner/Packer/Crunch.cs new file mode 100644 index 00000000..f30488ff --- /dev/null +++ b/BinaryObjectScanner/Packer/Crunch.cs @@ -0,0 +1,32 @@ +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 : IExtractableExecutable + { + /// + 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; + } + + /// + public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug) + { + return false; + } + } +} diff --git a/BinaryObjectScanner/Protection/SmartE.cs b/BinaryObjectScanner/Protection/SmartE.cs index e04422be..24702b14 100644 --- a/BinaryObjectScanner/Protection/SmartE.cs +++ b/BinaryObjectScanner/Protection/SmartE.cs @@ -1,26 +1,45 @@ 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; + } + } + + // 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/) + + return null; } /// @@ -49,5 +68,31 @@ 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"), + ]; + } } }