Create wrapper-centric methods

This commit is contained in:
Matt Nadareski
2025-09-06 10:40:26 -04:00
parent 821b91a555
commit 46feb3e568
18 changed files with 130 additions and 196 deletions

View File

@@ -12,14 +12,14 @@ namespace BinaryObjectScanner.Test
private static readonly List<WrapperType> _detectableTypes =
[
WrapperType.AACSMediaKeyBlock,
WrapperType.BDPlusSVM,
//WrapperType.CIA,
//WrapperType.Executable,
// WrapperType.AACSMediaKeyBlock, // TODO: Create wrapper to reenable test
// WrapperType.BDPlusSVM, // TODO: Create wrapper to reenable test
// WrapperType.CIA,
// WrapperType.Executable, // TODO: This needs to be split internally
WrapperType.LDSCRYPT,
//WrapperType.N3DS,
//WrapperType.Nitro,
WrapperType.PlayJAudioFile,
// WrapperType.N3DS,
// WrapperType.Nitro,
// WrapperType.PlayJAudioFile, // TODO: Create wrapper to reenable test
WrapperType.RealArcadeInstaller,
WrapperType.RealArcadeMezzanine,
WrapperType.SFFS,
@@ -63,7 +63,7 @@ namespace BinaryObjectScanner.Test
WrapperType.BZip2,
WrapperType.CFB,
//WrapperType.CIA,
//WrapperType.Executable,
//WrapperType.Executable, // TODO: This needs to be split internally
WrapperType.GCF,
WrapperType.GZip,
WrapperType.InstallShieldArchiveV3,
@@ -79,7 +79,7 @@ namespace BinaryObjectScanner.Test
WrapperType.PAK,
WrapperType.PFF,
WrapperType.PKZIP,
//WrapperType.PlayJAudioFile,
//WrapperType.PlayJAudioFile, // TODO: Create wrapper to reenable test
//WrapperType.Quantum,
WrapperType.RAR,
WrapperType.SevenZip,

View File

@@ -6,25 +6,29 @@ namespace BinaryObjectScanner.Test.FileType
{
public class AACSMediaKeyBlockTests
{
private static readonly SabreTools.Serialization.Wrappers.AACSMediaKeyBlock wrapper
= new(new SabreTools.Models.AACS.MediaKeyBlock(), new MemoryStream());
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new AACSMediaKeyBlock();
var detectable = new AACSMediaKeyBlock(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_Null()
public void DetectStream_EmptyStream_Empty()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new AACSMediaKeyBlock();
var detectable = new AACSMediaKeyBlock(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Null(actual);
Assert.NotNull(actual);
Assert.Empty(actual);
}
}
}

View File

@@ -6,25 +6,29 @@ namespace BinaryObjectScanner.Test.FileType
{
public class BDPlusSVMTests
{
private static readonly SabreTools.Serialization.Wrappers.BDPlusSVM wrapper
= new(new SabreTools.Models.BDPlus.SVM(), new MemoryStream());
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new BDPlusSVM();
var detectable = new BDPlusSVM(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_Null()
public void DetectStream_EmptyStream_Empty()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new BDPlusSVM();
var detectable = new BDPlusSVM(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Null(actual);
Assert.NotNull(actual);
Assert.Empty(actual);
}
}
}

View File

@@ -6,25 +6,29 @@ namespace BinaryObjectScanner.Test.FileType
{
public class LinearExecutableTests
{
private static readonly SabreTools.Serialization.Wrappers.LinearExecutable wrapper
= new(new SabreTools.Models.LinearExecutable.Executable(), new MemoryStream());
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new LinearExecutable();
var detectable = new LinearExecutable(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_Null()
public void DetectStream_EmptyStream_Empty()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new LinearExecutable();
var detectable = new LinearExecutable(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Null(actual);
Assert.NotNull(actual);
Assert.Empty(actual);
}
}
}

View File

@@ -6,25 +6,29 @@ namespace BinaryObjectScanner.Test.FileType
{
public class MSDOSTests
{
private static readonly SabreTools.Serialization.Wrappers.MSDOS wrapper
= new(new SabreTools.Models.MSDOS.Executable(), new MemoryStream());
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new MSDOS();
var detectable = new MSDOS(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_Null()
public void DetectStream_EmptyStream_Empty()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new MSDOS();
var detectable = new MSDOS(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Null(actual);
Assert.NotNull(actual);
Assert.Empty(actual);
}
}
}

View File

@@ -6,25 +6,29 @@ namespace BinaryObjectScanner.Test.FileType
{
public class NewExecutableTests
{
private static readonly SabreTools.Serialization.Wrappers.NewExecutable wrapper
= new(new SabreTools.Models.NewExecutable.Executable(), new MemoryStream());
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new NewExecutable();
var detectable = new NewExecutable(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_Null()
public void DetectStream_EmptyStream_Empty()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new NewExecutable();
var detectable = new NewExecutable(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Null(actual);
Assert.NotNull(actual);
Assert.Empty(actual);
}
}
}

View File

@@ -6,25 +6,29 @@ namespace BinaryObjectScanner.Test.FileType
{
public class PLJTests
{
private static readonly SabreTools.Serialization.Wrappers.PlayJAudioFile wrapper
= new(new SabreTools.Models.PlayJ.AudioFile(), new MemoryStream());
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new PLJ();
var detectable = new PLJ(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_Null()
public void DetectStream_EmptyStream_Empty()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new PLJ();
var detectable = new PLJ(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Null(actual);
Assert.NotNull(actual);
Assert.Empty(actual);
}
}
}

View File

@@ -6,25 +6,29 @@ namespace BinaryObjectScanner.Test.FileType
{
public class PortableExecutableTests
{
private static readonly SabreTools.Serialization.Wrappers.PortableExecutable wrapper
= new(new SabreTools.Models.PortableExecutable.Executable(), new MemoryStream());
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new PortableExecutable();
var detectable = new PortableExecutable(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_Null()
public void DetectStream_EmptyStream_Empty()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new PortableExecutable();
var detectable = new PortableExecutable(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Null(actual);
Assert.NotNull(actual);
Assert.Empty(actual);
}
}
}

View File

@@ -19,30 +19,23 @@ namespace BinaryObjectScanner
// Use the wrapper before the type
switch (wrapper)
{
case AACSMediaKeyBlock: return new FileType.AACSMediaKeyBlock();
case BDPlusSVM: return new FileType.BDPlusSVM();
// case CIA => new FileType.CIA(),
case LinearExecutable: return new FileType.LinearExecutable();
case MSDOS: return new FileType.MSDOS();
// case N3DS: return new FileType.N3DS();
case NewExecutable: return new FileType.NewExecutable();
case PlayJAudioFile: return new FileType.PLJ();
case PortableExecutable: return new FileType.PortableExecutable();
case AACSMediaKeyBlock obj: return new FileType.AACSMediaKeyBlock(obj);
case BDPlusSVM obj: return new FileType.BDPlusSVM(obj);
// case CIA obj => new FileType.CIA(obj),
case LinearExecutable obj: return new FileType.LinearExecutable(obj);
case MSDOS obj: return new FileType.MSDOS(obj);
// case N3DS obj: return new FileType.N3DS(obj);
case NewExecutable obj: return new FileType.NewExecutable(obj);
case PlayJAudioFile obj: return new FileType.PLJ(obj);
case PortableExecutable obj: return new FileType.PortableExecutable(obj);
}
// Fall back on the file type for types not implemented in Serialization
return fileType switch
{
WrapperType.AACSMediaKeyBlock => new FileType.AACSMediaKeyBlock(),
WrapperType.BDPlusSVM => new FileType.BDPlusSVM(),
// WrapperType.CIA => new FileType.CIA(),
WrapperType.LDSCRYPT => new FileType.LDSCRYPT(),
// WrapperType.LinearExecutable => new FileType.LinearExecutable(),
// WrapperType.MSDOS => new FileType.MSDOS(),
// WrapperType.N3DS => new FileType.N3DS(),
// WrapperType.NewExecutable => new FileType.NewExecutable(),
WrapperType.PlayJAudioFile => new FileType.PLJ(),
// WrapperType.PortableExecutable => new FileType.PortableExecutable(),
WrapperType.RealArcadeInstaller => new FileType.RealArcadeInstaller(),
WrapperType.RealArcadeMezzanine => new FileType.RealArcadeMezzanine(),
WrapperType.SFFS => new FileType.SFFS(),
@@ -74,22 +67,22 @@ namespace BinaryObjectScanner
case GZip: return new FileType.GZip();
case InstallShieldArchiveV3: return new FileType.InstallShieldArchiveV3();
case InstallShieldCabinet: return new FileType.InstallShieldCAB();
case LinearExecutable: return new FileType.LinearExecutable();
case LinearExecutable obj: return new FileType.LinearExecutable(obj);
case LZKWAJ: return new FileType.LZKWAJ();
case LZQBasic: return new FileType.LZQBasic();
case LZSZDD: return new FileType.LZSZDD();
case MicrosoftCabinet: return new FileType.MicrosoftCAB();
case MoPaQ: return new FileType.MPQ();
case MSDOS: return new FileType.MSDOS();
case MSDOS obj: return new FileType.MSDOS(obj);
// case N3DS: return new FileType.N3DS();
// case NCF: return new FileType.NCF();
case NewExecutable: return new FileType.NewExecutable();
case NewExecutable obj: return new FileType.NewExecutable(obj);
// case Nitro: return new FileType.Nitro();
case PAK: return new FileType.PAK();
case PFF: return new FileType.PFF();
case PKZIP: return new FileType.PKZIP();
// case PlayJAudioFile: return new FileType.PLJ();
case PortableExecutable: return new FileType.PortableExecutable();
case PortableExecutable obj: return new FileType.PortableExecutable(obj);
case Quantum: return new FileType.Quantum();
case RAR: return new FileType.RAR();
case SevenZip: return new FileType.SevenZip();

View File

@@ -8,16 +8,14 @@ namespace BinaryObjectScanner.FileType
/// </summary>
public class AACSMediaKeyBlock : DetectableBase<SabreTools.Serialization.Wrappers.AACSMediaKeyBlock>
{
/// <inheritdoc/>
public AACSMediaKeyBlock(SabreTools.Serialization.Wrappers.AACSMediaKeyBlock? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public override string? Detect(Stream stream, string file, bool includeDebug)
{
// Create the wrapper
var mkb = SabreTools.Serialization.Wrappers.AACSMediaKeyBlock.Create(stream);
if (mkb == null)
return null;
// Derive the version, if possible
var typeAndVersion = Array.Find(mkb.Records ?? [], r => r?.RecordType == SabreTools.Models.AACS.RecordType.TypeAndVersion);
var typeAndVersion = Array.Find(_wrapper.Records ?? [], r => r?.RecordType == SabreTools.Models.AACS.RecordType.TypeAndVersion);
if (typeAndVersion == null)
return "AACS (Unknown Version)";
else

View File

@@ -1,33 +1,17 @@
using System.IO;
using BinaryObjectScanner.Interfaces;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// BD+ SVM
/// </summary>
public class BDPlusSVM : IDetectable<SabreTools.Serialization.Wrappers.BDPlusSVM>
public class BDPlusSVM : DetectableBase<SabreTools.Serialization.Wrappers.BDPlusSVM>
{
/// <inheritdoc/>
public string? Detect(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Detect(fs, file, includeDebug);
}
public BDPlusSVM(SabreTools.Serialization.Wrappers.BDPlusSVM? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public string? Detect(Stream stream, string file, bool includeDebug)
{
// Create the wrapper
var svm = SabreTools.Serialization.Wrappers.BDPlusSVM.Create(stream);
if (svm == null)
return null;
// Return the formatted value
return $"BD+ {svm.Date}";
}
public override string? Detect(Stream stream, string file, bool includeDebug)
=> $"BD+ {_wrapper.Date}";
}
}

View File

@@ -1,3 +1,4 @@
using System;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Interfaces;
@@ -8,6 +9,25 @@ namespace BinaryObjectScanner.FileType
/// </summary>
public abstract class DetectableBase<T> : DetectableBase, IDetectable<T> where T : IWrapper
{
#region Protected Instance Variables
/// <summary>
/// Wrapper representing the detectable
/// </summary>
protected T _wrapper { get; private set; }
#endregion
#region Constructors
public DetectableBase(T? wrapper)
{
if (wrapper == null)
throw new ArgumentNullException(nameof(wrapper));
_wrapper = wrapper;
}
#endregion
}
}

View File

@@ -11,21 +11,11 @@ namespace BinaryObjectScanner.FileType
/// <summary>
/// Executable or library
/// </summary>
public abstract class Executable<T> : IDetectable, IExtractable
public abstract class Executable<T> : DetectableBase<T>, IExtractable
where T : WrapperBase
{
/// <inheritdoc/>
public string? Detect(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Detect(fs, file, includeDebug);
}
/// <inheritdoc/>
public abstract string? Detect(Stream stream, string file, bool includeDebug);
public Executable(T? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public bool Extract(string file, string outDir, bool includeDebug)

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Data;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.FileType
{
@@ -11,26 +9,15 @@ namespace BinaryObjectScanner.FileType
/// </summary>
public class LinearExecutable : Executable<SabreTools.Serialization.Wrappers.LinearExecutable>
{
/// <inheritdoc/>
public LinearExecutable(SabreTools.Serialization.Wrappers.LinearExecutable? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public override string? Detect(Stream stream, string file, bool includeDebug)
{
// Create the output dictionary
var protections = new ProtectionDictionary();
// Try to create a wrapper for the proper executable type
SabreTools.Serialization.Interfaces.IWrapper? wrapper;
try
{
wrapper = WrapperFactory.CreateExecutableWrapper(stream);
if (wrapper == null)
return null;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return null;
}
// Only use generic content checks if we're in debug mode
if (includeDebug)
{
@@ -38,13 +25,9 @@ namespace BinaryObjectScanner.FileType
protections.Append(file, contentProtections.Values);
}
// Only handle LE/LX
if (wrapper is not SabreTools.Serialization.Wrappers.LinearExecutable lex)
return null;
// Standard checks
var subProtections
= RunExecutableChecks(file, lex, StaticChecks.LinearExecutableCheckClasses, includeDebug);
= RunExecutableChecks(file, _wrapper, StaticChecks.LinearExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// If there are no protections

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Data;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.FileType
{
@@ -11,26 +9,15 @@ namespace BinaryObjectScanner.FileType
/// </summary>
public class MSDOS : Executable<SabreTools.Serialization.Wrappers.MSDOS>
{
/// <inheritdoc/>
public MSDOS(SabreTools.Serialization.Wrappers.MSDOS? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public override string? Detect(Stream stream, string file, bool includeDebug)
{
// Create the output dictionary
var protections = new ProtectionDictionary();
// Try to create a wrapper for the proper executable type
SabreTools.Serialization.Interfaces.IWrapper? wrapper;
try
{
wrapper = WrapperFactory.CreateExecutableWrapper(stream);
if (wrapper == null)
return null;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return null;
}
// Only use generic content checks if we're in debug mode
if (includeDebug)
{
@@ -38,13 +25,9 @@ namespace BinaryObjectScanner.FileType
protections.Append(file, contentProtections.Values);
}
// Only handle MS-DOS
if (wrapper is not SabreTools.Serialization.Wrappers.MSDOS mz)
return null;
// Standard checks
var subProtections
= RunExecutableChecks(file, mz, StaticChecks.MSDOSExecutableCheckClasses, includeDebug);
= RunExecutableChecks(file, _wrapper, StaticChecks.MSDOSExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// If there are no protections

View File

@@ -11,26 +11,15 @@ namespace BinaryObjectScanner.FileType
/// </summary>
public class NewExecutable : Executable<SabreTools.Serialization.Wrappers.NewExecutable>
{
/// <inheritdoc/>
public NewExecutable(SabreTools.Serialization.Wrappers.NewExecutable? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public override string? Detect(Stream stream, string file, bool includeDebug)
{
// Create the output dictionary
var protections = new ProtectionDictionary();
// Try to create a wrapper for the proper executable type
SabreTools.Serialization.Interfaces.IWrapper? wrapper;
try
{
wrapper = WrapperFactory.CreateExecutableWrapper(stream);
if (wrapper == null)
return null;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return null;
}
// Only use generic content checks if we're in debug mode
if (includeDebug)
{
@@ -38,13 +27,9 @@ namespace BinaryObjectScanner.FileType
protections.Append(file, contentProtections.Values);
}
// Only handle NE
if (wrapper is not SabreTools.Serialization.Wrappers.NewExecutable nex)
return null;
// Standard checks
var subProtections
= RunExecutableChecks(file, nex, StaticChecks.NewExecutableCheckClasses, includeDebug);
= RunExecutableChecks(file, _wrapper, StaticChecks.NewExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// If there are no protections

View File

@@ -1,6 +1,4 @@
using System;
using System.IO;
using SabreTools.Matching;
using System.IO;
namespace BinaryObjectScanner.FileType
{
@@ -9,23 +7,11 @@ namespace BinaryObjectScanner.FileType
/// </summary>
public class PLJ : DetectableBase<SabreTools.Serialization.Wrappers.PlayJAudioFile>
{
/// <inheritdoc/>
public PLJ(SabreTools.Serialization.Wrappers.PlayJAudioFile? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public override string? Detect(Stream stream, string file, bool includeDebug)
{
try
{
byte[] magic = new byte[16];
int read = stream.Read(magic, 0, 16);
if (magic.StartsWith(new byte?[] { 0xFF, 0x9D, 0x53, 0x4B }))
return "PlayJ Audio File";
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
return null;
}
=> "PlayJ Audio File";
}
}

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Data;
@@ -11,26 +10,15 @@ namespace BinaryObjectScanner.FileType
/// </summary>
public class PortableExecutable : Executable<SabreTools.Serialization.Wrappers.PortableExecutable>
{
/// <inheritdoc/>
public PortableExecutable(SabreTools.Serialization.Wrappers.PortableExecutable? wrapper) : base(wrapper) { }
/// <inheritdoc/>
public override string? Detect(Stream stream, string file, bool includeDebug)
{
// Create the output dictionary
var protections = new ProtectionDictionary();
// Try to create a wrapper for the proper executable type
SabreTools.Serialization.Interfaces.IWrapper? wrapper;
try
{
wrapper = WrapperFactory.CreateExecutableWrapper(stream);
if (wrapper == null)
return null;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return null;
}
// Only use generic content checks if we're in debug mode
if (includeDebug)
{
@@ -38,13 +26,9 @@ namespace BinaryObjectScanner.FileType
protections.Append(file, contentProtections.Values);
}
// Only handle PE
if (wrapper is not SabreTools.Serialization.Wrappers.PortableExecutable pex)
return null;
// Standard checks
var subProtections
= RunExecutableChecks(file, pex, StaticChecks.PortableExecutableCheckClasses, includeDebug);
= RunExecutableChecks(file, _wrapper, StaticChecks.PortableExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// If there are no protections