Skip to content
36 changes: 36 additions & 0 deletions BinaryObjectScanner.Test/Packer/CrunchTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
32 changes: 32 additions & 0 deletions BinaryObjectScanner/Packer/Crunch.cs
Original file line number Diff line number Diff line change
@@ -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<PortableExecutable>
{
/// <inheritdoc/>
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;
}

/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}
67 changes: 56 additions & 11 deletions BinaryObjectScanner/Protection/SmartE.cs
Original file line number Diff line number Diff line change
@@ -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<PortableExecutable>, IPathCheck
public class SmartE : IPathCheck, IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
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;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -49,5 +68,31 @@ public List<string> CheckDirectoryPath(string path, List<string>? files)

return MatchUtil.GetFirstMatch(path, matchers, any: true);
}

/// <summary>
/// Generate the set of matchers used for each section
/// </summary>
private static List<ContentMatchSet> 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"),
];
}
}
}
Loading