diff --git a/BinaryObjectScanner.FileType/BinaryObjectScanner.FileType.csproj b/BinaryObjectScanner.FileType/BinaryObjectScanner.FileType.csproj index 12347ebb..0ccae44d 100644 --- a/BinaryObjectScanner.FileType/BinaryObjectScanner.FileType.csproj +++ b/BinaryObjectScanner.FileType/BinaryObjectScanner.FileType.csproj @@ -22,6 +22,7 @@ + diff --git a/BinaryObjectScanner.FileType/Executable.cs b/BinaryObjectScanner.FileType/Executable.cs index 5d57262f..ee13bb25 100644 --- a/BinaryObjectScanner.FileType/Executable.cs +++ b/BinaryObjectScanner.FileType/Executable.cs @@ -23,6 +23,11 @@ namespace BinaryObjectScanner.FileType { #region Properties + /// + /// Determines if game engines are counted as detected protections or not + /// + public bool IncludeGameEngines { get; set; } + /// /// Determines if packers are counted as detected protections or not /// @@ -233,8 +238,12 @@ namespace BinaryObjectScanner.FileType if (string.IsNullOrWhiteSpace(protection)) return; - // If we are filtering the output of the check - if (!CheckIfPacker(checkClass) || !IncludePackers) + // If we are filtering on game engines + if (CheckIfGameEngine(checkClass) && !IncludeGameEngines) + return; + + // If we are filtering on packers + if (CheckIfPacker(checkClass) && !IncludePackers) return; protections.TryAdd(checkClass, protection); @@ -263,8 +272,12 @@ namespace BinaryObjectScanner.FileType if (string.IsNullOrWhiteSpace(protection)) return; - // If we are filtering the output of the check - if (!CheckIfPacker(checkClass) || !IncludePackers) + // If we are filtering on game engines + if (CheckIfGameEngine(checkClass) && !IncludeGameEngines) + return; + + // If we are filtering on packers + if (CheckIfPacker(checkClass) && !IncludePackers) return; protections.TryAdd(checkClass, protection); @@ -293,8 +306,12 @@ namespace BinaryObjectScanner.FileType if (string.IsNullOrWhiteSpace(protection)) return; - // If we are filtering the output of the check - if (!CheckIfPacker(checkClass) || !IncludePackers) + // If we are filtering on game engines + if (CheckIfGameEngine(checkClass) && !IncludeGameEngines) + return; + + // If we are filtering on packers + if (CheckIfPacker(checkClass) && !IncludePackers) return; protections.TryAdd(checkClass, protection); @@ -323,8 +340,12 @@ namespace BinaryObjectScanner.FileType if (string.IsNullOrWhiteSpace(protection)) return; - // If we are filtering the output of the check - if (!CheckIfPacker(checkClass) || !IncludePackers) + // If we are filtering on game engines + if (CheckIfGameEngine(checkClass) && !IncludeGameEngines) + return; + + // If we are filtering on packers + if (CheckIfPacker(checkClass) && !IncludePackers) return; protections.TryAdd(checkClass, protection); @@ -353,8 +374,12 @@ namespace BinaryObjectScanner.FileType if (string.IsNullOrWhiteSpace(protection)) return; - // If we are filtering the output of the check - if (!CheckIfPacker(checkClass) || !IncludePackers) + // If we are filtering on game engines + if (CheckIfGameEngine(checkClass) && !IncludeGameEngines) + return; + + // If we are filtering on packers + if (CheckIfPacker(checkClass) && !IncludePackers) return; protections.TryAdd(checkClass, protection); @@ -371,8 +396,9 @@ namespace BinaryObjectScanner.FileType /// Initialize all implementations of a type /// private static IEnumerable InitCheckClasses() - => InitCheckClasses(typeof(BinaryObjectScanner.Packer._DUMMY).Assembly) - .Concat(InitCheckClasses(typeof(BinaryObjectScanner.Protection._DUMMY).Assembly)); + => InitCheckClasses(typeof(GameEngine._DUMMY).Assembly) + .Concat(InitCheckClasses(typeof(Packer._DUMMY).Assembly)) + .Concat(InitCheckClasses(typeof(Protection._DUMMY).Assembly)); /// /// Initialize all implementations of a type @@ -388,6 +414,15 @@ namespace BinaryObjectScanner.FileType #region Helpers + /// + /// Check to see if an implementation is a game engine using reflection + /// + /// Implementation that was last used to check + private static bool CheckIfGameEngine(object impl) + { + return impl.GetType().Namespace.ToLowerInvariant().Contains("gameengine"); + } + /// /// Check to see if an implementation is a packer using reflection /// diff --git a/BinaryObjectScanner.GameEngine/BinaryObjectScanner.GameEngine.csproj b/BinaryObjectScanner.GameEngine/BinaryObjectScanner.GameEngine.csproj new file mode 100644 index 00000000..857023f7 --- /dev/null +++ b/BinaryObjectScanner.GameEngine/BinaryObjectScanner.GameEngine.csproj @@ -0,0 +1,29 @@ + + + + net48;net6.0;net7.0 + win-x86;win-x64;linux-x64;osx-x64 + BinaryObjectScanner.GameEngine + BinaryObjectScanner.GameEngine + Matt Nadareski + BurnOutSharp + Copyright (c)2022 Matt Nadareski + https://github.com/mnadareski/BurnOutSharp + 2.7 + 2.7 + 2.7 + true + true + + + + true + + + + + + + + + diff --git a/BinaryObjectScanner.GameEngine/_DUMMY.cs b/BinaryObjectScanner.GameEngine/_DUMMY.cs new file mode 100644 index 00000000..c3a6c514 --- /dev/null +++ b/BinaryObjectScanner.GameEngine/_DUMMY.cs @@ -0,0 +1,7 @@ +namespace BinaryObjectScanner.GameEngine +{ + /// + /// This class exists for reflection purposes and should not be used + /// + public sealed class _DUMMY { } +} diff --git a/BurnOutSharp.sln b/BurnOutSharp.sln index af886d16..01fe8980 100644 --- a/BurnOutSharp.sln +++ b/BurnOutSharp.sln @@ -38,7 +38,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner.Protect EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner.Packer", "BinaryObjectScanner.Packer\BinaryObjectScanner.Packer.csproj", "{59B3DE26-9399-4B5A-B39B-6239DAF1A017}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BinaryObjectScanner.FileType", "BinaryObjectScanner.FileType\BinaryObjectScanner.FileType.csproj", "{6733368B-63D4-4921-AE13-FD1A53C5180C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner.FileType", "BinaryObjectScanner.FileType\BinaryObjectScanner.FileType.csproj", "{6733368B-63D4-4921-AE13-FD1A53C5180C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BinaryObjectScanner.GameEngine", "BinaryObjectScanner.GameEngine\BinaryObjectScanner.GameEngine.csproj", "{8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -102,6 +104,10 @@ Global {6733368B-63D4-4921-AE13-FD1A53C5180C}.Debug|Any CPU.Build.0 = Debug|Any CPU {6733368B-63D4-4921-AE13-FD1A53C5180C}.Release|Any CPU.ActiveCfg = Release|Any CPU {6733368B-63D4-4921-AE13-FD1A53C5180C}.Release|Any CPU.Build.0 = Release|Any CPU + {8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BurnOutSharp/Options.cs b/BurnOutSharp/Options.cs index 586e96df..559682b9 100644 --- a/BurnOutSharp/Options.cs +++ b/BurnOutSharp/Options.cs @@ -15,6 +15,11 @@ /// public bool ScanContents { get; set; } + /// + /// Determines if game engines are counted as detected protections or not + /// + public bool ScanGameEngines { get; set; } + /// /// Determines if packers are counted as detected protections or not /// diff --git a/BurnOutSharp/Scanner.cs b/BurnOutSharp/Scanner.cs index cf3a663a..5c1e5040 100644 --- a/BurnOutSharp/Scanner.cs +++ b/BurnOutSharp/Scanner.cs @@ -23,10 +23,13 @@ namespace BurnOutSharp /// public bool ScanContents => options?.ScanContents ?? false; - /// + /// + public bool ScanGameEngines => options?.ScanGameEngines ?? false; + + /// public bool ScanPackers => options?.ScanPackers ?? false; - /// + /// public bool ScanPaths => options?.ScanPaths ?? false; /// @@ -49,16 +52,18 @@ namespace BurnOutSharp /// /// Enable scanning archive contents /// Enable including content detections in output + /// Enable including game engines in output /// Enable including packers in output /// Enable including path detections in output /// Enable including debug information /// Optional progress callback - public Scanner(bool scanArchives, bool scanContents, bool scanPackers, bool scanPaths, bool includeDebug, IProgress fileProgress = null) + public Scanner(bool scanArchives, bool scanContents, bool scanGameEngines, bool scanPackers, bool scanPaths, bool includeDebug, IProgress fileProgress = null) { this.options = new Options { ScanArchives = scanArchives, ScanContents = scanContents, + ScanGameEngines = scanGameEngines, ScanPackers = scanPackers, ScanPaths = scanPaths, IncludeDebug = includeDebug, @@ -300,6 +305,7 @@ namespace BurnOutSharp // If we have an executable, it needs to bypass normal handling if (detectable is Executable executable) { + executable.IncludeGameEngines = ScanGameEngines; executable.IncludePackers = ScanPackers; var subProtections = ProcessExecutable(executable, fileName, stream); if (subProtections != null) diff --git a/Coding Guide.md b/Coding Guide.md index d52568bf..49e747e7 100644 --- a/Coding Guide.md +++ b/Coding Guide.md @@ -325,6 +325,7 @@ This section contains information on project and class organization principles t | `BinaryObjectScanner.Builders` | One file per executable type. | | `BinaryObjectScanner.Compression` | One directory per compression type. | | `BinaryObjectScanner.FileType` | One file per file type. | +| `BinaryObjectScanner.GameEngine` | At least one file per game engine. Partial classes allowed. | | `BinaryObjectScanner.Interfaces` | One file per interface. | | `BinaryObjectScanner.Matching` | Flat directory structure. Include interfaces and base classes. | | `BinaryObjectScanner.Models` | One directory per executable type. One file per object model. | @@ -344,19 +345,19 @@ This section contains information on in-code organization principles that depend | Project | Description | | --- | --- | | `BurnOutSharp` | Varies from file to file. | -| `BurnOutSharp/FileType` | `IDetectable` implementations, `IExtractable` implementations, `IScannable` implementations, helper methods. | | `BurnOutSharp/Tools` | Methods grouped by function. Regions ordered alphabetically. | | `BinaryObjectScanner.ASN1` | Partial classes suggested for different implmentations. | | `BinaryObjectScanner.Builders` | Two copies of each non-generic method: one for byte arrays and one for Streams. | | `BinaryObjectScanner.Compression` | Varies from file to file. | | `BinaryObjectScanner.FileType` | `IDetectable` implementations, `IExtractable` implementations, helper methods. | +| `BinaryObjectScanner.GameEngine` | `IContentCheck` implementations, `ILinearExecutableCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IExtractable` implementations, helper methods. | | `BinaryObjectScanner.Interfaces` | Methods ordered alphabetically. | | `BinaryObjectScanner.Matching` | Varies from file to file. | | `BinaryObjectScanner.Models` | No methods at all, just properties. | | `BinaryObjectScanner.Packer` | `IContentCheck` implementations, `ILinearExecutableCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IExtractable` implementations, helper methods. | | `BinaryObjectScanner.Protection` | `IContentCheck` implementations, `ILinearExecutableCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IExtractable` implementations, helper methods. | | `BinaryObjectScanner.Utilities` | Varies from file to file. | -| `BurnOutSharp.Wrappers` | Follow region and method grouping from existing wrappers. | +| `BinaryObjectScanner.Wrappers` | Follow region and method grouping from existing wrappers. | | `psxt001z` | Varies from file to file. | | `Test` | New functionality should be added as a combination of a flag with a long and a short form, a new line in the help text, and a new method (if necessary). | diff --git a/Developer Guide.md b/Developer Guide.md index 0f0fe7c5..2fefe8bd 100644 --- a/Developer Guide.md +++ b/Developer Guide.md @@ -13,6 +13,7 @@ This is a guide for any developers who wish to research protections, implement n | `BinaryObjectScanner.Builder` | Library containing classes that assist in populating the various object models defined in `BinaryObjectScanner.Models`. Builders can work with either byte arrays or streams for input. At the time of writing, the following executable types have builders: **MS-DOS**, **New Executable**, **Portable Executable**. | | `BinaryObjectScanner.Compression` | Library containing classes that deal with different compression formats. This library is used extensively by the wrappers in `BinaryObjectScanner.Wrappers`. | | `BinaryObjectScanner.FileType` | Library containing file type definitions specific to scanning. | +| `BinaryObjectScanner.GameEngine` | Library containing game engine scanning definitions. | | `BinaryObjectScanner.Interfaces` | Library containing interface definitions for scanning and detection. | | `BinaryObjectScanner.Matching` | Library containing models and logic for generic searching and matching. This library is used extensively by the packer and protection checks in `BurnOutSharp`. | | `BinaryObjectScanner.Models` | Library containing object models that represent various pieces of known executable formats. At the time of writing, the following executable types have models: **MS-DOS**, **New Executable**, **Linear Executable (partial)**, **Portable Executable**. | @@ -91,6 +92,8 @@ Adding a new checker or format should happen in a few distinct steps: - If it is a new supported file type (such as an archive format), create the file in `BinaryObjectScanner.FileType`. By default, you will need to implement `BurnOutSharp.Interfaces.IDetectable` or `BinaryObjectScanner.Interfaces.IExtractable`. Do not implement any other interfaces. Please consider asking project maintainers before doing this work, especially if there are external dependencies. + - If it is a new supported game engine or standard library, create the file in `BinaryObjectScanner.GameEngine`. By default, you will need to implement at least one of: `BinaryObjectScanner.Interfaces.ILinearExecutableCheck`, `BinaryObjectScanner.Interfaces.INewExecutableCheck`, and `BinaryObjectScanner.Interfaces.IPortableExecutableCheck`. It is exceptionally rare to need to implement `BinaryObjectScanner.Interfaces.IPathCheck`. + - If it is a new supported executable packer, compressor, or installer format, create the file in `BinaryObjectScanner.Packer`. By default, you will need to implement `BinaryObjectScanner.Interfaces.IExtractable` as well as at least one of: `BinaryObjectScanner.Interfaces.ILinearExecutableCheck`, `BinaryObjectScanner.Interfaces.INewExecutableCheck`, and `BinaryObjectScanner.Interfaces.IPortableExecutableCheck`. It is exceptionally rare to need to implement `BinaryObjectScanner.Interfaces.IPathCheck`. - If it is a new supported DRM scheme, copy protection, or obfuscator, create the file in `BinaryObjectScanner.Protection`. By default, you will need to implement at least one of:`BinaryObjectScanner.Interfaces.ILinearExecutableCheck`, `BinaryObjectScanner.Interfaces.INewExecutableCheck`, `BinaryObjectScanner.Interfaces.IPortableExecutableCheck`, and `BinaryObjectScanner.Interfaces.IPathCheck`. It is exceptionally rare to need to implement `BinaryObjectScanner.Interfaces.Extractable`. diff --git a/Test/Options.cs b/Test/Options.cs index 86162a4c..35e2a51c 100644 --- a/Test/Options.cs +++ b/Test/Options.cs @@ -68,6 +68,11 @@ namespace Test /// public bool ScanContents { get; private set; } = true; + /// + /// Scan game engines during protection scanning + /// + public bool ScanGameEngines { get; private set; } = true; + /// /// Scan packers during protection scanning /// @@ -180,6 +185,11 @@ namespace Test options.ScanContents = false; break; + case "-ng": + case "--no-game-engines": + options.ScanGameEngines = false; + break; + case "-np": case "--no-packers": options.ScanPackers = false; @@ -230,27 +240,28 @@ namespace Test Console.WriteLine("test.exe file|directory ..."); Console.WriteLine(); Console.WriteLine("Features:"); - Console.WriteLine("-x, --extract Extract archive formats"); - Console.WriteLine("-i, --info Print executable info"); - Console.WriteLine("-s, --scan Enable protection scanning (default if none)"); + Console.WriteLine("-x, --extract Extract archive formats"); + Console.WriteLine("-i, --info Print executable info"); + Console.WriteLine("-s, --scan Enable protection scanning (default if none)"); Console.WriteLine(); Console.WriteLine("Common options:"); - Console.WriteLine("-?, -h, --help Display this help text and quit"); - Console.WriteLine("-d, --debug Enable debug mode"); + Console.WriteLine("-?, -h, --help Display this help text and quit"); + Console.WriteLine("-d, --debug Enable debug mode"); Console.WriteLine(); Console.WriteLine("Extraction options:"); - Console.WriteLine("-o, --outdir [PATH] Set output path for extraction (required)"); + Console.WriteLine("-o, --outdir [PATH] Set output path for extraction (required)"); #if NET6_0_OR_GREATER Console.WriteLine(); Console.WriteLine("Information options:"); - Console.WriteLine("-j, --json Print executable info as JSON"); + Console.WriteLine("-j, --json Print executable info as JSON"); #endif Console.WriteLine(); Console.WriteLine("Scanning options:"); - Console.WriteLine("-nc, --no-contents Disable scanning for content checks"); - Console.WriteLine("-na, --no-archives Disable scanning archives"); - Console.WriteLine("-np, --no-packers Disable scanning for packers"); - Console.WriteLine("-ns, --no-paths Disable scanning for path checks"); + Console.WriteLine("-nc, --no-contents Disable scanning for content checks"); + Console.WriteLine("-na, --no-archives Disable scanning archives"); + Console.WriteLine("-ng, --no-game-engines Disable scanning for game engines"); + Console.WriteLine("-np, --no-packers Disable scanning for packers"); + Console.WriteLine("-ns, --no-paths Disable scanning for path checks"); } /// diff --git a/Test/Program.cs b/Test/Program.cs index 7f816406..b237fc4c 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -31,6 +31,7 @@ namespace Test var scanner = new Scanner( options.ScanArchives, options.ScanContents, + options.ScanGameEngines, options.ScanPackers, options.ScanPaths, options.Debug,