mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-09 02:16:46 +00:00
The update
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace BinaryObjectScanner.Test
|
||||
//WrapperType.CIA,
|
||||
//WrapperType.Executable,
|
||||
WrapperType.GCF,
|
||||
WrapperType.GZIP,
|
||||
WrapperType.GZip,
|
||||
WrapperType.InstallShieldArchiveV3,
|
||||
WrapperType.InstallShieldCAB,
|
||||
WrapperType.LZKWAJ,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
BinaryObjectScanner.Test/Packer/EmbeddedFileTests.cs
Normal file
22
BinaryObjectScanner.Test/Packer/EmbeddedFileTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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(),
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
|
||||
|
||||
// Extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
bfpk.ExtractAll(outDir);
|
||||
bfpk.Extract(outDir, includeDebug);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
BinaryObjectScanner/FileType/GZip.cs
Normal file
36
BinaryObjectScanner/FileType/GZip.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.FileType
|
||||
|
||||
// Extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
pff.ExtractAll(outDir);
|
||||
pff.Extract(outDir, includeDebug);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
BinaryObjectScanner/Packer/EmbeddedFile.cs
Normal file
124
BinaryObjectScanner/Packer/EmbeddedFile.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
Submodule BinaryObjectScanner/_EXTERNAL/stormlibsharp deleted from 3bf1375317
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user