The update

This commit is contained in:
Matt Nadareski
2025-09-02 20:18:01 -04:00
parent 55e368e23a
commit 77ad6e791a
107 changed files with 365 additions and 3551 deletions

View File

@@ -17,9 +17,9 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.8.7" />
<PackageReference Include="SabreTools.Serialization" Version="1.9.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@@ -65,7 +65,7 @@ namespace BinaryObjectScanner.Test
//WrapperType.CIA,
//WrapperType.Executable,
WrapperType.GCF,
WrapperType.GZIP,
WrapperType.GZip,
WrapperType.InstallShieldArchiveV3,
WrapperType.InstallShieldCAB,
WrapperType.LZKWAJ,

View File

@@ -11,7 +11,7 @@ namespace BinaryObjectScanner.Test.FileType
{
string file = string.Empty;
string outDir = string.Empty;
var extractable = new GZIP();
var extractable = new GZip();
bool actual = extractable.Extract(file, outDir, includeDebug: false);
Assert.False(actual);
@@ -23,7 +23,7 @@ namespace BinaryObjectScanner.Test.FileType
Stream? stream = null;
string file = string.Empty;
string outDir = string.Empty;
var extractable = new GZIP();
var extractable = new GZip();
bool actual = extractable.Extract(stream, file, outDir, includeDebug: false);
Assert.False(actual);
@@ -35,7 +35,7 @@ namespace BinaryObjectScanner.Test.FileType
Stream? stream = new MemoryStream();
string file = string.Empty;
string outDir = string.Empty;
var extractable = new GZIP();
var extractable = new GZip();
bool actual = extractable.Extract(stream, file, outDir, includeDebug: false);
Assert.False(actual);

View File

@@ -17,17 +17,6 @@ namespace BinaryObjectScanner.Test.FileType
Assert.False(actual);
}
[Fact]
public void ExtractFile_LookForHeader_EmptyString_False()
{
string file = string.Empty;
string outDir = string.Empty;
var extractable = new PKZIP();
bool actual = extractable.Extract(file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_Null_False()
{
@@ -40,18 +29,6 @@ namespace BinaryObjectScanner.Test.FileType
Assert.False(actual);
}
[Fact]
public void ExtractStream_LookForHeader_Null_False()
{
Stream? stream = null;
string file = string.Empty;
string outDir = string.Empty;
var extractable = new PKZIP();
bool actual = extractable.Extract(stream, file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_Empty_False()
{
@@ -63,17 +40,5 @@ namespace BinaryObjectScanner.Test.FileType
bool actual = extractable.Extract(stream, file, outDir, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_LookForHeader_Empty_False()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
string outDir = string.Empty;
var extractable = new PKZIP();
bool actual = extractable.Extract(stream, file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -40,29 +40,5 @@ namespace BinaryObjectScanner.Test.FileType
bool actual = extractable.Extract(stream, file, outDir, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractAll_EmptyModel_False()
{
var model = new SabreTools.Models.Quantum.Archive();
var data = new MemoryStream();
var item = new SabreTools.Serialization.Wrappers.Quantum(model, data);
string outputDirectory = string.Empty;
bool actual = Quantum.ExtractAll(item, outputDirectory);
Assert.False(actual);
}
[Fact]
public void ExtractFile_EmptyModel_False()
{
var model = new SabreTools.Models.Quantum.Archive();
var data = new MemoryStream();
var item = new SabreTools.Serialization.Wrappers.Quantum(model, data);
string outputDirectory = string.Empty;
bool actual = Quantum.ExtractFile(item, 0, outputDirectory);
Assert.False(actual);
}
}
}

View File

@@ -17,17 +17,6 @@ namespace BinaryObjectScanner.Test.FileType
Assert.False(actual);
}
[Fact]
public void ExtractFile_LookForHeader_EmptyString_False()
{
string file = string.Empty;
string outDir = string.Empty;
var extractable = new RAR();
bool actual = extractable.Extract(file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_Null_False()
{
@@ -40,18 +29,6 @@ namespace BinaryObjectScanner.Test.FileType
Assert.False(actual);
}
[Fact]
public void ExtractStream_LookForHeader_Null_False()
{
Stream? stream = null;
string file = string.Empty;
string outDir = string.Empty;
var extractable = new RAR();
bool actual = extractable.Extract(stream, file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_Empty_False()
{
@@ -63,17 +40,5 @@ namespace BinaryObjectScanner.Test.FileType
bool actual = extractable.Extract(stream, file, outDir, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_LookForHeader_Empty_False()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
string outDir = string.Empty;
var extractable = new RAR();
bool actual = extractable.Extract(stream, file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -17,17 +17,6 @@ namespace BinaryObjectScanner.Test.FileType
Assert.False(actual);
}
[Fact]
public void ExtractFile_LookForHeader_EmptyString_False()
{
string file = string.Empty;
string outDir = string.Empty;
var extractable = new SevenZip();
bool actual = extractable.Extract(file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_Null_False()
{
@@ -40,18 +29,6 @@ namespace BinaryObjectScanner.Test.FileType
Assert.False(actual);
}
[Fact]
public void ExtractStream_LookForHeader_Null_False()
{
Stream? stream = null;
string file = string.Empty;
string outDir = string.Empty;
var extractable = new SevenZip();
bool actual = extractable.Extract(stream, file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_Empty_False()
{
@@ -63,17 +40,5 @@ namespace BinaryObjectScanner.Test.FileType
bool actual = extractable.Extract(stream, file, outDir, includeDebug: false);
Assert.False(actual);
}
[Fact]
public void ExtractStream_LookForHeader_Empty_False()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
string outDir = string.Empty;
var extractable = new SevenZip();
bool actual = extractable.Extract(stream, file, outDir, lookForHeader: true, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 ASPack();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 AdvancedInstaller();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 AutoPlayMediaStudio();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 CExe();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 DotFuscator();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 DotNetReactor();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -29,19 +29,5 @@ namespace BinaryObjectScanner.Test.Packer
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 EXEStealth();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -1,36 +0,0 @@
using System.IO;
using BinaryObjectScanner.Packer;
using Xunit;
namespace BinaryObjectScanner.Test.Packer
{
public class EmbeddedArchiveTests
{
[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 EmbeddedArchive();
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 EmbeddedArchive();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -1,36 +0,0 @@
using System.IO;
using BinaryObjectScanner.Packer;
using Xunit;
namespace BinaryObjectScanner.Test.Packer
{
public class EmbeddedExecutableTests
{
[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 EmbeddedExecutable();
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 EmbeddedExecutable();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -0,0 +1,22 @@
using System.IO;
using BinaryObjectScanner.Packer;
using Xunit;
namespace BinaryObjectScanner.Test.Packer
{
public class EmbeddedFileTests
{
[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 EmbeddedFile();
string? actual = checker.CheckExecutable(file, pex, includeDebug: false);
Assert.Null(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 GenteeInstaller();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 HyperTechCrackProof();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -31,33 +31,5 @@ namespace BinaryObjectScanner.Test.Packer
string? actual = checker.CheckExecutable(file, pex, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void ExtractNewExecutableTest()
{
string file = "filename";
SabreTools.Models.NewExecutable.Executable model = new();
Stream source = new MemoryStream();
SabreTools.Serialization.Wrappers.NewExecutable nex = new(model, source);
string outputDir = string.Empty;
var checker = new InnoSetup();
bool actual = checker.Extract(file, nex, outputDir, includeDebug: false);
Assert.False(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 InnoSetup();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 InstallAnywhere();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 InstallerVISE();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 IntelInstallationFramework();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 MicrosoftCABSFX();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 NSIS();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 NeoLite();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 PECompact();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 PEtite();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 SetupFactory();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 SevenZipSFX();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 Shrinker();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 UPX();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Packer
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 WinRARSFX();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -31,33 +31,5 @@ namespace BinaryObjectScanner.Test.Packer
string? actual = checker.CheckExecutable(file, pex, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void ExtractNewExecutableTest()
{
string file = "filename";
SabreTools.Models.NewExecutable.Executable model = new();
Stream source = new MemoryStream();
SabreTools.Serialization.Wrappers.NewExecutable nex = new(model, source);
string outputDir = string.Empty;
var checker = new WinZipSFX();
bool actual = checker.Extract(file, nex, outputDir, includeDebug: false);
Assert.False(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 WinZipSFX();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -31,33 +31,5 @@ namespace BinaryObjectScanner.Test.Packer
string? actual = checker.CheckExecutable(file, pex, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void ExtractNewExecutableTest()
{
string file = "filename";
SabreTools.Models.NewExecutable.Executable model = new();
Stream source = new MemoryStream();
SabreTools.Serialization.Wrappers.NewExecutable nex = new(model, source);
string outputDir = string.Empty;
var checker = new WiseInstaller();
bool actual = checker.Extract(file, nex, outputDir, includeDebug: false);
Assert.False(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 WiseInstaller();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -18,19 +18,5 @@ namespace BinaryObjectScanner.Test.Protection
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 Armadillo();
bool actual = checker.Extract(file, pex, outputDir, includeDebug: false);
Assert.False(actual);
}
}
}

View File

@@ -28,56 +28,18 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<!-- Set build flags for Windows specifically -->
<PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
<DefineConstants>$(DefineConstants);WINX86</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x64'">
<DefineConstants>$(DefineConstants);WINX64</DefineConstants>
</PropertyGroup>
<!-- Exclude certain parts of external modules for by default -->
<PropertyGroup>
<DefaultItemExcludes>
$(DefaultItemExcludes);
**\AssemblyInfo.cs;
_EXTERNAL\stormlibsharp\lib\**;
_EXTERNAL\stormlibsharp\src\TestConsole\**
</DefaultItemExcludes>
</PropertyGroup>
<!-- Exclude all external modules for .NET Framework 2.0, .NET Framework 3.5, or non-Windows
builds -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR !$(RuntimeIdentifier.StartsWith(`win-x86`))">
<DefaultItemExcludes>
$(DefaultItemExcludes);
_EXTERNAL\**
</DefaultItemExcludes>
</PropertyGroup>
<!-- These are needed for dealing with native Windows DLLs -->
<ItemGroup>
<Content Include="runtimes\**\*">
<PackagePath>%(Identity)</PackagePath>
<Pack>true</Pack>
</Content>
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="BinaryObjectScanner.Test" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.Compression" Version="0.6.3" />
<PackageReference Include="SabreTools.Hashing" Version="1.4.2" />
<PackageReference Include="SabreTools.IO" Version="1.6.3" />
<PackageReference Include="SabreTools.Matching" Version="1.5.2" />
<PackageReference Include="SabreTools.Models" Version="1.5.8" />
<PackageReference Include="SabreTools.Serialization" Version="1.8.6" />
<PackageReference Include="SharpCompress" Version="0.39.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.7" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="UnshieldSharp" Version="1.9.4" />
<PackageReference Include="WiseUnpacker" Version="1.5.6" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
<PackageReference Include="SabreTools.IO" Version="1.7.1" />
<PackageReference Include="SabreTools.Matching" Version="1.6.0" />
<PackageReference Include="SabreTools.Models" Version="1.7.0" />
<PackageReference Include="SabreTools.Serialization" Version="1.9.0" />
<PackageReference Include="SharpCompress" Version="0.40.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>
</Project>

View File

@@ -40,8 +40,9 @@ namespace BinaryObjectScanner
WrapperType.BZip2 => new FileType.BZip2(),
WrapperType.CFB => new FileType.CFB(),
//WrapperType.CIA => new FileType.CIA(),
WrapperType.Executable => new FileType.Executable(),
WrapperType.GCF => new FileType.GCF(),
WrapperType.GZIP => new FileType.GZIP(),
WrapperType.GZip => new FileType.GZip(),
WrapperType.InstallShieldArchiveV3 => new FileType.InstallShieldArchiveV3(),
WrapperType.InstallShieldCAB => new FileType.InstallShieldCAB(),
WrapperType.LZKWAJ => new FileType.LZKWAJ(),

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Extract all files
Directory.CreateDirectory(outDir);
bfpk.ExtractAll(outDir);
bfpk.Extract(outDir, includeDebug);
return true;
}

View File

@@ -30,7 +30,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
bsp.ExtractAllLumps(outDir);
bsp.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,7 +1,5 @@
using System;
using System.IO;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Compression.BZip2;
namespace BinaryObjectScanner.FileType
{
@@ -23,39 +21,16 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
if (stream == null || !stream.CanRead)
// Create the wrapper
var bzip = SabreTools.Serialization.Wrappers.BZip2.Create(stream);
if (bzip == null)
return false;
try
{
// Try opening the stream
using var bz2File = new BZip2InputStream(stream, true);
// Loop through and extract all files
Directory.CreateDirectory(outDir);
bzip.Extract(outDir, includeDebug);
// Ensure directory separators are consistent
string filename = Guid.NewGuid().ToString();
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Extract the file
using FileStream fs = File.OpenWrite(filename);
bz2File.CopyTo(fs);
fs.Flush();
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
return true;
}
}
}

View File

@@ -1,11 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Models.CFB;
using static SabreTools.Models.CFB.Constants;
namespace BinaryObjectScanner.FileType
{
@@ -27,725 +21,16 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
// Get a wrapper for the CFB
var model = Deserialize(stream);
if (model == null)
return false;
var cfb = new SabreTools.Serialization.Wrappers.CFB(model, stream);
if (cfb?.Model == null)
// Create the wrapper
var cfb = SabreTools.Serialization.Wrappers.CFB.Create(stream);
if (cfb == null)
return false;
// Loop through and extract all files
Directory.CreateDirectory(outDir);
ExtractAll(cfb, outDir, includeDebug);
cfb.Extract(outDir, includeDebug);
return true;
}
#region REMOVE WHEN SERIALIZATION UPDATED
/// <summary>
/// Extract all files from the CFB to an output directory
/// </summary>
/// <param name="outputDirectory">Output directory to write to</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>True if all files extracted, false otherwise</returns>
private static bool ExtractAll(SabreTools.Serialization.Wrappers.CFB? cfb, string outputDirectory, bool includeDebug)
{
// If we have no files
if (cfb?.Model?.DirectoryEntries == null || cfb.Model.DirectoryEntries.Length == 0)
return false;
// Loop through and extract all directory entries to the output
bool allExtracted = true;
for (int i = 0; i < cfb.Model.DirectoryEntries.Length; i++)
{
allExtracted &= ExtractEntry(cfb, i, outputDirectory, includeDebug);
}
return allExtracted;
}
/// <summary>
/// Extract a file from the CFB to an output directory by index
/// </summary>
/// <param name="index">Entry index to extract</param>
/// <param name="outputDirectory">Output directory to write to</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>True if the file extracted, false otherwise</returns>
private static bool ExtractEntry(SabreTools.Serialization.Wrappers.CFB cfb, int index, string outputDirectory, bool includeDebug)
{
// If we have no entries
if (cfb?.Model?.DirectoryEntries == null || cfb.Model.DirectoryEntries.Length == 0)
return false;
// If we have an invalid index
if (index < 0 || index >= cfb.Model.DirectoryEntries.Length)
return false;
// Get the entry information
var entry = cfb.Model.DirectoryEntries[index];
if (entry == null)
return false;
// Only try to extract stream objects
if (entry.ObjectType != ObjectType.StreamObject)
return true;
// Get the entry data
byte[]? data = GetDirectoryEntryData(cfb, entry);
if (data == null)
return false;
// If we have an invalid output directory
if (string.IsNullOrEmpty(outputDirectory))
return false;
// Ensure the output filename is trimmed
string filename = entry.Name ?? $"entry{index}";
byte[] nameBytes = Encoding.UTF8.GetBytes(filename);
if (nameBytes[0] == 0xe4 && nameBytes[1] == 0xa1 && nameBytes[2] == 0x80)
filename = Encoding.UTF8.GetString(nameBytes, 3, nameBytes.Length - 3);
foreach (char c in Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, '_');
}
// Ensure directory separators are consistent
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outputDirectory, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Try to write the data
try
{
// Open the output file for writing
using FileStream fs = File.OpenWrite(filename);
fs.Write(data);
fs.Flush();
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
return true;
}
/// <summary>
/// Read the entry data for a single directory entry, if possible
/// </summary>
/// <param name="entry">Entry to try to retrieve data for</param>
/// <returns>Byte array representing the entry data on success, null otherwise</returns>
private static byte[]? GetDirectoryEntryData(SabreTools.Serialization.Wrappers.CFB cfb, DirectoryEntry entry)
{
// If the CFB is invalid
if (cfb.Model?.Header == null)
return null;
// Only try to extract stream objects
if (entry.ObjectType != ObjectType.StreamObject)
return null;
// Determine which FAT is being used
bool miniFat = entry.StreamSize < cfb.Model.Header.MiniStreamCutoffSize;
// Get the chain data
var chain = miniFat
? GetMiniFATSectorChainData(cfb, (SectorNumber)entry.StartingSectorLocation)
: GetFATSectorChainData(cfb, (SectorNumber)entry.StartingSectorLocation);
if (chain == null)
return null;
// Return only the proper amount of data
byte[] data = new byte[entry.StreamSize];
Array.Copy(chain, 0, data, 0, (int)Math.Min(chain.Length, (long)entry.StreamSize));
return data;
}
/// <remarks>Adapted from LibMSI</remarks>
private static string? DecodeStreamName(string input)
{
if (input == null)
return null;
int count = 0;
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
int p = 0; // inputBytes[0]
byte[] output = new byte[inputBytes.Length + 1];
int q = 0; // output[0]
while (p < inputBytes.Length && inputBytes[p] != 0)
{
int ch = inputBytes[p];
if ((ch == 0xe3 && inputBytes[p + 1] >= 0xa0) || (ch == 0xe4 && inputBytes[p + 1] < 0xa0))
{
// UTF-8 encoding of 0x3800..0x47ff.
output[q++] = (byte)Mime2Utf(inputBytes[p + 2] & 0x7f);
output[q++] = (byte)Mime2Utf(inputBytes[p + 1] ^ 0xa0);
p += 3;
count += 2;
continue;
}
if (ch == 0xe4 && inputBytes[p + 1] == 0xa0)
{
// UTF-8 encoding of 0x4800..0x483f.
output[q++] = (byte)Mime2Utf(inputBytes[p + 2] & 0x7f);
p += 3;
count++;
continue;
}
output[q++] = inputBytes[p++];
if (ch >= 0xc1)
output[q++] = inputBytes[p++];
if (ch >= 0xe0)
output[q++] = inputBytes[p++];
if (ch >= 0xf0)
output[q++] = inputBytes[p++];
count++;
}
output[q] = 0;
return Encoding.UTF8.GetString(output);
}
/// <remarks>Adapted from LibMSI</remarks>
private static int Mime2Utf(int x)
{
if (x < 10)
return x + '0';
if (x < (10 + 26))
return x - 10 + 'A';
if (x < (10 + 26 + 26))
return x - 10 - 26 + 'a';
if (x == (10 + 26 + 26))
return '.';
return '_';
}
/// <inheritdoc/>
private static Binary? Deserialize(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
try
{
// Create a new binary to fill
var binary = new Binary();
#region Header
// Try to parse the file header
var fileHeader = ParseFileHeader(data);
if (fileHeader?.Signature != SignatureUInt64)
return null;
if (fileHeader.ByteOrder != 0xFFFE)
return null;
if (fileHeader.MajorVersion == 3 && fileHeader.SectorShift != 0x0009)
return null;
else if (fileHeader.MajorVersion == 4 && fileHeader.SectorShift != 0x000C)
return null;
if (fileHeader.MajorVersion == 3 && fileHeader.NumberOfDirectorySectors != 0)
return null;
if (fileHeader.MiniStreamCutoffSize != 0x00001000)
return null;
// Set the file header
binary.Header = fileHeader;
#endregion
#region DIFAT Sector Numbers
// Create a DIFAT sector table
var difatSectors = new List<SectorNumber>();
// Add the sectors from the header
if (fileHeader.DIFAT != null)
difatSectors.AddRange(fileHeader.DIFAT);
// Loop through and add the DIFAT sectors
var currentSector = (SectorNumber?)fileHeader.FirstDIFATSectorLocation;
for (int i = 0; i < fileHeader.NumberOfDIFATSectors; i++)
{
// If we have an unreadable sector
if (currentSector > SectorNumber.MAXREGSECT)
break;
// Get the new next sector information
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
if (sectorOffset < 0 || sectorOffset >= data.Length)
return null;
// Seek to the next sector
data.Seek(sectorOffset, SeekOrigin.Begin);
// Try to parse the sectors
var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift);
if (sectorNumbers == null)
return null;
// Add all but the last sector number that was parsed
for (int j = 0; j < sectorNumbers.Length - 1; j++)
{
difatSectors.Add(sectorNumbers[j]);
}
// Get the next sector from the final sector number
currentSector = sectorNumbers[sectorNumbers.Length - 1];
}
// Assign the DIFAT sectors table
binary.DIFATSectorNumbers = [.. difatSectors];
#endregion
#region FAT Sector Numbers
// Create a FAT sector table
var fatSectors = new List<SectorNumber>();
// Loop through and add the FAT sectors
for (int i = 0; i < fileHeader.NumberOfFATSectors; i++)
{
// Get the next sector from the DIFAT
currentSector = binary.DIFATSectorNumbers[i];
// If we have an unreadable sector
if (currentSector > SectorNumber.MAXREGSECT)
break;
// Get the new next sector information
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
if (sectorOffset < 0 || sectorOffset >= data.Length)
return null;
// Seek to the next sector
data.Seek(sectorOffset, SeekOrigin.Begin);
// Try to parse the sectors
var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift);
if (sectorNumbers == null)
return null;
// Add the sector shifts
fatSectors.AddRange(sectorNumbers);
}
// Assign the FAT sectors table
binary.FATSectorNumbers = [.. fatSectors];
#endregion
#region Mini FAT Sector Numbers
// Create a mini FAT sector table
var miniFatSectors = new List<SectorNumber>();
// Loop through and add the mini FAT sectors
currentSector = (SectorNumber)fileHeader.FirstMiniFATSectorLocation;
for (int i = 0; i < fileHeader.NumberOfMiniFATSectors; i++)
{
// If we have an unreadable sector
if (currentSector > SectorNumber.MAXREGSECT)
break;
// Get the new next sector information
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
if (sectorOffset < 0 || sectorOffset >= data.Length)
return null;
// Seek to the next sector
data.Seek(sectorOffset, SeekOrigin.Begin);
// Try to parse the sectors
var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift);
if (sectorNumbers == null)
return null;
// Add the sector shifts
miniFatSectors.AddRange(sectorNumbers);
// Get the next sector from the FAT
currentSector = binary.FATSectorNumbers[(int)currentSector];
}
// Assign the mini FAT sectors table
binary.MiniFATSectorNumbers = [.. miniFatSectors];
#endregion
#region Directory Entries
// Create a directory sector table
var directorySectors = new List<DirectoryEntry>();
// Get the number of directory sectors
uint directorySectorCount = 0;
switch (fileHeader.MajorVersion)
{
case 3:
directorySectorCount = int.MaxValue;
break;
case 4:
directorySectorCount = fileHeader.NumberOfDirectorySectors;
break;
}
// Loop through and add the directory sectors
currentSector = (SectorNumber)fileHeader.FirstDirectorySectorLocation;
for (int i = 0; i < directorySectorCount; i++)
{
// If we have an end of chain
if (currentSector == SectorNumber.ENDOFCHAIN)
break;
// If we have an unusable sector for a version 3 file
if (directorySectorCount == int.MaxValue && currentSector > SectorNumber.MAXREGSECT)
break;
// Get the new next sector information
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
if (sectorOffset < 0 || sectorOffset >= data.Length)
return null;
// Seek to the next sector
data.Seek(sectorOffset, SeekOrigin.Begin);
// Try to parse the sectors
var directoryEntries = ParseDirectoryEntries(data, fileHeader.SectorShift, fileHeader.MajorVersion);
if (directoryEntries == null)
return null;
// Add the sector shifts
directorySectors.AddRange(directoryEntries);
// Get the next sector from the FAT
currentSector = binary.FATSectorNumbers[(int)currentSector];
}
// Assign the Directory sectors table
binary.DirectoryEntries = [.. directorySectors];
#endregion
return binary;
}
catch
{
// Ignore the actual error
return null;
}
}
/// <summary>
/// Parse a Stream into a DirectoryEntry
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled DirectoryEntry on success, null on error</returns>
private static DirectoryEntry ParseDirectoryEntry(Stream data)
{
var obj = new DirectoryEntry();
byte[] name = data.ReadBytes(64);
obj.Name = DecodeStreamName(Encoding.Unicode.GetString(name))?.TrimEnd('\0');
obj.NameLength = data.ReadUInt16LittleEndian();
obj.ObjectType = (ObjectType)data.ReadByteValue();
obj.ColorFlag = (ColorFlag)data.ReadByteValue();
obj.LeftSiblingID = (StreamID)data.ReadUInt32LittleEndian();
obj.RightSiblingID = (StreamID)data.ReadUInt32LittleEndian();
obj.ChildID = (StreamID)data.ReadUInt32LittleEndian();
obj.CLSID = data.ReadGuid();
obj.StateBits = data.ReadUInt32LittleEndian();
obj.CreationTime = data.ReadUInt64LittleEndian();
obj.ModifiedTime = data.ReadUInt64LittleEndian();
obj.StartingSectorLocation = data.ReadUInt32LittleEndian();
obj.StreamSize = data.ReadUInt64LittleEndian();
return obj;
}
/// <summary>
/// Parse a Stream into a FileHeader
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled FileHeader on success, null on error</returns>
private static FileHeader ParseFileHeader(Stream data)
{
var obj = new FileHeader();
obj.Signature = data.ReadUInt64LittleEndian();
obj.CLSID = data.ReadGuid();
obj.MinorVersion = data.ReadUInt16LittleEndian();
obj.MajorVersion = data.ReadUInt16LittleEndian();
obj.ByteOrder = data.ReadUInt16LittleEndian();
obj.SectorShift = data.ReadUInt16LittleEndian();
obj.MiniSectorShift = data.ReadUInt16LittleEndian();
obj.Reserved = data.ReadBytes(6);
obj.NumberOfDirectorySectors = data.ReadUInt32LittleEndian();
obj.NumberOfFATSectors = data.ReadUInt32LittleEndian();
obj.FirstDirectorySectorLocation = data.ReadUInt32LittleEndian();
obj.TransactionSignatureNumber = data.ReadUInt32LittleEndian();
obj.MiniStreamCutoffSize = data.ReadUInt32LittleEndian();
obj.FirstMiniFATSectorLocation = data.ReadUInt32LittleEndian();
obj.NumberOfMiniFATSectors = data.ReadUInt32LittleEndian();
obj.FirstDIFATSectorLocation = data.ReadUInt32LittleEndian();
obj.NumberOfDIFATSectors = data.ReadUInt32LittleEndian();
obj.DIFAT = new SectorNumber[109];
for (int i = 0; i < 109; i++)
{
obj.DIFAT[i] = (SectorNumber)data.ReadUInt32LittleEndian();
}
// Skip rest of sector for version 4
if (obj.MajorVersion == 4)
_ = data.ReadBytes(3584);
return obj;
}
/// <summary>
/// Parse a Stream into a sector full of sector numbers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="sectorShift">Sector shift from the header</param>
/// <returns>Filled sector full of sector numbers on success, null on error</returns>
private static SectorNumber[] ParseSectorNumbers(Stream data, ushort sectorShift)
{
int sectorCount = (int)(Math.Pow(2, sectorShift) / sizeof(uint));
var sectorNumbers = new SectorNumber[sectorCount];
for (int i = 0; i < sectorNumbers.Length; i++)
{
sectorNumbers[i] = (SectorNumber)data.ReadUInt32LittleEndian();
}
return sectorNumbers;
}
/// <summary>
/// Parse a Stream into a sector full of directory entries
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="sectorShift">Sector shift from the header</param>
/// <param name="majorVersion">Major version from the header</param>
/// <returns>Filled sector full of directory entries on success, null on error</returns>
private static DirectoryEntry[]? ParseDirectoryEntries(Stream data, ushort sectorShift, ushort majorVersion)
{
// <see href="https://winprotocoldoc.z19.web.core.windows.net/MS-CFB/%5bMS-CFB%5d.pdf"/>
int directoryEntrySize = 128;
int dirsPerSector = (int)(Math.Pow(2, sectorShift) / directoryEntrySize);
var directoryEntries = new DirectoryEntry[dirsPerSector];
for (int i = 0; i < directoryEntries.Length; i++)
{
var directoryEntry = ParseDirectoryEntry(data);
// Handle version 3 entries
if (majorVersion == 3)
directoryEntry.StreamSize &= 0x00000000FFFFFFFF;
directoryEntries[i] = directoryEntry;
}
return directoryEntries;
}
/// <summary>
/// Get the ordered FAT sector chain for a given starting sector
/// </summary>
/// <param name="startingSector">Initial FAT sector</param>
/// <returns>Ordered list of sector numbers, null on error</returns>
private static List<SectorNumber>? GetFATSectorChain(SabreTools.Serialization.Wrappers.CFB cfb, SectorNumber? startingSector)
{
// If we have an invalid sector
if (startingSector == null || startingSector < 0 || cfb.Model.FATSectorNumbers == null || (long)startingSector >= cfb.Model.FATSectorNumbers.Length)
return null;
// Setup the returned list
var sectors = new List<SectorNumber> { startingSector.Value };
var lastSector = startingSector;
while (true)
{
if (lastSector == null)
break;
// Get the next sector from the lookup table
var nextSector = cfb.Model.FATSectorNumbers[(uint)lastSector!.Value];
// If we have an invalid sector
if (nextSector >= SectorNumber.MAXREGSECT)
break;
// Add the next sector to the list and replace the last sector
sectors.Add(nextSector);
lastSector = nextSector;
}
return sectors;
}
/// <summary>
/// Get the data for the FAT sector chain starting at a given starting sector
/// </summary>
/// <param name="startingSector">Initial FAT sector</param>
/// <returns>Ordered list of sector numbers, null on error</returns>
private static byte[]? GetFATSectorChainData(SabreTools.Serialization.Wrappers.CFB cfb, SectorNumber startingSector)
{
// Get the sector chain first
var sectorChain = GetFATSectorChain(cfb, startingSector);
if (sectorChain == null)
return null;
// Sequentially read the sectors
var data = new List<byte>();
for (int i = 0; i < sectorChain.Count; i++)
{
// Try to get the sector data offset
int sectorDataOffset = (int)FATSectorToFileOffset(cfb, sectorChain[i]);
if (sectorDataOffset < 0 || sectorDataOffset >= cfb.GetEndOfFile())
return null;
// Try to read the sector data
var sectorData = cfb.ReadFromDataSource(sectorDataOffset, (int)cfb.SectorSize);
if (sectorData == null)
return null;
// Add the sector data to the output
data.AddRange(sectorData);
}
return [.. data];
}
/// <summary>
/// Convert a FAT sector value to a byte offset
/// </summary>
/// <param name="sector">Sector to convert</param>
/// <returns>File offset in bytes, -1 on error</returns>
private static long FATSectorToFileOffset(SabreTools.Serialization.Wrappers.CFB cfb, SectorNumber? sector)
{
// If we have an invalid sector number
if (sector == null || sector > SectorNumber.MAXREGSECT)
return -1;
// Convert based on the sector shift value
return (long)(sector + 1) * cfb.SectorSize;
}
/// <summary>
/// Get the ordered Mini FAT sector chain for a given starting sector
/// </summary>
/// <param name="startingSector">Initial Mini FAT sector</param>
/// <returns>Ordered list of sector numbers, null on error</returns>
private static List<SectorNumber>? GetMiniFATSectorChain(SabreTools.Serialization.Wrappers.CFB cfb, SectorNumber? startingSector)
{
// If we have an invalid sector
if (startingSector == null || startingSector < 0 || cfb.Model.MiniFATSectorNumbers == null || (long)startingSector >= cfb.Model.MiniFATSectorNumbers.Length)
return null;
// Setup the returned list
var sectors = new List<SectorNumber> { startingSector.Value };
var lastSector = startingSector;
while (true)
{
if (lastSector == null)
break;
// Get the next sector from the lookup table
var nextSector = cfb.Model.MiniFATSectorNumbers[(uint)lastSector!.Value];
// If we have an invalid sector
if (nextSector >= SectorNumber.MAXREGSECT)
break;
// Add the next sector to the list and replace the last sector
sectors.Add(nextSector);
lastSector = nextSector;
}
return sectors;
}
/// <summary>
/// Get the data for the Mini FAT sector chain starting at a given starting sector
/// </summary>
/// <param name="startingSector">Initial Mini FAT sector</param>
/// <returns>Ordered list of sector numbers, null on error</returns>
private static byte[]? GetMiniFATSectorChainData(SabreTools.Serialization.Wrappers.CFB cfb, SectorNumber startingSector)
{
// Ignore invalid data
if (cfb.Model?.Header == null || cfb.Model.DirectoryEntries == null || cfb.Model.DirectoryEntries.Length == 0)
return null;
// Get the mini stream offset
uint miniStreamSectorLocation = cfb.Model.DirectoryEntries[0].StartingSectorLocation;
// Get the mini stream data
var miniStreamData = GetFATSectorChainData(cfb, (SectorNumber)miniStreamSectorLocation);
if (miniStreamData == null)
return null;
// Get the sector chain
var sectorChain = GetMiniFATSectorChain(cfb, startingSector);
if (sectorChain == null)
return null;
// Sequentially read the sectors
var data = new List<byte>();
for (int i = 0; i < sectorChain.Count; i++)
{
// Try to get the mini stream data offset
int streamDataOffset = (int)MiniFATSectorToMiniStreamOffset(cfb, sectorChain[i]);
if (streamDataOffset < 0 || streamDataOffset > miniStreamData.Length)
return null;
// Try to read the sector data
var sectorData = miniStreamData.ReadBytes(ref streamDataOffset, (int)cfb.MiniSectorSize);
if (sectorData == null)
return null;
// Add the sector data to the output
data.AddRange(sectorData);
}
return [.. data];
}
/// <summary>
/// Convert a Mini FAT sector value to a byte offset
/// </summary>
/// <param name="sector">Sector to convert</param>
/// <returns>Stream offset in bytes, -1 on error</returns>
/// <remarks>Offset is within the mini stream, not the full file</remarks>
private static long MiniFATSectorToMiniStreamOffset(SabreTools.Serialization.Wrappers.CFB cfb, SectorNumber? sector)
{
// If we have an invalid sector number
if (sector == null || sector > SectorNumber.MAXREGSECT)
return -1;
// Get the mini stream location
return (long)sector * cfb.MiniSectorSize;
}
#endregion
}
}

View File

@@ -4,7 +4,6 @@ using System.IO;
using BinaryObjectScanner.Data;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Serialization.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.FileType
@@ -12,7 +11,7 @@ namespace BinaryObjectScanner.FileType
/// <summary>
/// Executable or library
/// </summary>
public class Executable : IDetectable
public class Executable : IDetectable, IExtractable
{
/// <inheritdoc/>
public string? Detect(string file, bool includeDebug)
@@ -56,7 +55,7 @@ namespace BinaryObjectScanner.FileType
var protections = new ProtectionDictionary();
// Try to create a wrapper for the proper executable type
IWrapper? wrapper;
SabreTools.Serialization.Interfaces.IWrapper? wrapper;
try
{
wrapper = WrapperFactory.CreateExecutableWrapper(stream);
@@ -82,11 +81,6 @@ namespace BinaryObjectScanner.FileType
var subProtections
= RunExecutableChecks(file, mz, StaticChecks.MSDOSExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// Extractable checks
var extractedProtections
= HandleExtractableProtections(file, mz, subProtections.Keys, getProtections, includeDebug);
protections.Append(extractedProtections);
}
else if (wrapper is LinearExecutable lex)
{
@@ -94,11 +88,6 @@ namespace BinaryObjectScanner.FileType
var subProtections
= RunExecutableChecks(file, lex, StaticChecks.LinearExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// Extractable checks
var extractedProtections
= HandleExtractableProtections(file, lex, subProtections.Keys, getProtections, includeDebug);
protections.Append(extractedProtections);
}
else if (wrapper is NewExecutable nex)
{
@@ -106,11 +95,6 @@ namespace BinaryObjectScanner.FileType
var subProtections
= RunExecutableChecks(file, nex, StaticChecks.NewExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// Extractable checks
var extractedProtections
= HandleExtractableProtections(file, nex, subProtections.Keys, getProtections, includeDebug);
protections.Append(extractedProtections);
}
else if (wrapper is PortableExecutable pex)
{
@@ -118,16 +102,39 @@ namespace BinaryObjectScanner.FileType
var subProtections
= RunExecutableChecks(file, pex, StaticChecks.PortableExecutableCheckClasses, includeDebug);
protections.Append(file, subProtections.Values);
// Extractable checks
var extractedProtections
= HandleExtractableProtections(file, pex, subProtections.Keys, getProtections, includeDebug);
protections.Append(extractedProtections);
}
return protections;
}
/// <inheritdoc/>
public bool Extract(string file, string outDir, bool includeDebug)
{
if (!File.Exists(file))
return false;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, outDir, includeDebug);
}
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
// Create the wrapper
var exe = WrapperFactory.CreateExecutableWrapper(stream);
if (exe == null)
return false;
// Extract all files
Directory.CreateDirectory(outDir);
if (exe is PortableExecutable pex)
pex.Extract(outDir, includeDebug);
else if (exe is NewExecutable nex)
nex.Extract(outDir, includeDebug);
return true;
}
#region Check Runners
/// <summary>
@@ -211,104 +218,6 @@ namespace BinaryObjectScanner.FileType
return protections;
}
/// <summary>
/// Handle extractable protections, such as executable packers
/// </summary>
/// <param name="file">Name of the source file of the stream, for tracking</param>
/// <param name="exe">Executable to scan the contents of</param>
/// <param name="checks">Set of classes returned from Exectuable scans</param>
/// <param name="getProtections">Optional function for handling recursive protections</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Set of protections found from extraction, empty on error</returns>
private static ProtectionDictionary HandleExtractableProtections<T, U>(string file,
T exe,
ICollection<U> checks,
Func<string, ProtectionDictionary>? getProtections,
bool includeDebug)
where T : WrapperBase
where U : IExecutableCheck<T>
{
// Create the output dictionary
var protections = new ProtectionDictionary();
// If we have an invalid set of classes
if (checks == null)
return protections;
// If we have any extractable packers
var extractables = new List<IExtractableExecutable<T>>();
foreach (var check in checks)
{
if (check is not IExtractableExecutable<T> extractable)
continue;
extractables.Add(extractable);
}
extractables.IterateWithAction(extractable =>
{
var subProtections = PerformExtractableCheck(extractable!, file, exe, getProtections, includeDebug);
protections.Append(subProtections);
});
return protections;
}
/// <summary>
/// Handle files based on an IExtractableExecutable implementation
/// </summary>
/// <param name="file">Name of the source file of the stream, for tracking</param>
/// <param name="exe">Executable to scan the contents of</param>
/// <param name="impl">IExtractableExecutable class representing the file type</param>
/// <param name="getProtections">Optional function for handling recursive protections</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Set of protections in path, empty on error</returns>
private static ProtectionDictionary PerformExtractableCheck<T>(IExtractableExecutable<T> impl,
string file,
T exe,
Func<string, ProtectionDictionary>? getProtections,
bool includeDebug)
where T : WrapperBase
{
// If we have an invalid extractable somehow
if (impl == null)
return [];
// If the extractable file itself fails
try
{
// Extract and get the output path
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
bool extracted = impl.Extract(file, exe, tempPath, includeDebug);
// Collect and format all found protections
ProtectionDictionary? subProtections = null;
if (extracted && getProtections != null)
subProtections = getProtections(tempPath);
// If temp directory cleanup fails
try
{
if (Directory.Exists(tempPath))
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
// Prepare the returned protections
subProtections?.StripFromKeys(tempPath);
subProtections?.PrependToKeys(file);
return subProtections ?? [];
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return [];
}
}
#endregion
}
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
gcf.ExtractAll(outDir);
gcf.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,61 +0,0 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Compression.Deflate;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// gzip archive
/// </summary>
public class GZIP : IExtractable
{
/// <inheritdoc/>
public bool Extract(string file, string outDir, bool includeDebug)
{
if (!File.Exists(file))
return false;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, outDir, includeDebug);
}
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
if (stream == null || !stream.CanRead)
return false;
try
{
// Try opening the stream
using var gzipFile = new GZipStream(stream, CompressionMode.Decompress, true);
// Ensure directory separators are consistent
string filename = Guid.NewGuid().ToString();
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Extract the file
using FileStream fs = File.OpenWrite(filename);
gzipFile.CopyTo(fs);
fs.Flush();
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.IO;
using BinaryObjectScanner.Interfaces;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// gzip archive
/// </summary>
public class GZip : IExtractable
{
/// <inheritdoc/>
public bool Extract(string file, string outDir, bool includeDebug)
{
if (!File.Exists(file))
return false;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, outDir, includeDebug);
}
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
// Create the wrapper
var gcf = SabreTools.Serialization.Wrappers.GZip.Create(stream);
if (gcf == null)
return false;
// Loop through and extract all files
Directory.CreateDirectory(outDir);
gcf.Extract(outDir, includeDebug);
return true;
}
}
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
isv3.ExtractAll(outDir);
isv3.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,8 +1,5 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.IO;
using BinaryObjectScanner.Interfaces;
using UnshieldSharp;
namespace BinaryObjectScanner.FileType
{
@@ -24,81 +21,16 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
// Handles getting full path if relative paths were passed.
file = Path.GetFullPath(file);
// Get the name of the first cabinet file or header
var directory = Path.GetDirectoryName(file);
string noExtension = Path.GetFileNameWithoutExtension(file);
bool shouldScanCabinet;
if (directory == null)
{
string filenamePattern = noExtension;
filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty);
bool cabinetHeaderExists = File.Exists(filenamePattern + "1.hdr");
shouldScanCabinet = cabinetHeaderExists
? file.Equals(filenamePattern + "1.hdr", StringComparison.OrdinalIgnoreCase)
: file.Equals(filenamePattern + "1.cab", StringComparison.OrdinalIgnoreCase);
}
else
{
string filenamePattern = Path.Combine(directory, noExtension);
filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty);
bool cabinetHeaderExists = File.Exists(Path.Combine(directory, filenamePattern + "1.hdr"));
shouldScanCabinet = cabinetHeaderExists
? file.Equals(Path.Combine(directory, filenamePattern + "1.hdr"), StringComparison.OrdinalIgnoreCase)
: file.Equals(Path.Combine(directory, filenamePattern + "1.cab"), StringComparison.OrdinalIgnoreCase);
}
// If we have anything but the first file
if (!shouldScanCabinet)
// Create the wrapper
var iscab = SabreTools.Serialization.Wrappers.InstallShieldCabinet.Create(stream);
if (iscab == null)
return false;
try
{
if (!File.Exists(file))
return false;
// Loop through and extract all files
Directory.CreateDirectory(outDir);
iscab.Extract(outDir, includeDebug);
var cabfile = InstallShieldCabinet.Open(file);
if (cabfile?.HeaderList == null)
return false;
for (int i = 0; i < cabfile.HeaderList.FileCount; i++)
{
try
{
// Check if the file is valid first
if (!cabfile.HeaderList.FileIsValid(i))
continue;
// Ensure directory separators are consistent
string filename = cabfile.HeaderList.GetFileName(i) ?? $"BAD_FILENAME{i}";
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
cabfile.FileSave(i, filename);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
return true;
}
}
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
kwaj.Extract(outDir);
kwaj.Extract(outDir, includeDebug);
return true;
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
qbasic.Extract(outDir);
qbasic.Extract(outDir, includeDebug);
return true;
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
szdd.Extract(Path.GetFileName(file), outDir);
szdd.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,8 +1,5 @@
using System.IO;
using BinaryObjectScanner.Interfaces;
#if (NET452_OR_GREATER || NETCOREAPP) && (WINX86 || WINX64)
using StormLibSharp;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -21,73 +18,19 @@ namespace BinaryObjectScanner.FileType
return Extract(fs, file, outDir, includeDebug);
}
// TODO: Add stream opening support
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
#if NET20 || NET35 || !(WINX86 || WINX64)
// Not supported for old .NET due to feature requirements
// Not supported in non-Windows builds due to DLL requirements
return false;
#else
try
{
if (!File.Exists(file))
return false;
// Try to open the archive and listfile
var mpqArchive = new MpqArchive(file, FileAccess.Read);
string? listfile = null;
MpqFileStream listStream = mpqArchive.OpenFile("(listfile)");
// If we can't read the listfile, we just return
if (!listStream.CanRead)
return false;
// Read the listfile in for processing
using (var sr = new StreamReader(listStream))
{
listfile = sr.ReadToEnd();
}
// Split the listfile by newlines
string[] listfileLines = listfile.Replace("\r\n", "\n").Split('\n');
// Loop over each entry
foreach (string sub in listfileLines)
{
// Ensure directory separators are consistent
string filename = sub;
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Try to write the data
try
{
mpqArchive.ExtractFile(sub, filename);
}
catch (System.Exception ex)
{
if (includeDebug) System.Console.WriteLine(ex);
}
}
return true;
}
catch (System.Exception ex)
{
if (includeDebug) System.Console.WriteLine(ex);
// Create the wrapper
var mpq = SabreTools.Serialization.Wrappers.MoPaQ.Create(stream);
if (mpq == null)
return false;
}
#endif
// Loop through and extract all files
Directory.CreateDirectory(outDir);
mpq.Extract(outDir, includeDebug);
return true;
}
}
}

View File

@@ -1,16 +1,11 @@
using System;
using System.IO;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Models.MicrosoftCabinet;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// Microsoft cabinet file
/// </summary>
/// <remarks>Specification available at <see href="http://download.microsoft.com/download/5/0/1/501ED102-E53F-4CE0-AA6B-B0F93629DDC6/Exchange/%5BMS-CAB%5D.pdf"/></remarks>
/// <see href="https://github.com/wine-mirror/wine/tree/master/dlls/cabinet"/>
public class MicrosoftCAB : IExtractable
{
/// <inheritdoc/>
@@ -26,457 +21,16 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
// TODO: Remove once Serialization is updated
// Get a wrapper for the set, if possible
SabreTools.Serialization.Wrappers.MicrosoftCabinet? current;
if (File.Exists(file))
current = OpenSet(file);
else
current = SabreTools.Serialization.Wrappers.MicrosoftCabinet.Create(stream);
// Validate the header exists
if (current?.Model?.Header == null)
// Create the wrapper
var mscab = SabreTools.Serialization.Wrappers.MicrosoftCabinet.Create(stream);
if (mscab == null)
return false;
try
{
// Loop through the cabinets
do
{
ExtractCabinet(current, file, outDir, forwardOnly: true, includeDebug);
current = OpenNext(current, file);
}
while (current?.Model?.Header != null);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
// Loop through and extract all files
Directory.CreateDirectory(outDir);
mscab.Extract(outDir, includeDebug);
return true;
}
/// <summary>
/// Open a cabinet set for reading, if possible
/// </summary>
/// <param name="filename">Filename for one cabinet in the set</param>
/// <returns>Wrapper representing the set, null on error</returns>
/// TODO: Remove once Serialization is updated
private static SabreTools.Serialization.Wrappers.MicrosoftCabinet? OpenSet(string? filename)
{
// If the file is invalid
if (string.IsNullOrEmpty(filename))
return null;
else if (!File.Exists(filename!))
return null;
// Get the full file path and directory
filename = Path.GetFullPath(filename);
// Read in the current file and try to parse
var stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var current = SabreTools.Serialization.Wrappers.MicrosoftCabinet.Create(stream);
if (current?.Model?.Header == null)
return null;
// Seek to the first part of the cabinet set
while (current.Model.Header?.CabinetPrev != null)
{
// Attempt to open the previous cabinet
var prev = OpenPrevious(current, filename);
if (prev?.Model?.Header == null)
break;
// Assign previous as new current
current = prev;
}
// Return the start of the set
return current;
}
/// <summary>
/// Extract a cabinet file to an output directory, if possible
/// </summary>
/// <param name="filename">Filename for one cabinet in the set, if available</param>
/// <param name="outDir">Path to the output directory</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Indicates if all files were able to be extracted</returns>
/// TODO: Remove once Serialization is updated
private static bool ExtractCabinet(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive, string? filename, string outDir, bool forwardOnly, bool includeDebug)
{
// If the archive is invalid
if (cabArchive?.Model?.Folders == null || cabArchive.Model.Folders.Length == 0)
return false;
try
{
// Loop through the folders
for (int f = 0; f < cabArchive.Model.Folders.Length; f++)
{
var folder = cabArchive.Model.Folders[f];
ExtractFolder(cabArchive, filename, outDir, folder, f, includeDebug);
}
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
/// <summary>
/// Extract the contents of a single folder
/// </summary>
/// <param name="filename">Filename for one cabinet in the set, if available</param>
/// <param name="outDir">Path to the output directory</param>
/// <param name="folder">Folder containing the blocks to decompress</param>
/// <param name="folderIndex">Index of the folder in the cabinet</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// TODO: Remove once Serialization is updated
private static void ExtractFolder(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive,
string? filename,
string outDir,
CFFOLDER? folder,
int folderIndex,
bool includeDebug)
{
// Decompress the blocks, if possible
using var blockStream = DecompressBlocks(cabArchive, filename, folder, folderIndex, includeDebug);
if (blockStream == null || blockStream.Length == 0)
return;
// Loop through the files
var files = GetFiles(cabArchive, folderIndex);
for (int i = 0; i < files.Length; i++)
{
var file = files[i];
ExtractFile(outDir, blockStream, file, includeDebug);
}
}
/// <summary>
/// Extract the contents of a single file
/// </summary>
/// <param name="outDir">Path to the output directory</param>
/// <param name="blockStream">Stream representing the uncompressed block data</param>
/// <param name="file">File information</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// TODO: Remove once Serialization is updated
private static void ExtractFile(string outDir, Stream blockStream, CFFILE file, bool includeDebug)
{
try
{
blockStream.Seek(file.FolderStartOffset, SeekOrigin.Begin);
byte[] fileData = blockStream.ReadBytes((int)file.FileSize);
// Ensure directory separators are consistent
string fileName = file.Name!;
if (Path.DirectorySeparatorChar == '\\')
fileName = fileName.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
fileName = fileName.Replace('\\', '/');
string tempFile = Path.Combine(outDir, fileName);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
using var of = File.OpenWrite(tempFile);
of.Write(fileData, 0, fileData.Length);
of.Flush();
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
/// <summary>
/// Decompress all blocks for a folder
/// </summary>
/// <param name="filename">Filename for one cabinet in the set, if available</param>
/// <param name="folder">Folder containing the blocks to decompress</param>
/// <param name="folderIndex">Index of the folder in the cabinet</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Stream representing the decompressed data on success, null otherwise</returns>
/// TODO: Remove once Serialization is updated
private static Stream? DecompressBlocks(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive, string? filename, CFFOLDER? folder, int folderIndex, bool includeDebug)
{
// Ensure data blocks
var dataBlocks = GetDataBlocks(cabArchive, filename, folder, folderIndex);
if (dataBlocks == null || dataBlocks.Length == 0)
return null;
// Get the compression type
var compressionType = GetCompressionType(folder!);
// Setup decompressors
var mszip = SabreTools.Compression.MSZIP.Decompressor.Create();
//uint quantumWindowBits = (uint)(((ushort)folder.CompressionType >> 8) & 0x1f);
// Loop through the data blocks
var ms = new MemoryStream();
for (int i = 0; i < dataBlocks.Length; i++)
{
var db = dataBlocks[i];
if (db.CompressedData == null)
continue;
// Get the data to be processed
byte[] blockData = db.CompressedData;
// If the block is continued, append
bool continuedBlock = false;
if (db.UncompressedSize == 0)
{
var nextBlock = dataBlocks[i + 1];
byte[]? nextData = nextBlock.CompressedData;
if (nextData == null)
continue;
continuedBlock = true;
blockData = [.. blockData, .. nextData];
db.CompressedSize += nextBlock.CompressedSize;
db.UncompressedSize = nextBlock.UncompressedSize;
}
// Get the uncompressed data block
byte[] data = compressionType switch
{
CompressionType.TYPE_NONE => blockData,
CompressionType.TYPE_MSZIP => DecompressMSZIPBlock(folderIndex, mszip, i, db, blockData, includeDebug),
// TODO: Unsupported
CompressionType.TYPE_QUANTUM => [],
CompressionType.TYPE_LZX => [],
// Should be impossible
_ => [],
};
// Write the uncompressed data block
ms.Write(data, 0, data.Length);
ms.Flush();
// Increment additionally if we had a continued block
if (continuedBlock) i++;
}
return ms;
}
/// <summary>
/// Decompress an MS-ZIP block using an existing decompressor
/// </summary>
/// <param name="folderIndex">Index of the folder in the cabinet</param>
/// <param name="mszip">MS-ZIP decompressor with persistent state</param>
/// <param name="blockIndex">Index of the block within the folder</param>
/// <param name="block">Block data to be used for decompression</param>
/// <param name="blockData">Block data to be used for decompression</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Byte array representing the decompressed data, empty on error</returns>
/// TODO: Remove once Serialization is updated
private static byte[] DecompressMSZIPBlock(int folderIndex, SabreTools.Compression.MSZIP.Decompressor mszip, int blockIndex, CFDATA block, byte[] blockData, bool includeDebug)
{
try
{
// Decompress to a temporary stream
using var stream = new MemoryStream();
mszip.CopyTo(blockData, stream);
// Pad to the correct size but throw a warning about this
if (stream.Length < block.UncompressedSize)
{
if (includeDebug)
Console.Error.WriteLine($"Data block {blockIndex} in folder {folderIndex} had mismatching sizes. Expected: {block.UncompressedSize}, Got: {stream.Length}");
byte[] padding = new byte[block.UncompressedSize - stream.Length];
stream.Write(padding, 0, padding.Length);
}
// Return the byte array data
return stream.ToArray();
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return [];
}
}
/// <summary>
/// Get the unmasked compression type for a folder
/// </summary>
/// <param name="folder">Folder to get the compression type for</param>
/// <returns>Compression type on success, <see cref="ushort.MaxValue"/> on error</returns>
/// TODO: Remove once Serialization is updated
private static CompressionType GetCompressionType(CFFOLDER folder)
{
if ((folder!.CompressionType & CompressionType.MASK_TYPE) == CompressionType.TYPE_NONE)
return CompressionType.TYPE_NONE;
else if ((folder.CompressionType & CompressionType.MASK_TYPE) == CompressionType.TYPE_MSZIP)
return CompressionType.TYPE_MSZIP;
else if ((folder.CompressionType & CompressionType.MASK_TYPE) == CompressionType.TYPE_QUANTUM)
return CompressionType.TYPE_QUANTUM;
else if ((folder.CompressionType & CompressionType.MASK_TYPE) == CompressionType.TYPE_LZX)
return CompressionType.TYPE_LZX;
else
return (CompressionType)ushort.MaxValue;
}
/// <summary>
/// Get the set of data blocks for a folder
/// </summary>
/// <param name="filename">Filename for one cabinet in the set, if available</param>
/// <param name="folder">Folder containing the blocks to decompress</param>
/// <param name="folderIndex">Index of the folder in the cabinet</param>
/// <param name="skipPrev">Indicates if previous cabinets should be ignored</param>
/// <param name="skipNext">Indicates if next cabinets should be ignored</param>
/// <returns>Array of data blocks on success, null otherwise</returns>
/// TODO: Remove once Serialization is updated
private static CFDATA[]? GetDataBlocks(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive, string? filename, CFFOLDER? folder, int folderIndex, bool skipPrev = false, bool skipNext = false)
{
// Skip invalid folders
if (folder?.DataBlocks == null || folder.DataBlocks.Length == 0)
return null;
// Get all files for the folder
var files = GetFiles(cabArchive, folderIndex);
if (files.Length == 0)
return folder.DataBlocks;
// Check if the folder spans in either direction
bool spanPrev = Array.Exists(files, f => f.FolderIndex == FolderIndex.CONTINUED_FROM_PREV || f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT);
bool spanNext = Array.Exists(files, f => f.FolderIndex == FolderIndex.CONTINUED_TO_NEXT || f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT);
// If the folder spans backward and Prev is not being skipped
CFDATA[] prevBlocks = [];
if (!skipPrev && spanPrev)
{
// Get all blocks from Prev
var prev = OpenPrevious(cabArchive, filename);
if (prev?.Model?.Header != null && prev.Model.Folders != null)
{
int prevFolderIndex = prev.Model.Header.FolderCount;
var prevFolder = prev.Model.Folders[prevFolderIndex - 1];
prevBlocks = GetDataBlocks(prev, filename, prevFolder, prevFolderIndex, skipNext: true) ?? [];
}
}
// If the folder spans forward and Next is not being skipped
CFDATA[] nextBlocks = [];
if (!skipNext && spanNext)
{
// Get all blocks from Prev
var next = OpenNext(cabArchive, filename);
if (next?.Model?.Header != null && next.Model.Folders != null)
{
var nextFolder = next.Model.Folders[0];
nextBlocks = GetDataBlocks(next, filename, nextFolder, 0, skipPrev: true) ?? [];
}
}
// Return all found blocks in order
return [.. prevBlocks, .. folder.DataBlocks, .. nextBlocks];
}
/// <summary>
/// Get all files for the current folder index
/// </summary>
/// TODO: Remove once Serialization is updated
private static CFFILE[] GetFiles(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive, int folderIndex)
{
// Ignore invalid archives
if (cabArchive.Model.Files == null)
return [];
// Get all files with a name and matching index
return Array.FindAll(cabArchive.Model.Files, f =>
{
if (string.IsNullOrEmpty(f.Name))
return false;
int fileFolder = GetFolderIndex(cabArchive, f);
return fileFolder == folderIndex;
});
}
/// <summary>
/// Get the corrected folder index
/// </summary>
/// TODO: Remove once Serialization is updated
private static int GetFolderIndex(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive, CFFILE file)
{
return file.FolderIndex switch
{
FolderIndex.CONTINUED_FROM_PREV => 0,
FolderIndex.CONTINUED_TO_NEXT => (cabArchive.Model.Header?.FolderCount ?? 1) - 1,
FolderIndex.CONTINUED_PREV_AND_NEXT => 0,
_ => (int)file.FolderIndex,
};
}
/// <summary>
/// Open the next archive, if possible
/// </summary>
/// <param name="filename">Filename for one cabinet in the set</param>
/// TODO: Remove once Serialization is updated
private static SabreTools.Serialization.Wrappers.MicrosoftCabinet? OpenNext(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive, string? filename)
{
// Ignore invalid archives
if (cabArchive.Model?.Header == null || string.IsNullOrEmpty(filename))
return null;
// Normalize the filename
filename = Path.GetFullPath(filename);
// Get if the cabinet has a next part
string? next = cabArchive.Model.Header.CabinetNext;
if (string.IsNullOrEmpty(next))
return null;
// Get the full next path
string? folder = Path.GetDirectoryName(filename);
if (folder != null)
next = Path.Combine(folder, next);
// Open and return the next cabinet
var fs = File.Open(next, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return SabreTools.Serialization.Wrappers.MicrosoftCabinet.Create(fs);
}
/// <summary>
/// Open the previous archive, if possible
/// </summary>
/// <param name="filename">Filename for one cabinet in the set</param>
/// TODO: Remove once Serialization is updated
private static SabreTools.Serialization.Wrappers.MicrosoftCabinet? OpenPrevious(SabreTools.Serialization.Wrappers.MicrosoftCabinet cabArchive, string? filename)
{
// Ignore invalid archives
if (cabArchive.Model?.Header == null || string.IsNullOrEmpty(filename))
return null;
// Normalize the filename
filename = Path.GetFullPath(filename);
// Get if the cabinet has a previous part
string? prev = cabArchive.Model.Header.CabinetPrev;
if (string.IsNullOrEmpty(prev))
return null;
// Get the full next path
string? folder = Path.GetDirectoryName(filename);
if (folder != null)
prev = Path.Combine(folder, prev);
// Open and return the previous cabinet
var fs = File.Open(prev, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return SabreTools.Serialization.Wrappers.MicrosoftCabinet.Create(fs);
}
}
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
pak.ExtractAll(outDir);
pak.Extract(outDir, includeDebug);
return true;
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Extract all files
Directory.CreateDirectory(outDir);
pff.ExtractAll(outDir);
pff.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,11 +1,5 @@
using System;
using System.IO;
using System.IO;
using BinaryObjectScanner.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Readers;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -16,96 +10,27 @@ namespace BinaryObjectScanner.FileType
{
/// <inheritdoc/>
public bool Extract(string file, string outDir, bool includeDebug)
=> Extract(file, outDir, lookForHeader: false, includeDebug);
/// <inheritdoc cref="IExtractable.Extract(string, string, bool)"/>
public bool Extract(string file, string outDir, bool lookForHeader, bool includeDebug)
{
if (!File.Exists(file))
return false;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, outDir, lookForHeader, includeDebug);
return Extract(fs, file, outDir, includeDebug);
}
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
=> Extract(stream, file, outDir, lookForHeader: false, includeDebug);
/// <inheritdoc cref="IExtractable.Extract(Stream?, string, string, bool)"/>
public bool Extract(Stream? stream, string file, string outDir, bool lookForHeader, bool includeDebug)
{
if (stream == null || !stream.CanRead)
// Create the wrapper
var pkzip = SabreTools.Serialization.Wrappers.PKZIP.Create(stream);
if (pkzip == null)
return false;
#if NET462_OR_GREATER || NETCOREAPP
try
{
var readerOptions = new ReaderOptions() { LookForHeader = lookForHeader };
var zipFile = ZipArchive.Open(stream, readerOptions);
// Loop through and extract all files
Directory.CreateDirectory(outDir);
pkzip.Extract(outDir, includeDebug);
// If the file exists
if (!string.IsNullOrEmpty(file) && File.Exists(file!))
{
// Find all file parts
FileInfo[] parts = [.. ArchiveFactory.GetFileParts(new FileInfo(file))];
// If there are multiple parts
if (parts.Length > 1)
zipFile = ZipArchive.Open(parts, readerOptions);
// Try to read the file path if no entries are found
else if (zipFile.Entries.Count == 0)
zipFile = ZipArchive.Open(parts, readerOptions);
}
foreach (var entry in zipFile.Entries)
{
try
{
// If the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
// If the entry is partial due to an incomplete multi-part archive, skip it
if (!entry.IsComplete)
continue;
// Ensure directory separators are consistent
string filename = entry.Key;
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
entry.WriteToFile(filename);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
#else
return false;
#endif
return true;
}
}
}

View File

@@ -22,121 +22,13 @@ namespace BinaryObjectScanner.FileType
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
// Create the wrapper
var quantum = SabreTools.Serialization.Wrappers.Quantum.Create(stream);
if (quantum == null)
var qtm = SabreTools.Serialization.Wrappers.Quantum.Create(stream);
if (qtm == null)
return false;
// Extract all files
// Loop through and extract all files
Directory.CreateDirectory(outDir);
ExtractAll(quantum, outDir);
return true;
}
/// <summary>
/// Extract all files from the Quantum archive to an output directory
/// </summary>
/// <param name="outputDirectory">Output directory to write to</param>
/// <returns>True if all files extracted, false otherwise</returns>
public static bool ExtractAll(SabreTools.Serialization.Wrappers.Quantum item, string outputDirectory)
{
// If we have no files
if (item.Model.FileList == null || item.Model.FileList.Length == 0)
return false;
// Loop through and extract all files to the output
bool allExtracted = true;
for (int i = 0; i < item.Model.FileList.Length; i++)
{
allExtracted &= ExtractFile(item, i, outputDirectory);
}
return allExtracted;
}
/// <summary>
/// Extract a file from the Quantum archive to an output directory by index
/// </summary>
/// <param name="index">File index to extract</param>
/// <param name="outputDirectory">Output directory to write to</param>
/// <returns>True if the file extracted, false otherwise</returns>
public static bool ExtractFile(SabreTools.Serialization.Wrappers.Quantum item, int index, string outputDirectory)
{
// If we have no files
if (item.Model.Header == null || item.Model.Header.FileCount == 0 || item.Model.FileList == null || item.Model.FileList.Length == 0)
return false;
// If we have an invalid index
if (index < 0 || index >= item.Model.FileList.Length)
return false;
// Get the file information
var fileDescriptor = item.Model.FileList[index];
// Read the entire compressed data
int compressedDataOffset = (int)item.Model.CompressedDataOffset;
int compressedDataLength = item.GetEndOfFile() - compressedDataOffset;
var compressedData = item.ReadFromDataSource(compressedDataOffset, compressedDataLength);
// TODO: Figure out decompression
// - Single-file archives seem to work
// - Single-file archives with files that span a window boundary seem to work
// - The first files in each archive seem to work
return false;
// // Setup the decompression state
// State state = new State();
// Decompressor.InitState(state, TableSize, CompressionFlags);
// // Decompress the entire array
// int decompressedDataLength = (int)FileList.Sum(fd => fd.ExpandedFileSize);
// byte[] decompressedData = new byte[decompressedDataLength];
// Decompressor.Decompress(state, compressedData.Length, compressedData, decompressedData.Length, decompressedData);
// // Read the data
// int offset = (int)FileList.Take(index).Sum(fd => fd.ExpandedFileSize);
// byte[] data = new byte[fileDescriptor.ExpandedFileSize];
// Array.Copy(decompressedData, offset, data, 0, data.Length);
// // Loop through all files before the current
// for (int i = 0; i < index; i++)
// {
// // Decompress the next block of data
// byte[] tempData = new byte[FileList[i].ExpandedFileSize];
// int lastRead = Decompressor.Decompress(state, compressedData.Length, compressedData, tempData.Length, tempData);
// compressedData = new ReadOnlySpan<byte>(compressedData, (lastRead), compressedData.Length - (lastRead)).ToArray();
// }
// // Read the data
// byte[] data = new byte[fileDescriptor.ExpandedFileSize];
// _ = Decompressor.Decompress(state, compressedData.Length, compressedData, data.Length, data);
// // Create the filename
// string filename = fileDescriptor.FileName;
// // If we have an invalid output directory
// if (string.IsNullOrEmpty(outputDirectory))
// return false;
// // Create the full output path
// filename = Path.Combine(outputDirectory, filename);
// // Ensure the output directory is created
// Directory.CreateDirectory(Path.GetDirectoryName(filename));
// // Try to write the data
// try
// {
// // Open the output file for writing
// using (Stream fs = File.OpenWrite(filename))
// {
// fs.Write(data, 0, data.Length);
// }
// }
// catch
// {
// return false;
// }
qtm.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,12 +1,5 @@
using System;
using System.IO;
using System.IO;
using BinaryObjectScanner.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Archives;
using SharpCompress.Archives.Rar;
using SharpCompress.Common;
using SharpCompress.Readers;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -17,137 +10,27 @@ namespace BinaryObjectScanner.FileType
{
/// <inheritdoc/>
public bool Extract(string file, string outDir, bool includeDebug)
=> Extract(file, outDir, lookForHeader: false, includeDebug);
/// <inheritdoc cref="IExtractable.Extract(string, string, bool)"/>
public bool Extract(string file, string outDir, bool lookForHeader, bool includeDebug)
{
if (!File.Exists(file))
return false;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, outDir, lookForHeader, includeDebug);
return Extract(fs, file, outDir, includeDebug);
}
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
=> Extract(stream, file, outDir, lookForHeader: false, includeDebug);
/// <inheritdoc cref="IExtractable.Extract(Stream?, string, string, bool)"/>
public bool Extract(Stream? stream, string file, string outDir, bool lookForHeader, bool includeDebug)
{
if (stream == null || !stream.CanRead)
// Create the wrapper
var rar = SabreTools.Serialization.Wrappers.RAR.Create(stream);
if (rar == null)
return false;
#if NET462_OR_GREATER || NETCOREAPP
try
{
var readerOptions = new ReaderOptions() { LookForHeader = lookForHeader };
RarArchive rarFile = RarArchive.Open(stream, readerOptions);
// If the file exists
if (!string.IsNullOrEmpty(file) && File.Exists(file!))
{
// Find all file parts
FileInfo[] parts = [.. ArchiveFactory.GetFileParts(new FileInfo(file))];
// If there are multiple parts
if (parts.Length > 1)
rarFile = RarArchive.Open(parts, readerOptions);
// Try to read the file path if no entries are found
else if (rarFile.Entries.Count == 0)
rarFile = RarArchive.Open(parts, readerOptions);
}
if (rarFile.IsSolid)
return ExtractSolid(rarFile, outDir, includeDebug);
else
return ExtractNonSolid(rarFile, outDir, includeDebug);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
#else
return false;
#endif
}
#if NET462_OR_GREATER || NETCOREAPP
/// <summary>
/// Extraction method for non-solid archives. This iterates over each entry in the archive to extract every
/// file individually, in order to extract all valid files from the archive.
/// </summary>
private static bool ExtractNonSolid(RarArchive rarFile, string outDir, bool includeDebug)
{
foreach (var entry in rarFile.Entries)
{
try
{
// If the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
// If we have a partial entry due to an incomplete multi-part archive, skip it
if (!entry.IsComplete)
continue;
// Ensure directory separators are consistent
string filename = entry.Key;
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
entry.WriteToFile(filename);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
return true;
}
/// <summary>
/// Extraction method for solid archives. Uses ExtractAllEntries because extraction for solid archives must be
/// done sequentially, and files beyond a corrupted point in a solid archive will be unreadable anyways.
/// </summary>
private static bool ExtractSolid(RarArchive rarFile, string outDir, bool includeDebug)
{
try
{
if (!Directory.Exists(outDir))
Directory.CreateDirectory(outDir);
rarFile.WriteToDirectory(outDir, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true,
});
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
// Loop through and extract all files
Directory.CreateDirectory(outDir);
rar.Extract(outDir, includeDebug);
return true;
}
#endif
}
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
sga.ExtractAll(outDir);
sga.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,12 +1,5 @@
using System;
using System.IO;
using System.IO;
using BinaryObjectScanner.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Common;
using SharpCompress.Readers;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -17,138 +10,27 @@ namespace BinaryObjectScanner.FileType
{
/// <inheritdoc/>
public bool Extract(string file, string outDir, bool includeDebug)
=> Extract(file, outDir, lookForHeader: false, includeDebug);
/// <inheritdoc cref="IExtractable.Extract(string, string, bool)"/>
public bool Extract(string file, string outDir, bool lookForHeader, bool includeDebug)
{
if (!File.Exists(file))
return false;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, outDir, lookForHeader, includeDebug);
return Extract(fs, file, outDir, includeDebug);
}
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
=> Extract(stream, file, outDir, lookForHeader: false, includeDebug);
/// <inheritdoc cref="IExtractable.Extract(Stream?, string, string, bool)"/>
public bool Extract(Stream? stream, string file, string outDir, bool lookForHeader, bool includeDebug)
{
if (stream == null || !stream.CanRead)
// Create the wrapper
var sevenZip = SabreTools.Serialization.Wrappers.SevenZip.Create(stream);
if (sevenZip == null)
return false;
#if NET462_OR_GREATER || NETCOREAPP
try
{
var readerOptions = new ReaderOptions() { LookForHeader = lookForHeader };
var sevenZip = SevenZipArchive.Open(stream, readerOptions);
// If the file exists
if (!string.IsNullOrEmpty(file) && File.Exists(file!))
{
// Find all file parts
FileInfo[] parts = [.. ArchiveFactory.GetFileParts(new FileInfo(file))];
// If there are multiple parts
if (parts.Length > 1)
sevenZip = SevenZipArchive.Open(parts, readerOptions);
// Try to read the file path if no entries are found
else if (sevenZip.Entries.Count == 0)
sevenZip = SevenZipArchive.Open(parts, readerOptions);
}
// Currently doesn't flag solid 7z archives with only 1 solid block as solid, but practically speaking
// this is not much of a concern.
if (sevenZip.IsSolid)
return ExtractSolid(sevenZip, outDir, includeDebug);
else
return ExtractNonSolid(sevenZip, outDir, includeDebug);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
#else
return false;
#endif
}
#if NET462_OR_GREATER || NETCOREAPP
/// <summary>
/// Extraction method for non-solid archives. This iterates over each entry in the archive to extract every
/// file individually, in order to extract all valid files from the archive.
/// </summary>
private static bool ExtractNonSolid(SevenZipArchive sevenZip, string outDir, bool includeDebug)
{
foreach (var entry in sevenZip.Entries)
{
try
{
// If the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
// If we have a partial entry due to an incomplete multi-part archive, skip it
if (!entry.IsComplete)
continue;
// Ensure directory separators are consistent
string filename = entry.Key;
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
entry.WriteToFile(filename);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
return true;
}
/// <summary>
/// Extraction method for solid archives. Uses ExtractAllEntries because extraction for solid archives must be
/// done sequentially, and files beyond a corrupted point in a solid archive will be unreadable anyways.
/// </summary>
private static bool ExtractSolid(SevenZipArchive sevenZip, string outDir, bool includeDebug)
{
try
{
if (!Directory.Exists(outDir))
Directory.CreateDirectory(outDir);
sevenZip.WriteToDirectory(outDir, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true,
});
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
// Loop through and extract all files
Directory.CreateDirectory(outDir);
sevenZip.Extract(outDir, includeDebug);
return true;
}
#endif
}
}

View File

@@ -1,10 +1,5 @@
using System;
using System.IO;
using System.IO;
using BinaryObjectScanner.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Archives;
using SharpCompress.Archives.Tar;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -26,65 +21,16 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
if (stream == null || !stream.CanRead)
// Create the wrapper
var tar = SabreTools.Serialization.Wrappers.TapeArchive.Create(stream);
if (tar == null)
return false;
#if NET462_OR_GREATER || NETCOREAPP
try
{
var tarFile = TarArchive.Open(stream);
// Loop through and extract all files
Directory.CreateDirectory(outDir);
tar.Extract(outDir, includeDebug);
// Try to read the file path if no entries are found
if (tarFile.Entries.Count == 0 && !string.IsNullOrEmpty(file) && File.Exists(file))
tarFile = TarArchive.Open(file);
foreach (var entry in tarFile.Entries)
{
try
{
// If the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
// If we have a partial entry due to an incomplete multi-part archive, skip it
if (!entry.IsComplete)
continue;
// Ensure directory separators are consistent
string filename = entry.Key;
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
entry.WriteToFile(filename);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
#else
return false;
#endif
return true;
}
}
}

View File

@@ -30,7 +30,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
vbsp.ExtractAllLumps(outDir);
vbsp.Extract(outDir, includeDebug);
return true;
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
vpk.ExtractAll(outDir);
vpk.Extract(outDir, includeDebug);
return true;
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
wad.ExtractAllLumps(outDir);
wad.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,9 +1,5 @@
using System;
using System.IO;
using System.IO;
using BinaryObjectScanner.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Compressors.Xz;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -25,43 +21,16 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
{
#if NET462_OR_GREATER || NETCOREAPP
if (stream == null || !stream.CanRead)
// Create the wrapper
var xz = SabreTools.Serialization.Wrappers.XZ.Create(stream);
if (xz == null)
return false;
try
{
// Try opening the stream
using var xzFile = new XZStream(stream);
// Loop through and extract all files
Directory.CreateDirectory(outDir);
xz.Extract(outDir, includeDebug);
// Ensure directory separators are consistent
string filename = Guid.NewGuid().ToString();
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Extract the file
using FileStream fs = File.OpenWrite(filename);
xzFile.CopyTo(fs);
fs.Flush();
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
#else
return false;
#endif
return true;
}
}
}

View File

@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
// Loop through and extract all files
Directory.CreateDirectory(outDir);
xzp.ExtractAll(outDir);
xzp.Extract(outDir, includeDebug);
return true;
}

View File

@@ -1,20 +0,0 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark an executable type as being able to be extracted
/// </summary>
public interface IExtractableExecutable<T> : IExecutableCheck<T> where T : WrapperBase
{
/// <summary>
/// Extract an Executable to a path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="exe">Executable representing the read-in file</param>
/// <param name="outDir">Path to the output directory</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
bool Extract(string file, T exe, string outDir, bool includeDebug);
}
}

View File

@@ -8,7 +8,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// TODO: Research and add support for ASProtect. It seems to be an additional layer of protection for ASPack, making detecting it separately more difficult.
public class ASPack : IExtractableExecutable<PortableExecutable>
public class ASPack : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -49,12 +49,6 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
/// <summary>
/// Generate the set of matchers used for each section
/// </summary>

View File

@@ -5,7 +5,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// TODO: Verify that all versions are detected
public class AdvancedInstaller : IExtractableExecutable<PortableExecutable>
public class AdvancedInstaller : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -20,11 +20,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -7,7 +7,7 @@ namespace BinaryObjectScanner.Packer
// Created by IndigoRose (creators of Setup Factory), primarily to be used to create autorun menus for various media.
// Official website: https://www.autoplay.org/
// TODO: Add extraction
public class AutoPlayMediaStudio : IExtractableExecutable<PortableExecutable>
public class AutoPlayMediaStudio : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -25,12 +25,6 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
private static string GetVersion(PortableExecutable pex)
{
// Check the product version explicitly

View File

@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Compression.zlib;
using SabreTools.Matching;
using SabreTools.Matching.Content;
using SabreTools.Serialization.Wrappers;
@@ -12,7 +9,7 @@ namespace BinaryObjectScanner.Packer
// The official website for CExe also includes the source code (which does have to be retrieved by the Wayback Machine)
// http://www.scottlu.com/Content/CExe.html
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class CExe : IExtractableExecutable<PortableExecutable>
public class CExe : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -43,107 +40,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
try
{
// Get all resources of type 99 with index 2
var resources = pex.FindResourceByNamedType("99, 2");
if (resources == null || resources.Count == 0)
return false;
// Get the first resource of type 99 with index 2
var payload = resources[0];
if (payload == null || payload.Length == 0)
return false;
// Create the output data buffer
byte[]? data = [];
// If we had the decompression DLL included, it's zlib
if (pex.FindResourceByNamedType("99, 1").Count > 0)
{
try
{
// Inflate the data into the buffer
var zstream = new ZLib.z_stream_s();
data = new byte[payload.Length * 4];
unsafe
{
fixed (byte* payloadPtr = payload)
fixed (byte* dataPtr = data)
{
zstream.next_in = payloadPtr;
zstream.avail_in = (uint)payload.Length;
zstream.total_in = (uint)payload.Length;
zstream.next_out = dataPtr;
zstream.avail_out = (uint)data.Length;
zstream.total_out = 0;
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), payload.Length);
int zret = ZLib.inflate(zstream, 1);
ZLib.inflateEnd(zstream);
}
}
// Trim the buffer to the proper size
uint read = zstream.total_out;
#if NETFRAMEWORK
var temp = new byte[read];
Array.Copy(data, temp, read);
data = temp;
#else
data = new ReadOnlySpan<byte>(data, 0, (int)read).ToArray();
#endif
}
catch
{
// Reset the data
data = null;
}
}
// Otherwise, LZ is used
else
{
try
{
var decompressor = SabreTools.Compression.SZDD.Decompressor.CreateSZDD(payload);
var dataStream = new MemoryStream();
decompressor.CopyTo(dataStream);
data = dataStream.ToArray();
}
catch
{
// Reset the data
data = null;
}
}
// If we have no data
if (data == null)
return false;
// Create the temp filename
string tempFile = string.IsNullOrEmpty(file) ? "temp.sxe" : $"{Path.GetFileNameWithoutExtension(file)}.sxe";
tempFile = Path.Combine(outDir, tempFile);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Write the file data to a temp file
var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
tempStream.Write(data, 0, data.Length);
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
}
}

View File

@@ -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 : IExtractableExecutable<PortableExecutable>
public class Crunch : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -22,11 +22,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -4,7 +4,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class DotFuscator : IExtractableExecutable<PortableExecutable>
public class DotFuscator : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -19,11 +19,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -15,7 +15,7 @@ namespace BinaryObjectScanner.Packer
///
/// Resource that could be useful for extraction: https://github.com/SychicBoy/NETReactorSlayer
/// </summary>
public class DotNetReactor : IExtractableExecutable<PortableExecutable>
public class DotNetReactor : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -87,12 +87,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
// TODO: Add extraction
return false;
}
}
}

View File

@@ -10,7 +10,7 @@ namespace BinaryObjectScanner.Packer
// TODO: Detect 3.15 and up (maybe looking for `Metamorphism`)
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class EXEStealth : IContentCheck, IExtractableExecutable<PortableExecutable>
public class EXEStealth : IContentCheck, IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
@@ -62,11 +62,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -1,181 +0,0 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
/// <summary>
/// Though not technically a packer, this detection is for any executables that include
/// archives in their resources in some uncompressed manner to be used at runtime.
/// </summary>
public class EmbeddedArchive : IExtractableExecutable<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the resources that have a PKZIP signature
if (pex.ResourceData != null)
{
foreach (var value in pex.ResourceData.Values)
{
if (value == null || value is not byte[] ba)
continue;
if (ba.StartsWith([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]))
return "Embedded 7-Zip Archive";
if (ba.StartsWith([0x50, 0x4B, 0x03, 0x04]))
return "Embedded PKZIP Archive";
if (ba.StartsWith([0x50, 0x4B, 0x05, 0x06]))
return "Embedded PKZIP Archive";
if (ba.StartsWith([0x50, 0x4B, 0x07, 0x08]))
return "Embedded PKZIP Archive";
if (ba.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
return "Embedded RAR Archive";
if (ba.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
return "Embedded RAR Archive";
}
}
// Check the overlay, if it exists
if (pex.OverlayData != null && pex.OverlayData.Length > 0)
{
if (pex.OverlayData.StartsWith([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]))
return "Embedded 7-Zip Archive";
if (pex.OverlayData.StartsWith([0x50, 0x4B, 0x03, 0x04]))
return "Embedded PKZIP Archive";
if (pex.OverlayData.StartsWith([0x50, 0x4B, 0x05, 0x06]))
return "Embedded PKZIP Archive";
if (pex.OverlayData.StartsWith([0x50, 0x4B, 0x07, 0x08]))
return "Embedded PKZIP Archive";
if (pex.OverlayData.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
return "Embedded RAR Archive";
if (pex.OverlayData.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
return "Embedded RAR Archive";
}
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
bool overlay = ExtractFromOverlay(pex, outDir, includeDebug);
bool resources = ExtractFromResources(pex, outDir, includeDebug);
return overlay || resources;
}
/// <summary>
/// Extract archive data from the overlay
/// </summary>
private static bool ExtractFromOverlay(PortableExecutable pex, string outDir, bool includeDebug)
{
try
{
// Get the overlay data for easier reading
var overlayData = pex.OverlayData;
if (overlayData == null)
return false;
// Only process the overlay if it has an archive signature
string extension = string.Empty;
if (overlayData.StartsWith([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]))
extension = "7z";
else if (overlayData.StartsWith([0x50, 0x4B, 0x03, 0x04]))
extension = "zip";
else if (overlayData.StartsWith([0x50, 0x4B, 0x05, 0x06]))
extension = "zip";
else if (overlayData.StartsWith([0x50, 0x4B, 0x07, 0x08]))
extension = "zip";
else if (overlayData.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
extension = "rar";
else if (overlayData.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
extension = "rar";
else
return false;
// Create the temp filename
string tempFile = $"embedded_overlay.{extension}";
tempFile = Path.Combine(outDir, tempFile);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Write the resource data to a temp file
using var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
tempStream?.Write(overlayData, 0, overlayData.Length);
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
/// <summary>
/// Extract archive data from the resources
/// </summary>
private static bool ExtractFromResources(PortableExecutable pex, string outDir, bool includeDebug)
{
try
{
// If there are no resources
if (pex.ResourceData == null)
return false;
// Get the resources that have an archive signature
int i = 0;
foreach (var value in pex.ResourceData.Values)
{
if (value == null || value is not byte[] ba)
continue;
// Only process the resource if it has an archive signature
string extension = string.Empty;
if (ba.StartsWith([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]))
extension = "7z";
else if (ba.StartsWith([0x50, 0x4B, 0x03, 0x04]))
extension = "zip";
else if (ba.StartsWith([0x50, 0x4B, 0x05, 0x06]))
extension = "zip";
else if (ba.StartsWith([0x50, 0x4B, 0x07, 0x08]))
extension = "zip";
else if (ba.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
extension = "rar";
else if (ba.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
extension = "rar";
else
continue;
try
{
// Create the temp filename
string tempFile = $"embedded_resource_{i++}.{extension}";
tempFile = Path.Combine(outDir, tempFile);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Write the resource data to a temp file
using var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
tempStream?.Write(ba, 0, ba.Length);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
}
}

View File

@@ -1,134 +0,0 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
/// <summary>
/// Though not technically a packer, this detection is for any executables that include
/// others in their resources in some uncompressed manner to be used at runtime.
/// </summary>
public class EmbeddedExecutable : IExtractableExecutable<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the resources that have an executable signature
if (pex.ResourceData != null)
{
foreach (var value in pex.ResourceData.Values)
{
if (value == null || value is not byte[] ba)
continue;
if (!ba.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes))
continue;
return "Embedded Executable";
}
}
// Check the overlay, if it exists
if (pex.OverlayData != null && pex.OverlayData.Length > 0)
{
if (pex.OverlayData.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes))
return "Embedded Executable";
}
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
bool overlay = ExtractFromOverlay(pex, outDir, includeDebug);
bool resources = ExtractFromResources(pex, outDir, includeDebug);
return overlay || resources;
}
/// <summary>
/// Extract executable data from the overlay
/// </summary>
private static bool ExtractFromOverlay(PortableExecutable pex, string outDir, bool includeDebug)
{
try
{
// Get the overlay data for easier reading
var overlayData = pex.OverlayData;
if (overlayData == null)
return false;
// Only process the overlay if it has an executable signature
if (!overlayData.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes))
return false;
// Create the temp filename
string tempFile = $"embedded_overlay.bin"; // exe/dll
tempFile = Path.Combine(outDir, tempFile);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Write the resource data to a temp file
using var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
tempStream?.Write(overlayData, 0, overlayData.Length);
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
/// <summary>
/// Extract executable data from the resources
/// </summary>
private static bool ExtractFromResources(PortableExecutable pex, string outDir, bool includeDebug)
{
try
{
// If there are no resources
if (pex.ResourceData == null)
return false;
// Get the resources that have an executable signature
int i = 0;
foreach (var value in pex.ResourceData.Values)
{
if (value == null || value is not byte[] ba)
continue;
if (!ba.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes))
continue;
try
{
// Create the temp filename
string tempFile = $"embedded_resource_{i++}.bin"; // exe/dll
tempFile = Path.Combine(outDir, tempFile);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Write the resource data to a temp file
using var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
tempStream?.Write(ba, 0, ba.Length);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
}
}
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
}
}

View File

@@ -0,0 +1,124 @@
using System;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
/// <summary>
/// Though not technically a packer, this detection is for any executables that include
/// archives or executables in their resources in some uncompressed manner to be used at runtime.
/// </summary>
public class EmbeddedFile : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// TODO: Have this return all detected things, not just the first
// Only process the overlay if it is recognized
if (pex.ResourceData != null)
{
// Cache the resource data for easier reading
var resourceData = pex.ResourceData;
// Get the resources that have an archive signature
foreach (var value in resourceData.Values)
{
if (value == null || value is not byte[] ba || ba.Length == 0)
continue;
// Set the output variables
int resourceOffset = 0;
// Only process the resource if it a recognized signature
for (; resourceOffset < 0x100 && resourceOffset < ba.Length - 0x10; resourceOffset++)
{
int temp = resourceOffset;
byte[] resourceSample = ba.ReadBytes(ref temp, 0x10);
if (resourceSample.StartsWith([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]))
return "Embedded 7-Zip Archive";
else if (resourceSample.StartsWith(SabreTools.Models.MicrosoftCabinet.Constants.SignatureBytes))
return "Embedded MS-CAB Archive";
else if (resourceSample.StartsWith(SabreTools.Models.PKZIP.Constants.LocalFileHeaderSignatureBytes))
return "Embedded PKZIP Archive";
else if (resourceSample.StartsWith(SabreTools.Models.PKZIP.Constants.EndOfCentralDirectoryRecordSignatureBytes))
return "Embedded PKZIP Archive";
else if (resourceSample.StartsWith(SabreTools.Models.PKZIP.Constants.EndOfCentralDirectoryRecord64SignatureBytes))
return "Embedded PKZIP Archive";
else if (resourceSample.StartsWith(SabreTools.Models.PKZIP.Constants.DataDescriptorSignatureBytes))
return "Embedded PKZIP Archive";
else if (resourceSample.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
return "Embedded RAR Archive";
else if (resourceSample.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
return "Embedded RAR Archive";
else if (resourceSample.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes))
return "Embedded Executable";
}
}
}
// Check the overlay, if it exists
if (pex.OverlayData != null && pex.OverlayData.Length > 0)
{
// Set the output variables
int overlayOffset = 0;
// Only process the overlay if it is recognized
for (; overlayOffset < 0x100 && overlayOffset < pex.OverlayData.Length - 0x10; overlayOffset++)
{
int temp = overlayOffset;
byte[] overlaySample = pex.OverlayData.ReadBytes(ref temp, 0x10);
if (overlaySample.StartsWith([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]))
{
return "Embedded 7-Zip Archive";
}
else if (overlaySample.StartsWith(SabreTools.Models.MicrosoftCabinet.Constants.SignatureBytes))
{
return "Embedded MS-CAB Archive";
}
else if (overlaySample.StartsWith(SabreTools.Models.PKZIP.Constants.LocalFileHeaderSignatureBytes))
{
return "Embedded PKZIP Archive";
}
else if (overlaySample.StartsWith(SabreTools.Models.PKZIP.Constants.EndOfCentralDirectoryRecordSignatureBytes))
{
return "Embedded PKZIP Archive";
}
else if (overlaySample.StartsWith(SabreTools.Models.PKZIP.Constants.EndOfCentralDirectoryRecord64SignatureBytes))
{
return "Embedded PKZIP Archive";
}
else if (overlaySample.StartsWith(SabreTools.Models.PKZIP.Constants.DataDescriptorSignatureBytes))
{
return "Embedded PKZIP Archive";
}
else if (overlaySample.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
{
return "Embedded RAR Archive";
}
else if (overlaySample.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
{
return "Embedded RAR Archive";
}
else if (overlaySample.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes))
{
return "Embedded Executable";
}
else if (overlaySample.StartsWith([0x3B, 0x21, 0x40, 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C]))
{
// 7-zip SFX script -- ";!@Install" to ";!@InstallEnd@!"
overlayOffset = pex.OverlayData.FirstPosition([0x3B, 0x21, 0x40, 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C, 0x45, 0x6E, 0x64, 0x40, 0x21]);
if (overlayOffset > -1)
return "Embedded 7-Zip Archive";
}
}
}
return null;
}
}
}

View File

@@ -5,7 +5,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class GenteeInstaller : IExtractableExecutable<PortableExecutable>
public class GenteeInstaller : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -23,11 +23,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -9,7 +9,7 @@ namespace BinaryObjectScanner.Packer
// https://www.reddit.com/r/riseofincarnates/comments/m3vbnm/subreddit_revival_does_anyone_still_have_rise_of/
// https://steamcommunity.com/app/310950/discussions/0/4224890554455490819/
// https://github.com/horsicq/Detect-It-Easy/blob/63a1aa8bb23ca02d8a7fd5936db8dbc5c5d52dea/db/PE/HyperTech%20Crackproof.2.sg
public class HyperTechCrackProof : IExtractableExecutable<PortableExecutable>
public class HyperTechCrackProof : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -24,11 +24,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -8,8 +8,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction - https://github.com/dscharrer/InnoExtract
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class InnoSetup : IExtractableExecutable<NewExecutable>,
IExtractableExecutable<PortableExecutable>
public class InnoSetup : IExecutableCheck<NewExecutable>, IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, NewExecutable nex, bool includeDebug)
@@ -52,18 +51,6 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, NewExecutable nex, string outDir, bool includeDebug)
{
return false;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
private static string GetOldVersion(string file, NewExecutable nex)
{
// Notes:

View File

@@ -6,7 +6,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction, which may be possible with the current libraries but needs to be investigated further.
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class InstallAnywhere : IExtractableExecutable<PortableExecutable>
public class InstallAnywhere : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -22,12 +22,6 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
private static string GetVersion(PortableExecutable pex)
{
// Check the internal versions

View File

@@ -5,7 +5,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction - https://github.com/Bioruebe/UniExtract2
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class InstallerVISE : IExtractableExecutable<PortableExecutable>
public class InstallerVISE : IExecutableCheck<PortableExecutable>
{
//TODO: Add exact version detection for Windows builds, make sure versions before 3.X are detected as well, and detect the Mac builds.
/// <inheritdoc/>
@@ -21,11 +21,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -5,7 +5,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction, seems to primarily use MSZip compression.
public class IntelInstallationFramework : IExtractableExecutable<PortableExecutable>
public class IntelInstallationFramework : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -26,11 +26,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -6,7 +6,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class MicrosoftCABSFX : IExtractableExecutable<PortableExecutable>
public class MicrosoftCABSFX : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -40,12 +40,6 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
private static string GetVersion(PortableExecutable pex)
{
// Check the internal versions

View File

@@ -4,7 +4,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class NSIS : IExtractableExecutable<PortableExecutable>
public class NSIS : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -23,11 +23,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -13,7 +13,7 @@ namespace BinaryObjectScanner.Packer
/// PEiD scanning definitions that include NeoLite: https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
/// Website listing various packers, including NeoLite: http://protools.narod.ru/packers.htm
/// </summary>
public class NeoLite : IExtractableExecutable<PortableExecutable>
public class NeoLite : IExecutableCheck<PortableExecutable>
{
// TODO: Find samples of NeoLite 1.X.
/// <inheritdoc/>
@@ -28,11 +28,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -5,7 +5,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Better version detection - https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
// TODO: Add extraction
public class PECompact : IExtractableExecutable<PortableExecutable>
public class PECompact : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -33,11 +33,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -5,7 +5,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class PEtite : IExtractableExecutable<PortableExecutable>
public class PEtite : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -16,11 +16,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -7,7 +7,7 @@ namespace BinaryObjectScanner.Packer
// TODO: Add extraction, which is possible but the only tools available that can
// do this seem to be Universal Extractor 2 and InstallExplorer (https://totalcmd.net/plugring/InstallExplorer.html)
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class SetupFactory : IExtractableExecutable<PortableExecutable>
public class SetupFactory : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -32,12 +32,6 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
private static string GetVersion(PortableExecutable pex)
{
// Check the product version explicitly

View File

@@ -3,7 +3,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
public class SevenZipSFX : IExtractableExecutable<PortableExecutable>
public class SevenZipSFX : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -36,12 +36,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
var sevenZip = new FileType.SevenZip();
return sevenZip.Extract(file, outDir, lookForHeader: true, includeDebug);
}
}
}

View File

@@ -5,7 +5,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class Shrinker : IExtractableExecutable<PortableExecutable>
public class Shrinker : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -16,11 +16,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -8,7 +8,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class UPX : IExtractableExecutable<PortableExecutable>
public class UPX : IExecutableCheck<PortableExecutable>
{
private static readonly Regex _oldUpxVersionMatch = new Regex(@"\$Id: UPX (.*?) Copyright \(C\)", RegexOptions.Compiled);
@@ -56,12 +56,6 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
try

View File

@@ -3,7 +3,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
public class WinRARSFX : IExtractableExecutable<PortableExecutable>
public class WinRARSFX : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -17,12 +17,5 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
var rar = new FileType.RAR();
return rar.Extract(file, outDir, lookForHeader: true, includeDebug);
}
}
}

View File

@@ -5,7 +5,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
public class WinZipSFX : IExtractableExecutable<NewExecutable>, IExtractableExecutable<PortableExecutable>
public class WinZipSFX : IExecutableCheck<NewExecutable>, IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, NewExecutable nex, bool includeDebug)
@@ -56,23 +56,6 @@ namespace BinaryObjectScanner.Packer
// TODO: Find a way to generically detect 2.X versions and improve exact version detection for SFX PE versions bundled with WinZip 11+
/// <inheritdoc/>
public bool Extract(string file, NewExecutable nex, string outDir, bool includeDebug)
=> Extract(file, outDir, includeDebug);
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
=> Extract(file, outDir, includeDebug);
/// <summary>
/// Handle common extraction between executable types
/// </summary>
public static bool Extract(string file, string outDir, bool includeDebug)
{
var pkzip = new FileType.PKZIP();
return pkzip.Extract(file, outDir, lookForHeader: true, includeDebug);
}
/// <summary>
/// Get the version from the NE header value combinations
/// </summary>

View File

@@ -1,155 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Matching.Content;
using SabreTools.Serialization.Wrappers;
using WiseUnpacker;
using WiseUnpacker.EWISE;
namespace BinaryObjectScanner.Packer
{
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class WiseInstaller : IExtractableExecutable<NewExecutable>, IExtractableExecutable<PortableExecutable>
public class WiseInstaller : IExecutableCheck<NewExecutable>, IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, NewExecutable nex, bool includeDebug)
{
// If we match a known header
if (MatchesNEVersion(nex) != null)
// If the overlay header can be found
if (nex.FindWiseOverlayHeader() > -1)
return "Wise Installation Wizard Module";
// TODO: Investigate STUB.EXE in nonresident-name table
// TODO: Don't read entire file
var data = nex.ReadArbitraryRange();
if (data == null)
return null;
var neMatchSets = new List<ContentMatchSet>
{
// WiseInst
new(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x49, 0x6E, 0x73, 0x74 }, "Wise Installation Wizard Module"),
// WiseMain
new(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"),
};
return MatchUtil.GetFirstMatch(file, data, neMatchSets, includeDebug);
return null;
}
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// If we match a known header
if (GetPEFormat(pex) != null)
// If the overlay header can be found
if (pex.FindWiseOverlayHeader() > -1)
return "Wise Installation Wizard Module";
// TODO: Investigate STUB32.EXE in export directory table
// Get the .data/DATA section strings, if they exist
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
if (strs.Exists(s => s.Contains("WiseMain")))
return "Wise Installation Wizard Module";
}
// Get the .rdata section strings, if they exist
strs = pex.GetFirstSectionStrings(".rdata");
if (strs != null)
{
if (strs.Exists(s => s.Contains("WiseMain")))
return "Wise Installation Wizard Module";
}
return null;
}
/// <inheritdoc/>
public bool Extract(string file, NewExecutable nex, string outDir, bool includeDebug)
{
try
{
Directory.CreateDirectory(outDir);
return Extractor.ExtractTo(file, outDir);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
try
{
Directory.CreateDirectory(outDir);
return Extractor.ExtractTo(file, outDir);
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
/// <summary>
/// Checks an NE header to see if it matches a known signature
/// </summary>
/// <param name="nex">New executable to check</param>
/// <returns>True if it matches a known version, false otherwise</returns>
private static FormatProperty? MatchesNEVersion(NewExecutable nex)
{
// TODO: Offset is _not_ the EXE header address, rather where the data starts. Fix this.
return (nex.Model.Stub?.Header?.NewExeHeaderAddr) switch
{
0x84b0 => new FormatProperty { ArchiveEnd = -1 },
0x3e10 => new FormatProperty { ArchiveEnd = -1 },
0x3e50 => new FormatProperty { ArchiveEnd = -1 },
0x3c20 => new FormatProperty { ArchiveEnd = -1 },
0x3c30 => new FormatProperty { ArchiveEnd = -1 },
0x3660 => new FormatProperty { ArchiveEnd = 0x3c },
0x36f0 => new FormatProperty { ArchiveEnd = 0x44 },
0x3770 => new FormatProperty { ArchiveEnd = 0x4c },
0x3780 => new FormatProperty { ArchiveEnd = 0x4c },
0x37b0 => new FormatProperty { ArchiveEnd = 0x4c },
0x37d0 => new FormatProperty { ArchiveEnd = 0x4c },
0x3c80 => new FormatProperty { ArchiveEnd = 0x4c },
0x3bd0 => new FormatProperty { ArchiveEnd = 0x4c },
0x3c10 => new FormatProperty { ArchiveEnd = 0x4c },
_ => null,
};
}
/// <summary>
/// Checks a PE header to see if it matches a known signature
/// </summary>
/// <param name="pex">Portable executable to check</param>
/// <returns>True if it matches a known version, false otherwise</returns>
private static FormatProperty? GetPEFormat(PortableExecutable pex)
{
// Get the current format
var current = new FormatProperty
{
ExecutableType = ExecutableType.PE,
CodeSectionLength = (int?)pex.GetFirstSection(".text")?.VirtualSize ?? -1,
DataSectionLength = (int?)pex.GetFirstSection(".data")?.VirtualSize ?? -1,
};
// Search known formats
foreach (var format in FormatProperty.KnownFormats)
{
if (current.Equals(format))
return format;
}
// Found in Binary.WiseCustomCalla
if (pex.OverlayAddress == 0x6200)
return new FormatProperty { ArchiveEnd = 0x4c };
// If the section header can be found
if (pex.FindWiseSection() != null)
return "Wise Installation Wizard Module";
return null;
}

View File

@@ -18,7 +18,7 @@ namespace BinaryObjectScanner.Protection
// TODO: Add version checking, if possible
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class Armadillo : IExtractableExecutable<PortableExecutable>
public class Armadillo : IExecutableCheck<PortableExecutable>
{
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -44,11 +44,5 @@ namespace BinaryObjectScanner.Protection
return null;
}
/// <inheritdoc/>
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
{
return false;
}
}
}

View File

@@ -1,7 +0,0 @@
# External Library Notes
This directory contains multiple external libraries. Here is the status of each:
| Directory | Library | Status |
| --------- | ------- | ------ |
| stormlibsharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | External submodule |

Some files were not shown because too many files have changed in this diff Show More