Compare commits

...

49 Commits
3.1.0 ... 3.1.5

Author SHA1 Message Date
Matt Nadareski
25d6822283 Bump version 2024-04-05 15:42:43 -04:00
Silent
3b22262c21 Update SecuROM v7 detection to correctly handle a partly stripped header (#297) 2024-04-04 15:00:09 -04:00
Matt Nadareski
314fc1e3fc Update SabreTools.Printing 2024-04-04 13:48:00 -04:00
Matt Nadareski
5742749dec Update packages 2024-04-04 12:12:52 -04:00
Silent
c55fffeb7b Fix a crash when a sharing violation occurs during --info (#296)
Prints an exception the same way GetInternalProtections
does.
2024-04-03 12:52:12 -07:00
TheRogueArchivist
e469dc38bf Fix Roxxe false positive (#294) 2024-04-02 21:57:23 -07:00
Matt Nadareski
553703c30e Bump version 2024-04-02 17:03:49 -04:00
Matt Nadareski
3fd093f9b4 Update packages 2024-04-02 16:37:02 -04:00
TheRogueArchivist
4946d4e7ff Add Roxxe detection (#293)
* Add Roxxe detection

* Fix Roxxe PR review comments
2024-04-01 09:14:13 -07:00
Matt Nadareski
491fc0f71c Bump version 2024-03-27 12:01:40 -04:00
Matt Nadareski
fe6627f1ba Update to WiseUnpacker 1.3.1 2024-03-25 22:43:34 -04:00
Matt Nadareski
edffa3c7cc Default to using net8.0 target for launch 2024-03-25 22:32:29 -04:00
Matt Nadareski
a66d62bfbc Remove version gate from UnshieldSharp in Test 2024-03-25 21:56:08 -04:00
Matt Nadareski
9321b8f221 Update to UnshieldSharp 1.7.3 2024-03-25 21:53:33 -04:00
Matt Nadareski
cd0863ac56 Ensure zlib inflateEnd is called 2024-03-25 21:34:38 -04:00
Matt Nadareski
24a73e8bfd Update README 2024-03-25 20:08:51 -04:00
Matt Nadareski
46eaa7db1e Fix errant formatting issues 2024-03-25 19:58:37 -04:00
TheRogueArchivist
0eab7fd555 Significantly update Rainbow Sentinel detections and notes (#290) 2024-03-25 16:57:29 -07:00
Matt Nadareski
dba476d8bb Create new extractable interfaces for each executable type 2024-03-25 19:55:16 -04:00
Matt Nadareski
b10b4d6658 Seek to beginning for Embedded Executable 2024-03-25 17:14:10 -04:00
Matt Nadareski
2959fdbe9a Fix zlib with init 2024-03-25 17:10:24 -04:00
Matt Nadareski
9c0d100c2a Set zlib totals (nw) 2024-03-25 17:06:17 -04:00
Matt Nadareski
03ca0faf2e Start fixing zlib implementation 2024-03-25 16:58:11 -04:00
Matt Nadareski
cbaf004e25 Seek to beginning of stream for CExe 2024-03-25 16:37:53 -04:00
Matt Nadareski
bbe4fb610c Correct assumption about directory separators 2024-03-25 16:16:55 -04:00
Matt Nadareski
650115f722 Replace SharpZipLib with ST.Compression 2024-03-25 15:00:42 -04:00
Matt Nadareski
1afcbe3182 Update packages 2024-03-25 14:41:20 -04:00
TheRogueArchivist
8aa90dbc49 Add FlexLM detection (#289) 2024-03-24 19:08:09 -07:00
Matt Nadareski
0781524669 Bump version 2024-03-16 11:16:00 -04:00
Matt Nadareski
3b3cb7a862 Case-insensitive hash comparisons 2024-03-15 21:22:48 -04:00
Matt Nadareski
810d20d95c Bump version 2024-03-14 13:20:30 -04:00
Matt Nadareski
de578511bf Fix type retrieval for all build types 2024-03-13 23:26:16 -04:00
Matt Nadareski
f1ec025950 Fix nulability warning 2024-03-12 16:54:49 -04:00
Matt Nadareski
0c58ecc548 Update packages 2024-03-12 16:53:12 -04:00
Matt Nadareski
d2a73a153b Unroll Linq statements to fix exceptions (fixes #287) 2024-03-10 22:27:18 -04:00
TheRogueArchivist
eae2e3366b Remove DBB Skeleton (#286)
DBB is now documented in DRML.
2024-03-06 08:15:06 -08:00
Matt Nadareski
afb04c99c0 Update packages 2024-03-06 11:08:22 -05:00
Matt Nadareski
1d3bd2f8b1 Use SabreTools.Hashing 2024-03-04 21:27:36 -05:00
TheRogueArchivist
3f52c24713 Move CopyKiller comments to DRML (#285) 2024-02-28 07:36:23 -08:00
Matt Nadareski
ae1417a343 Remove unnecessary commit setting 2024-02-27 17:17:04 -05:00
Matt Nadareski
871a3e6366 Add PR check workflow 2024-02-27 11:21:01 -05:00
Matt Nadareski
3457b807cb Remove GHA on pull request builds 2024-02-27 11:17:41 -05:00
Matt Nadareski
027f295d21 Remove unnecessary newline 2024-02-26 12:46:26 -05:00
Matt Nadareski
63e6d1e285 Add badges to README 2024-02-26 12:43:49 -05:00
Matt Nadareski
2193095f70 Change nupkg location 2024-02-26 12:42:55 -05:00
Matt Nadareski
074694298f Typo typo 2024-02-26 12:37:43 -05:00
Matt Nadareski
ce4d32b053 Forgot to add recursive here 2024-02-26 12:37:11 -05:00
Matt Nadareski
a25af3940c Add recursive checkout 2024-02-26 12:36:47 -05:00
Matt Nadareski
9d1a2db45a Migrate to GitHub Actions 2024-02-26 12:29:12 -05:00
55 changed files with 1452 additions and 888 deletions

43
.github/workflows/build_nupkg.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Nuget Pack
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Pack
run: dotnet pack
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: 'Nuget Package'
path: 'BinaryObjectScanner/bin/Release/*.nupkg'
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: 'BinaryObjectScanner/bin/Release/*.nupkg'
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

53
.github/workflows/build_test.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Build Test
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [Test]
runtime: [win-x86, win-x64, linux-x64, osx-x64] #[win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
conf: [Release, Debug]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
- name: Archive build
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

17
.github/workflows/check_pr.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: Build PR
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Build
run: dotnet build

2
.vscode/launch.json vendored
View File

@@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Test/bin/Debug/net6.0/Test.dll",
"program": "${workspaceFolder}/Test/bin/Debug/net8.0/Test.dll",
"args": [],
"cwd": "${workspaceFolder}/Test",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console

View File

@@ -11,7 +11,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<!-- <TreatWarningsAsErrors>true</TreatWarningsAsErrors> --> <!-- Can't be enabled because of external code -->
<Version>3.1.0</Version>
<Version>3.1.5</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
@@ -70,12 +70,10 @@
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`))">
<PackageReference Include="OpenMcdf" Version="2.3.0" />
<PackageReference Include="UnshieldSharp" Version="1.7.2" />
<PackageReference Include="OpenMcdf" Version="2.3.1" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="SharpCompress" Version="0.36.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
@@ -83,12 +81,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.Compression" Version="0.3.0" />
<PackageReference Include="SabreTools.IO" Version="1.3.0" />
<PackageReference Include="SabreTools.Matching" Version="1.3.0" />
<PackageReference Include="SabreTools.Models" Version="1.3.0" />
<PackageReference Include="SabreTools.Serialization" Version="1.3.2" />
<PackageReference Include="WiseUnpacker" Version="1.3.0" />
<PackageReference Include="SabreTools.Compression" Version="0.4.2" />
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
<PackageReference Include="SabreTools.IO" Version="1.3.3" />
<PackageReference Include="SabreTools.Matching" Version="1.3.1" />
<PackageReference Include="SabreTools.Models" Version="1.4.2" />
<PackageReference Include="SabreTools.Serialization" Version="1.5.0" />
<PackageReference Include="UnshieldSharp" Version="1.7.4" />
<PackageReference Include="WiseUnpacker" Version="1.3.2" />
</ItemGroup>
</Project>

View File

@@ -43,7 +43,7 @@ namespace BinaryObjectScanner.FileType
get
{
contentCheckClasses ??= InitCheckClasses<IContentCheck>();
return contentCheckClasses ?? Enumerable.Empty<IContentCheck>();
return contentCheckClasses ?? [];
}
}
@@ -55,7 +55,7 @@ namespace BinaryObjectScanner.FileType
get
{
linearExecutableCheckClasses ??= InitCheckClasses<ILinearExecutableCheck>();
return linearExecutableCheckClasses ?? Enumerable.Empty<ILinearExecutableCheck>();
return linearExecutableCheckClasses ?? [];
}
}
@@ -67,7 +67,7 @@ namespace BinaryObjectScanner.FileType
get
{
msdosExecutableCheckClasses ??= InitCheckClasses<IMSDOSExecutableCheck>();
return msdosExecutableCheckClasses ?? Enumerable.Empty<IMSDOSExecutableCheck>();
return msdosExecutableCheckClasses ?? [];
}
}
@@ -79,7 +79,7 @@ namespace BinaryObjectScanner.FileType
get
{
newExecutableCheckClasses ??= InitCheckClasses<INewExecutableCheck>();
return newExecutableCheckClasses ?? Enumerable.Empty<INewExecutableCheck>();
return newExecutableCheckClasses ?? [];
}
}
@@ -91,7 +91,7 @@ namespace BinaryObjectScanner.FileType
get
{
portableExecutableCheckClasses ??= InitCheckClasses<IPortableExecutableCheck>();
return portableExecutableCheckClasses ?? Enumerable.Empty<IPortableExecutableCheck>();
return portableExecutableCheckClasses ?? [];
}
}
@@ -529,7 +529,7 @@ namespace BinaryObjectScanner.FileType
return protections;
}
#endregion
#endregion
#region Initializers
@@ -537,17 +537,40 @@ namespace BinaryObjectScanner.FileType
/// Initialize all implementations of a type
/// </summary>
private static IEnumerable<T>? InitCheckClasses<T>() =>
InitCheckClasses<T>(typeof(Handler).Assembly) ?? Enumerable.Empty<T>();
InitCheckClasses<T>(Assembly.GetExecutingAssembly()) ?? [];
/// <summary>
/// Initialize all implementations of a type
/// </summary>
private static IEnumerable<T>? InitCheckClasses<T>(Assembly assembly)
{
return assembly.GetTypes()?
.Where(t => t.IsClass && t.GetInterface(typeof(T).Name) != null)?
.Select(t => (T?)Activator.CreateInstance(t))
.Cast<T>() ?? [];
List<T> classTypes = [];
// If not all types can be loaded, use the ones that could be
List<Type> assemblyTypes = [];
try
{
assemblyTypes = assembly.GetTypes().ToList<Type>();
}
catch (ReflectionTypeLoadException rtle)
{
assemblyTypes = rtle.Types.Where(t => t != null)!.ToList<Type>();
}
// Loop through all types
foreach (Type type in assemblyTypes)
{
// If the type isn't a class or doesn't implement the interface
if (!type.IsClass || type.GetInterface(typeof(T).Name) == null)
continue;
// Try to create a concrete instance of the type
var instance = (T?)Activator.CreateInstance(type);
if (instance != null)
classTypes.Add(instance);
}
return classTypes;
}
#endregion

View File

@@ -2,9 +2,7 @@
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
#if NET40_OR_GREATER || NETCOREAPP
using UnshieldSharp.Archive;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -26,10 +24,6 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
{
#if NET20 || NET35
// Not supported for .NET Framework 2.0 or .NET Framework 3.5 due to library support
return null;
#else
try
{
// Create a temp output directory
@@ -68,7 +62,6 @@ namespace BinaryObjectScanner.FileType
if (includeDebug) Console.WriteLine(ex);
return null;
}
#endif
}
}
}

View File

@@ -2,9 +2,7 @@
using System.IO;
using System.Text.RegularExpressions;
using BinaryObjectScanner.Interfaces;
#if NET40_OR_GREATER || NETCOREAPP
using UnshieldSharp.Cabinet;
#endif
namespace BinaryObjectScanner.FileType
{
@@ -26,10 +24,6 @@ namespace BinaryObjectScanner.FileType
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
{
#if NET20 || NET35
// Not supported for .NET Framework 2.0 or .NET Framework 3.5 due to library support
return null;
#else
// Get the name of the first cabinet file or header
var directory = Path.GetDirectoryName(file);
string noExtension = Path.GetFileNameWithoutExtension(file);
@@ -102,7 +96,6 @@ namespace BinaryObjectScanner.FileType
if (includeDebug) Console.WriteLine(ex);
return null;
}
#endif
}
}
}

View File

@@ -3,9 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using ICSharpCode.SharpZipLib.Zip.Compression;
#endif
using SabreTools.Compression.zlib;
namespace BinaryObjectScanner.FileType
{
@@ -230,15 +228,26 @@ namespace BinaryObjectScanner.FileType
}
else
{
// Decompress the data
#if NET462_OR_GREATER || NETCOREAPP
// Inflate the data into the buffer
var zstream = new ZLib.z_stream_s();
data = new byte[outputFileSize];
Inflater inflater = new Inflater();
inflater.SetInput(compressedData);
inflater.Inflate(data);
#else
data = new byte[outputFileSize];
#endif
unsafe
{
fixed (byte* payloadPtr = compressedData)
fixed (byte* dataPtr = data)
{
zstream.next_in = payloadPtr;
zstream.avail_in = (uint)compressedData.Length;
zstream.total_in = (uint)compressedData.Length;
zstream.next_out = dataPtr;
zstream.avail_out = (uint)data.Length;
zstream.total_out = 0;
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), compressedData.Length);
int zret = ZLib.inflate(zstream, 1);
ZLib.inflateEnd(zstream);
}
}
}
// If we have an invalid output directory

View File

@@ -9,6 +9,7 @@ using System.Reflection;
using System.Threading.Tasks;
using BinaryObjectScanner.Interfaces;
using BinaryObjectScanner.Utilities;
using SabreTools.Serialization.Wrappers;
using static BinaryObjectScanner.Utilities.Dictionary;
namespace BinaryObjectScanner
@@ -62,7 +63,7 @@ namespace BinaryObjectScanner
#endif
// Preprocess the list of files
files = files?.Select(f => f.Replace('\\', '/'))?.ToList();
files = files?.Select(f => f.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar))?.ToList();
// Iterate through all checks
#if NET20 || NET35
@@ -108,7 +109,7 @@ namespace BinaryObjectScanner
/// <summary>
/// Handle files based on an IExtractable implementation
/// </summary>
/// <param name="impl">IDetectable class representing the file type</param>
/// <param name="impl">IExtractable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="stream">Stream to scan the contents of</param>
/// <param name="scanner">Scanner object to use on extractable contents</param>
@@ -153,6 +154,198 @@ namespace BinaryObjectScanner
return null;
}
/// <summary>
/// Handle files based on an IExtractableMSDOSExecutable implementation
/// </summary>
/// <param name="impl">IExtractableMSDOSExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="mz">MSDOS to scan the contents of</param>
/// <param name="scanner">Scanner object to use on extractable contents</param>
/// <returns>Set of protections in file, null on error</returns>
#if NET20 || NET35
public static Dictionary<string, Queue<string>>? HandleExtractable(IExtractableMSDOSExecutable impl, string fileName, MSDOS mz, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractableMSDOSExecutable impl, string fileName, MSDOS mz, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, mz, scanner.IncludeDebug);
if (tempPath == null)
return null;
// Collect and format all found protections
var subProtections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Prepare the returned protections
StripFromKeys(subProtections, tempPath);
PrependToKeys(subProtections, fileName);
return subProtections;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
return null;
}
/// <summary>
/// Handle files based on an IExtractableLinearExecutable implementation
/// </summary>
/// <param name="impl">IExtractableLinearExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="lex">LinearExecutable to scan the contents of</param>
/// <param name="scanner">Scanner object to use on extractable contents</param>
/// <returns>Set of protections in file, null on error</returns>
#if NET20 || NET35
public static Dictionary<string, Queue<string>>? HandleExtractable(IExtractableLinearExecutable impl, string fileName, LinearExecutable lex, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractableLinearExecutable impl, string fileName, LinearExecutable lex, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, lex, scanner.IncludeDebug);
if (tempPath == null)
return null;
// Collect and format all found protections
var subProtections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Prepare the returned protections
StripFromKeys(subProtections, tempPath);
PrependToKeys(subProtections, fileName);
return subProtections;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
return null;
}
/// <summary>
/// Handle files based on an IExtractableNewExecutable implementation
/// </summary>
/// <param name="impl">IExtractableNewExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="nex">NewExecutable to scan the contents of</param>
/// <param name="scanner">Scanner object to use on extractable contents</param>
/// <returns>Set of protections in file, null on error</returns>
#if NET20 || NET35
public static Dictionary<string, Queue<string>>? HandleExtractable(IExtractableNewExecutable impl, string fileName, NewExecutable nex, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractableNewExecutable impl, string fileName, NewExecutable nex, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, nex, scanner.IncludeDebug);
if (tempPath == null)
return null;
// Collect and format all found protections
var subProtections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Prepare the returned protections
StripFromKeys(subProtections, tempPath);
PrependToKeys(subProtections, fileName);
return subProtections;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
return null;
}
/// <summary>
/// Handle files based on an IExtractablePortableExecutable implementation
/// </summary>
/// <param name="impl">IExtractablePortableExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="pex">PortableExecutable to scan the contents of</param>
/// <param name="scanner">Scanner object to use on extractable contents</param>
/// <returns>Set of protections in file, null on error</returns>
#if NET20 || NET35
public static Dictionary<string, Queue<string>>? HandleExtractable(IExtractablePortableExecutable impl, string fileName, PortableExecutable pex, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractablePortableExecutable impl, string fileName, PortableExecutable pex, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, pex, scanner.IncludeDebug);
if (tempPath == null)
return null;
// Collect and format all found protections
var subProtections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Prepare the returned protections
StripFromKeys(subProtections, tempPath);
PrependToKeys(subProtections, fileName);
return subProtections;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
return null;
}
/// <summary>
/// Handle files based on an IPathCheck implementation
/// </summary>
@@ -204,16 +397,40 @@ namespace BinaryObjectScanner
/// Initialize all implementations of a type
/// </summary>
private static IEnumerable<T?> InitCheckClasses<T>() =>
InitCheckClasses<T>(typeof(Handler).Assembly);
InitCheckClasses<T>(Assembly.GetExecutingAssembly());
/// <summary>
/// Initialize all implementations of a type
/// </summary>
private static IEnumerable<T?> InitCheckClasses<T>(Assembly assembly)
{
return assembly.GetTypes()?
.Where(t => t.IsClass && t.GetInterface(typeof(T).Name) != null)?
.Select(t => (T?)Activator.CreateInstance(t)) ?? [];
List<T?> classTypes = [];
// If not all types can be loaded, use the ones that could be
List<Type> assemblyTypes = [];
try
{
assemblyTypes = assembly.GetTypes().ToList<Type>();
}
catch (ReflectionTypeLoadException rtle)
{
assemblyTypes = rtle.Types.Where(t => t != null)!.ToList<Type>();
}
// Loop through all types
foreach (Type type in assemblyTypes)
{
// If the type isn't a class or doesn't implement the interface
if (!type.IsClass || type.GetInterface(typeof(T).Name) == null)
continue;
// Try to create a concrete instance of the type
var instance = (T?)Activator.CreateInstance(type);
if (instance != null)
classTypes.Add(instance);
}
return classTypes;
}
#endregion

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a LinearExecutable type as being able to be extracted
/// </summary>
public interface IExtractableLinearExecutable
{
/// <summary>
/// Extract a LinearExecutable to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="lex">LinearExecutable representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, LinearExecutable lex, bool includeDebug);
}
}

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a MSDOS type as being able to be extracted
/// </summary>
public interface IExtractableMSDOSExecutable
{
/// <summary>
/// Extract a MSDOS to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="mz">MSDOS representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, MSDOS mz, bool includeDebug);
}
}

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a NewExecutable type as being able to be extracted
/// </summary>
public interface IExtractableNewExecutable
{
/// <summary>
/// Extract a NewExecutable to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="nex">NewExecutable representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, NewExecutable nex, bool includeDebug);
}
}

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a PortableExecutable type as being able to be extracted
/// </summary>
public interface IExtractablePortableExecutable
{
/// <summary>
/// Extract a PortableExecutable to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="pex">PortableExecutable representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, PortableExecutable pex, bool includeDebug);
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
@@ -8,7 +7,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class ASPack : IExtractable, IPortableExecutableCheck
public class ASPack : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -51,19 +50,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -8,7 +6,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// TODO: Verify that all versions are detected
public class AdvancedInstaller : IExtractable, IPortableExecutableCheck
public class AdvancedInstaller : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -30,19 +28,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Linq;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -8,7 +7,7 @@ namespace BinaryObjectScanner.Packer
// TODO: Add extraction
// TODO: Add version checking, if possible
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class Armadillo : IExtractable, IPortableExecutableCheck
public class Armadillo : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -42,19 +41,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -8,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 : IExtractable, IPortableExecutableCheck
public class AutoPlayMediaStudio : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -34,19 +33,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -3,9 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using ICSharpCode.SharpZipLib.Zip.Compression;
#endif
using SabreTools.Compression.zlib;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
@@ -14,7 +12,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 : IExtractable, IPortableExecutableCheck
public class CExe : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -52,25 +50,10 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, includeDebug);
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
try
{
// Parse into an executable again for easier extraction
var pex = PortableExecutable.Create(stream);
if (pex == null)
return null;
// Get the first resource of type 99 with index 2
var payload = pex.FindResourceByNamedType("99, 2").FirstOrDefault();
if (payload == null || payload.Length == 0)
@@ -88,16 +71,34 @@ namespace BinaryObjectScanner.Packer
try
{
// Inflate the data into the buffer
#if NET462_OR_GREATER || NETCOREAPP
Inflater inflater = new Inflater();
inflater.SetInput(payload);
var zstream = new ZLib.z_stream_s();
data = new byte[payload.Length * 4];
int read = inflater.Inflate(data);
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
data = new ReadOnlySpan<byte>(data, 0, read).ToArray();
uint read = zstream.total_out;
#if NET462_OR_GREATER || NETCOREAPP
data = new ReadOnlySpan<byte>(data, 0, (int)read).ToArray();
#else
data = null;
var temp = new byte[read];
Array.Copy(data, 0, temp, 0, read);
data = temp;
#endif
}
catch
@@ -107,7 +108,7 @@ namespace BinaryObjectScanner.Packer
}
}
// Otherwise, LZ is used via the Windows API
// Otherwise, LZ is used
else
{
try

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
@@ -10,7 +9,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, IExtractable, IPortableExecutableCheck
public class EXEStealth : IContentCheck, IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
@@ -75,19 +74,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -11,7 +11,7 @@ namespace BinaryObjectScanner.Packer
/// 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 : IExtractable, IPortableExecutableCheck
public class EmbeddedExecutable : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -29,27 +29,10 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
try
{
// Parse into an executable again for easier extraction
var pex = PortableExecutable.Create(stream);
if (pex?.ResourceData == null)
return null;
// Get the resources that have an executable signature
var resources = pex.ResourceData
.Where(kvp => kvp.Value != null && kvp.Value is byte[])

View File

@@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -8,7 +6,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class GenteeInstaller : IExtractable, IPortableExecutableCheck
public class GenteeInstaller : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -33,19 +31,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System.IO;
using System.Linq;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -10,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 : IExtractable, IPortableExecutableCheck
public class HyperTechCrackProof : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -32,19 +31,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
@@ -9,7 +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 : IExtractable, INewExecutableCheck, IPortableExecutableCheck
public class InnoSetup : IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
@@ -54,19 +53,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -7,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 : IExtractable, IPortableExecutableCheck
public class InstallAnywhere : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -29,19 +28,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -8,7 +6,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 : IExtractable, IPortableExecutableCheck
public class InstallerVISE : IExtractablePortableExecutable, IPortableExecutableCheck
{
//TODO: Add exact version detection for Windows builds, make sure versions before 3.X are detected as well, and detect the Mac builds.
/// <inheritdoc/>
@@ -31,19 +29,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,12 +1,11 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction, seems to primarily use MSZip compression.
public class IntelInstallationFramework : IExtractable, IPortableExecutableCheck
public class IntelInstallationFramework : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -34,19 +33,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -8,7 +7,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction, which should be possible with LibMSPackN, but it refuses to extract due to SFX files lacking the typical CAB identifiers.
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class MicrosoftCABSFX : IExtractable, IPortableExecutableCheck
public class MicrosoftCABSFX : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -48,23 +47,11 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
private string GetVersion(PortableExecutable pex)
{
// Check the internal versions

View File

@@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -7,7 +5,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class NSIS : IExtractable, IPortableExecutableCheck
public class NSIS : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -33,19 +31,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -14,7 +14,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 : IExtractable, IPortableExecutableCheck
public class NeoLite : IExtractablePortableExecutable, IPortableExecutableCheck
{
// TODO: Find samples of NeoLite 1.X.
/// <inheritdoc/>
@@ -37,20 +37,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
// TODO: Add extraction
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,12 +1,11 @@
using System.IO;
using BinaryObjectScanner.Interfaces;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Better version detection - https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
// TODO: Add extraction
public class PECompact : IExtractable, IPortableExecutableCheck
public class PECompact : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -42,19 +41,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,12 +1,11 @@
using System.IO;
using BinaryObjectScanner.Interfaces;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class PEtite : IExtractable, IPortableExecutableCheck
public class PEtite : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -25,19 +24,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -8,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 : IExtractable, IPortableExecutableCheck
public class SetupFactory : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -39,23 +38,11 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
private string GetVersion(PortableExecutable pex)
{
// Check the product version explicitly

View File

@@ -1,4 +1,3 @@
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -6,7 +5,7 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class SevenZipSFX : IExtractable, IPortableExecutableCheck
public class SevenZipSFX : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -46,19 +45,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,4 +1,3 @@
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
@@ -6,7 +5,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class Shrinker : IExtractable, IPortableExecutableCheck
public class Shrinker : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -26,19 +25,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
@@ -10,7 +9,7 @@ namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class UPX : IExtractable, IPortableExecutableCheck
public class UPX : IExtractablePortableExecutable, IPortableExecutableCheck
{
private static readonly Regex _oldUpxVersionMatch = new Regex(@"\$Id: UPX (.*?) Copyright \(C\)", RegexOptions.Compiled);
@@ -64,19 +63,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -11,7 +11,7 @@ using SharpCompress.Readers;
namespace BinaryObjectScanner.Packer
{
public class WinRARSFX : IExtractable, IPortableExecutableCheck
public class WinRARSFX : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -33,17 +33,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, includeDebug);
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
#if NET462_OR_GREATER || NETCOREAPP
try

View File

@@ -11,7 +11,7 @@ using SharpCompress.Archives.Zip;
namespace BinaryObjectScanner.Packer
{
public class WinZipSFX : IExtractable, INewExecutableCheck, IPortableExecutableCheck
public class WinZipSFX : IExtractableNewExecutable, IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
@@ -63,17 +63,17 @@ 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 string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, includeDebug);
}
public string? Extract(string file, NewExecutable nex, bool includeDebug)
=> Extract(file, includeDebug);
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
=> Extract(file, includeDebug);
/// <summary>
/// Handle common extraction between executable types
/// </summary>
private static string? Extract(string file, bool includeDebug)
{
#if NET462_OR_GREATER || NETCOREAPP
try
@@ -122,7 +122,7 @@ namespace BinaryObjectScanner.Packer
/// </summary>
/// TODO: Reduce the checks to only the ones that differ between versions
/// TODO: Research to see if the versions are embedded elsewhere in these files
private string? GetNEHeaderVersion(NewExecutable nex)
private static string? GetNEHeaderVersion(NewExecutable nex)
{
#region 2.0 Variants
@@ -680,7 +680,7 @@ namespace BinaryObjectScanner.Packer
/// Get the version from the PE export directory table value combinations
/// </summary>
/// TODO: Research to see if the versions are embedded elsewhere in these files
private string? GetPEExportDirectoryVersion(PortableExecutable pex)
private static string? GetPEExportDirectoryVersion(PortableExecutable pex)
{
string sfxFileName = pex.Model.ExportTable?.ExportDirectoryTable?.Name ?? string.Empty;
uint sfxTimeDateStamp = pex.Model.ExportTable?.ExportDirectoryTable?.TimeDateStamp ?? uint.MaxValue;

View File

@@ -11,7 +11,7 @@ using Wise = WiseUnpacker.WiseUnpacker;
namespace BinaryObjectScanner.Packer
{
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class WiseInstaller : IExtractable, INewExecutableCheck, IPortableExecutableCheck
public class WiseInstaller : IExtractableNewExecutable, IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
@@ -73,33 +73,140 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
public string? Extract(string file, NewExecutable nex, bool includeDebug)
{
if (!File.Exists(file))
return null;
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
try
{
return Extract(fs, file, includeDebug);
// TODO: Try to find where the file data lives and how to get it
var unpacker = new Wise();
if (!unpacker.ExtractTo(file, tempPath))
{
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
}
return null;
}
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
return null;
}
return tempPath;
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
try
{
// Try to parse as a New Executable
var nex = NewExecutable.Create(stream);
if (nex != null)
return ExtractNewExecutable(nex, file, includeDebug);
// Get the matching PE format
var format = GetPEFormat(pex);
if (format == null)
return null;
// Try to parse as a Portable Executable
var pex = PortableExecutable.Create(stream);
if (pex != null)
return ExtractPortableExecutable(pex, file, includeDebug);
// Get the overlay data for easier reading
int overlayOffset = 0, dataStart = 0;
var overlayData = pex.OverlayData;
if (overlayData == null)
return null;
return null;
// Skip over the additional DLL name, if we expect it
if (format.Dll)
{
// Read the name length
byte dllNameLength = overlayData.ReadByte(ref overlayOffset);
dataStart++;
// Read the name, if it exists
if (dllNameLength != 0)
{
// Ignore the name for now
_ = overlayData.ReadBytes(ref overlayOffset, dllNameLength);
dataStart += dllNameLength;
// Named DLLs also have a DLL length that we ignore
_ = overlayData.ReadUInt32(ref overlayOffset);
dataStart += 4;
}
}
// Check if flags are consistent
if (!format.NoCrc)
{
// Unlike WiseUnpacker, we ignore the flag value here
_ = overlayData.ReadUInt32(ref overlayOffset);
}
// Ensure that we have an archive end
if (format.ArchiveEnd > 0)
{
overlayOffset = dataStart + format.ArchiveEnd;
int archiveEndLoaded = overlayData.ReadInt32(ref overlayOffset);
if (archiveEndLoaded != 0)
format.ArchiveEnd = archiveEndLoaded;
}
// Skip to the start of the archive
overlayOffset = dataStart + format.ArchiveStart;
// Skip over the initialization text, if we expect it
if (format.InitText)
{
int initTextLength = overlayData.ReadByte(ref overlayOffset);
_ = overlayData.ReadBytes(ref overlayOffset, initTextLength);
}
// Cache the current offset in the overlay as the "start of data"
int offsetReal = overlayOffset;
// If the first entry is PKZIP, we assume it's an embedded zipfile
var magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4;
bool pkzip = magic?.StartsWith(new byte?[] { (byte)'P', (byte)'K' }) ?? false;
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
// If we have PKZIP
if (pkzip)
{
string tempFile = Path.Combine(tempPath, "WISEDATA.zip");
using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
tempStream.Write(overlayData, overlayOffset, overlayData.Length - overlayOffset);
}
}
// If we have DEFLATE -- TODO: Port implementation here or use DeflateStream
else
{
Wise unpacker = new Wise();
if (!unpacker.ExtractTo(file, tempPath))
{
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
}
return null;
}
}
return tempPath;
}
catch (Exception ex)
{
@@ -210,165 +317,10 @@ namespace BinaryObjectScanner.Packer
return null;
}
/// <summary>
/// Attempt to extract Wise data from a New Executable
/// </summary>
/// <param name="nex">New executable to check</param>
/// <param name="file">Path to the input file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>True if it matches a known version, false otherwise</returns>
private string? ExtractNewExecutable(NewExecutable nex, string file, bool includeDebug)
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
try
{
// TODO: Try to find where the file data lives and how to get it
Wise unpacker = new Wise();
if (!unpacker.ExtractTo(file, tempPath))
{
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
}
return null;
}
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
return null;
}
return tempPath;
}
/// <summary>
/// Attempt to extract Wise data from a Portable Executable
/// </summary>
/// <param name="pex">Portable executable to check</param>
/// <param name="file">Path to the input file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>True if it matches a known version, false otherwise</returns>
private string? ExtractPortableExecutable(PortableExecutable pex, string file, bool includeDebug)
{
try
{
// Get the matching PE format
var format = GetPEFormat(pex);
if (format == null)
return null;
// Get the overlay data for easier reading
int overlayOffset = 0, dataStart = 0;
var overlayData = pex.OverlayData;
if (overlayData == null)
return null;
// Skip over the additional DLL name, if we expect it
if (format.Dll)
{
// Read the name length
byte dllNameLength = overlayData.ReadByte(ref overlayOffset);
dataStart++;
// Read the name, if it exists
if (dllNameLength != 0)
{
// Ignore the name for now
_ = overlayData.ReadBytes(ref overlayOffset, dllNameLength);
dataStart += dllNameLength;
// Named DLLs also have a DLL length that we ignore
_ = overlayData.ReadUInt32(ref overlayOffset);
dataStart += 4;
}
}
// Check if flags are consistent
if (!format.NoCrc)
{
// Unlike WiseUnpacker, we ignore the flag value here
_ = overlayData.ReadUInt32(ref overlayOffset);
}
// Ensure that we have an archive end
if (format.ArchiveEnd > 0)
{
overlayOffset = dataStart + format.ArchiveEnd;
int archiveEndLoaded = overlayData.ReadInt32(ref overlayOffset);
if (archiveEndLoaded != 0)
format.ArchiveEnd = archiveEndLoaded;
}
// Skip to the start of the archive
overlayOffset = dataStart + format.ArchiveStart;
// Skip over the initialization text, if we expect it
if (format.InitText)
{
int initTextLength = overlayData.ReadByte(ref overlayOffset);
_ = overlayData.ReadBytes(ref overlayOffset, initTextLength);
}
// Cache the current offset in the overlay as the "start of data"
int offsetReal = overlayOffset;
// If the first entry is PKZIP, we assume it's an embedded zipfile
var magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4;
bool pkzip = magic?.StartsWith(new byte?[] { (byte)'P', (byte)'K' }) ?? false;
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
// If we have PKZIP
if (pkzip)
{
string tempFile = Path.Combine(tempPath, "WISEDATA.zip");
using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
tempStream.Write(overlayData, overlayOffset, overlayData.Length - overlayOffset);
}
}
// If we have DEFLATE -- TODO: Port implementation here or use DeflateStream
else
{
Wise unpacker = new Wise();
if (!unpacker.ExtractTo(file, tempPath))
{
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
}
return null;
}
}
return tempPath;
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
return null;
}
}
/// <summary>
/// Class representing the properties of each recognized Wise installer format
/// </summary>
/// <see href="https://github.com/mnadareski/WiseUnpacker/blob/master/WiseUnpacker/FormatProperty.cs"/>
/// TODO: Requires all fields to be writable in package before replacement
private class FormatProperty
{
/// <summary>

View File

@@ -1,13 +1,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class dotFuscator : IExtractable, IPortableExecutableCheck
public class dotFuscator : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -29,19 +27,7 @@ namespace BinaryObjectScanner.Packer
}
/// <inheritdoc/>
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Extract(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Extract(Stream? stream, string file, bool includeDebug)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -10,33 +10,10 @@ using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// CopyKiller was a program made by Webstylerzone that allowed you to copyprotect your own discs.
/// It appears to have used 3 different main forms of copy protection:
///
/// First, its core copy protection is applied by adding a folder from the program's installation directory to the disc as you burn it.
/// The files in this folder appear to only be text files identifying the software used, and seemingly random file contents.
/// How this protects the disc is currently not confirmed, and the data itself isn't corrupted or copied incorrectly on purpose.
/// A personal guess is that it intended to use the same effect as SafeDisc's "weak sectors" to rely on the drive writing the disc incorrectly and making an "uncopyable" disc.
/// This is backed up by an official description of how CopyKillers works, saying how it "uses a firmware error to make the cd copy protected." (https://web.archive.org/web/20061109151642/http://www.webtoolmaster.com/copykiller.htm)
///
/// Second, an optional autorun feature can be used by adding the appropriate contents of the "Autorun" folder from the program's installation directory to the disc as you burn it.
/// This relies on Window running the autorun automatically, causing a window to warning to popup that tells the user that this is a pirated copy, with seemingly nothing else happening.
/// I believe that it simply checks for the presence of the other protection files due to the complete lack of any ability to customize the DRM.
///
/// Last, there is a locked option to learn how to use it to protect audio CDs, but unfortunately this is only available with a registered version.
/// This means that the mechanics of how this was done are currently unknown, but may have simply been to write the same folder's data in, whether as raw audio data or a separate data track.
///
/// At some point at least as early as 2006 (https://web.archive.org/web/20061109151642/http://www.webtoolmaster.com/copykiller.htm), WTM (WebToolMaster) and Webstylerzone had some sort of partnership.
/// For example, WTM began hosting a link to CopyKiller beginning in 2006, and Webstylerzoning advertising WTM's products (https://web.archive.org/web/20070811202419/http://www.webstylerzone.com/en/download_brenner_copykiller_safedisc_safediscscanner_whatspeed_copyprotection_copy_protection_protect_cd_cds_audiocd_datacd_against_copying.htm).
/// As of October of 2011, WTM announced that CopyKiller was officially no longer being developed (https://web.archive.org/web/20111014233821/http://webtoolmaster.com/copykiller.htm).
///
/// CopyKiller website: https://web.archive.org/web/20030312200712/http://www.webstylerzone.com/CopyKiller/index.htm
/// Version 3.62 Installer: https://web.archive.org/web/20031130192048/http://www.webstylerzone.com/Downloads/Brennertools/CopyKiller-Setup.exe
/// Version 3.64 Installer: https://web.archive.org/web/20060524220845/http://download.webstylerzone.com:80/exe/CopyKiller-Setup.exe
/// Version 3.99 Installer: https://web.archive.org/web/20060524220845/http://download.webstylerzone.com:80/exe/CopyKiller-Setup.exe
/// Version 3.99a Installer: https://web.archive.org/web/20070721070138/http://www.webstylerzone.com/Downloads/exe/CopyKiller-Setup.exe
/// Version 3.99a Portable: https://web.archive.org/web/20070721070214/http://www.webstylerzone.com/Downloads/zip/CopyKiller.zip
///
/// CopyKiller was a program made by WebStylerZone that allowed users to copy-protect their burned discs.
/// It worked by having users copy files with byte patterns that would create weak sectors to their discs to burn, and relied on drives with buggy firmwares to create bad burns of the discs.
/// This would result in discs having intentional bad sectors, making them harder to copy. There was also an optional autorun available that would check for the original CopyKiller files on the disc.
/// <see href="https://github.com/TheRogueArchivist/DRML/blob/main/entries/CopyKiller/CopyKiller.md"/>
/// TODO: Add support for the developer's EXE obfuscator, "EXEShield Deluxe". Most, if not all, EXEShield protected files are currently detected as "EXE Stealth" by BOS.
/// Samples include CopyKiller (Versions 3.64 & 3.99a) and SafeDiscScanner (Version 0.16) (https://archive.org/details/safediscscanner-0.16-webstylerzone-from-unofficial-source).
/// </summary>

View File

@@ -1,7 +0,0 @@
namespace BinaryObjectScanner.Protection
{
public class DBB
{
// TODO: Implement - http://web.archive.org/web/20040604233815/www.wkit.com/sites/wkit/setup/eng/index.asp
}
}

View File

@@ -1,4 +1,11 @@
using System;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Protection
@@ -16,12 +23,96 @@ namespace BinaryObjectScanner.Protection
if (sections == null)
return null;
// Found in "IsSvcInstDanceEJay7.dll" in IA item "computer200709dvd" (Dance eJay 7).
var name = pex.ProductName;
// Found in "IsSvcInstDanceEJay7.dll" in IA item "computer200709dvd" (Dance eJay 7).
if (name?.Equals("FLEXnet Activation Toolkit", StringComparison.OrdinalIgnoreCase) == true)
return $"FLEXnet";
return "FLEXnet";
// Found in "INSTALLS.EXE", "LMGR326B.DLL", "LMGRD.EXE", and "TAKEFIVE.EXE" in IA item "prog-17_202403".
if (name?.Equals("Globetrotter Software Inc lmgr326b Flexlm", StringComparison.OrdinalIgnoreCase) == true)
return $"FlexLM {pex.ProductVersion}";
// Generic case to catch unknown versions.
if (name?.Contains("Flexlm") == true)
return "FlexLM (Unknown Version - Please report to us on GitHub)";
name = pex.FileDescription;
// Found in "INSTALLS.EXE", "LMGR326B.DLL", "LMGRD.EXE", and "TAKEFIVE.EXE" in IA item "prog-17_202403".
if (name?.Equals("lmgr326b", StringComparison.OrdinalIgnoreCase) == true)
return $"FlexLM {pex.ProductVersion}";
name = pex.LegalTrademarks;
// Found in "INSTALLS.EXE", "LMGR326B.DLL", "LMGRD.EXE", and "TAKEFIVE.EXE" in IA item "prog-17_202403".
if (name?.Equals("Flexible License Manager,FLEXlm,Globetrotter,FLEXID", StringComparison.OrdinalIgnoreCase) == true)
return $"FlexLM {pex.ProductVersion}";
if (name?.Contains("FLEXlm") == true)
return $"FlexLM {pex.ProductVersion}";
name = pex.OriginalFilename;
// Found in "INSTALLS.EXE", "LMGR326B.DLL", "LMGRD.EXE", and "TAKEFIVE.EXE" in IA item "prog-17_202403".
// It isn't known why these various executables have the same original filename.
if (name?.Equals("lmgr326b.dll", StringComparison.OrdinalIgnoreCase) == true)
return $"FlexLM {pex.ProductVersion}";
// Get the .data/DATA section strings, if they exist
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
// Found in "FLEXLM.CPL", "INSTALLS.EXE", "LMGR326B.DLL", "LMGRD.EXE", and "TAKEFIVE.EXE" in IA item "prog-17_202403".
if (strs.Any(s => s.Contains("FLEXlm License Manager")))
return "FlexLM";
}
return null;
}
/// <inheritdoc cref="Interfaces.IPathCheck.CheckDirectoryPath(string, IEnumerable{string})"/>
#if NET20 || NET35
internal Queue<string> FLEXNetCheckDirectoryPath(string path, IEnumerable<string>? files)
#else
internal ConcurrentQueue<string> FLEXNetDirectoryPath(string path, IEnumerable<string>? files)
#endif
{
var matchers = new List<PathMatchSet>
{
// Found in IA item "prog-17_202403".
new(new PathMatch("FlexLM-6.1F", useEndsWith: true), "FlexLM 6.1f"),
new(new PathMatch("FlexLM", useEndsWith: true), "FlexLM"),
new(new PathMatch("FLexLM_Licensing.wri", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMGR326B.DLL", useEndsWith: true), "FlexLM"),
new(new PathMatch("FLEXLM.CPL", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMGRD.EXE", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMGRD95.EXE", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMUTIL.EXE", useEndsWith: true), "FlexLM"),
new(new PathMatch("READFLEX.WRI", useEndsWith: true), "FlexLM"),
};
return MatchUtil.GetAllMatches(files, matchers, any: false);
}
/// <inheritdoc cref="Interfaces.IPathCheck.CheckFilePath(string)"/>
internal string? FLEXNetCheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
// Found in IA item "prog-17_202403".
new(new PathMatch("FlexLM-6.1F", useEndsWith: true), "FlexLM 6.1f"),
new(new PathMatch("FlexLM", useEndsWith: true), "FlexLM"),
new(new PathMatch("FLexLM_Licensing.wri", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMGR326B.DLL", useEndsWith: true), "FlexLM"),
new(new PathMatch("FLEXLM.CPL", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMGRD.EXE", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMGRD95.EXE", useEndsWith: true), "FlexLM"),
new(new PathMatch("LMUTIL.EXE", useEndsWith: true), "FlexLM"),
new(new PathMatch("READFLEX.WRI", useEndsWith: true), "FlexLM"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
}
}

View File

@@ -4,9 +4,9 @@ using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.IO;
using SabreTools.Hashing;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
using static BinaryObjectScanner.Utilities.Hashing;
namespace BinaryObjectScanner.Protection
{
@@ -47,8 +47,8 @@ namespace BinaryObjectScanner.Protection
// So far, every seemingly-randomly named EXE on RipGuard discs have a consistent hash.
if (fi.Length == 49_152)
{
var sha1 = GetFileSHA1(file);
if (sha1 == "6A7B8545800E0AB252773A8CD0A2185CA2497938")
var sha1 = HashTool.GetFileHash(file, HashType.SHA1);
if (string.Equals(sha1, "6A7B8545800E0AB252773A8CD0A2185CA2497938", StringComparison.OrdinalIgnoreCase))
return "RipGuard";
}
}

View File

@@ -5,9 +5,9 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SabreTools.Hashing;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
using static BinaryObjectScanner.Utilities.Hashing;
namespace BinaryObjectScanner.Protection
{
@@ -363,8 +363,8 @@ namespace BinaryObjectScanner.Protection
return string.Empty;
// The hash of the file CLCD16.dll is able to provide a broad version range that appears to be consistent, but it seems it was rarely updated so these checks are quite broad.
var sha1 = GetFileSHA1(firstMatchedString);
return sha1 switch
var sha1 = HashTool.GetFileHash(firstMatchedString, HashType.SHA1);
return sha1?.ToUpperInvariant() switch
{
// Found in Redump entries 61731 and 66005.
"C13493AB753891B8BEE9E4E014896B026C01AC92" => "1.00.025-1.01.044",
@@ -386,8 +386,8 @@ namespace BinaryObjectScanner.Protection
return string.Empty;
// The hash of the file CLCD32.dll so far appears to be a solid indicator of version for versions it was used with. It appears to have been updated with every release, unlike its counterpart, CLCD16.dll.
var sha1 = GetFileSHA1(firstMatchedString);
return sha1 switch
var sha1 = HashTool.GetFileHash(firstMatchedString, HashType.SHA1);
return sha1?.ToUpperInvariant() switch
{
// Found in Redump entry 66005.
"BAD49BA0DEA041E85EF1CABAA9F0ECD822CE1376" => "1.00.025",
@@ -485,8 +485,8 @@ namespace BinaryObjectScanner.Protection
// The hash of every "CLOKSPL.EXE" correlates directly to a specific SafeDisc version.
var sha1 = GetFileSHA1(firstMatchedString);
return sha1 switch
var sha1 = HashTool.GetFileHash(firstMatchedString, HashType.SHA1);
return sha1?.ToUpperInvariant() switch
{
// Found in Redump entry 66005.
"DD131A7B988065764E2A0F20B66C89049B20A7DE" => "1.00.025",
@@ -632,8 +632,8 @@ namespace BinaryObjectScanner.Protection
// There are occasionaly inconsistencies, even within the well detected version range. This seems to me to mostly happen with later (3.20+) games, and seems to me to be an example of the SafeDisc distribution becoming more disorganized with time.
// Particularly interesting inconsistencies will be noted below:
// Redump entry 73786 has an EXE with a scrubbed version, a DIAG.exe with a version of 4.60.000, and a copy of drvmgt.dll belonging to version 3.10.020. This seems like an accidental(?) distribution of older drivers, as this game was released 3 years after the use of 3.10.020.
var sha1 = GetFileSHA1(firstMatchedString);
return sha1 switch
var sha1 = HashTool.GetFileHash(firstMatchedString, HashType.SHA1);
return sha1?.ToUpperInvariant() switch
{
// Found in Redump entry 102979.
"B858CB282617FB0956D960215C8E84D1CCF909C6" => "(Empty File)",
@@ -780,8 +780,8 @@ namespace BinaryObjectScanner.Protection
if (string.IsNullOrEmpty(firstMatchedString) || !File.Exists(firstMatchedString))
return string.Empty;
var sha1 = GetFileSHA1(firstMatchedString);
switch (sha1)
var sha1 = HashTool.GetFileHash(firstMatchedString, HashType.SHA1);
switch (sha1?.ToUpperInvariant())
{
// Found in Redump entry 63488.
case "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709":

View File

@@ -4,6 +4,7 @@ using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
@@ -15,16 +16,122 @@ namespace BinaryObjectScanner.Protection
/// Rainbow Sentinel SuperPro: https://www.rainbow.com.my/superpro.php
/// TODO: Investigate other versions/products.
/// TODO: See if this is at all related to https://cpl.thalesgroup.com/software-monetization/all-products/sentinel-hl.
/// TODO: Investigate the possible integration between FlexLM and Rainbow Sentinel in IA item "prog-17_202403".
/// TODO: Investigate the "NetSentinel Protection System" found in "NSRVOM.EXE" and "NSRVGX.EXE" in IA item "czchip199707cd".
/// TODO: Investigate "sntnlusb.sys" (https://www.rainbow.com.my/document/endusertroubleshooting.pdf).
///
/// Versions:
/// Rainbow Sentinel PD-5.1: IA items "pcwkcd-1296" and "CHIPTRMart97".
/// Rainbow Sentinel PD-5.1e (Beta): IA item "CHIPTRMart97".
/// Rainbow Sentinel PD-5.39: IA item "chip-cds-2001-08".
/// Rainbow Sentinel PD-15: IA items "ASMEsMechanicalEngineeringToolkit1997December" and "aplicaciones-windows".
/// Rainbow Sentinel PD-17: IA item "czchip199707cd".
/// Rainbow Sentinel PD-30: BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]" and IA item "auto-cad-r14-cdrom".
/// Rainbow Sentinel PD-31: BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]" and IA item "auto-cad-r14-cdrom".
///
/// Rainbow Sentinel SuperPro 5.0: IA items "chip-cds-2001-08".
/// Rainbow Sentinel SuperPro 5.1: IA items "ASMEsMechanicalEngineeringToolkit1997December" and "aplicaciones-windows".
///
/// Rainbow SentinelPro 5.1: IA item "pcwkcd-1296".
///
/// Rainbow NetSentinel: IA item "czchip199707cd".
/// </summary>
public class RainbowSentinel : IPathCheck, IPortableExecutableCheck
public class RainbowSentinel : IPathCheck, INewExecutableCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
{
// TODO: Don't read entire file
var data = nex.ReadArbitraryRange();
if (data == null)
return null;
// TODO: Figure out what NE section this lives in
var neMatchSets = new List<ContentMatchSet>
{
// SentinelPro Windows Driver DLL
// Found in "SSWIN.dll" in IA item "pcwkcd-1296".
new(new byte?[]
{
0x53, 0x65, 0x6E, 0x74, 0x69, 0x6E, 0x65, 0x6C,
0x50, 0x72, 0x6F, 0x20, 0x57, 0x69, 0x6E, 0x64,
0x6F, 0x77, 0x73, 0x20, 0x44, 0x72, 0x69, 0x76,
0x65, 0x72, 0x20, 0x44, 0x4C, 0x4C
}, "Rainbow SentinelPro"),
// Sentinel Device Driver Version <20>PD-5.17
// Found in "SENTINEL.SYS" in IA item "czchip199707cd".
new(new byte?[]
{
0x53, 0x65, 0x6E, 0x74, 0x69, 0x6E, 0x65, 0x6C,
0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20,
0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x20, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x00,
0x50, 0x44, 0x2D, 0x35, 0x2E, 0x31, 0x37
}, "Rainbow Sentinel PD-5.17"),
// NetSentinel OS/2 security server
// Found in "NSRVOM.EXE" in IA item "czchip199707cd".
new(new byte?[]
{
0x4E, 0x65, 0x74, 0x53, 0x65, 0x6E, 0x74, 0x69,
0x6E, 0x65, 0x6C, 0x20, 0x4F, 0x53, 0x2F, 0x32,
0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72
}, "Rainbow NetSentinel Server for OS/2"),
// NetSentinel Monitor
// Found in "OS2MON.EXE" in IA item "czchip199707cd".
new(new byte?[]
{
0x4E, 0x65, 0x74, 0x53, 0x65, 0x6E, 0x74, 0x69,
0x6E, 0x65, 0x6C, 0x20, 0x20, 0x4D, 0x6F, 0x6E,
0x69, 0x74, 0x6F, 0x72
}, "Rainbow NetSentinel Monitor"),
// Sentinel Device Driver
// Generic case to catch unknown versions.
// TODO: Add version parsing for this check.
new (new byte?[]
{
0x53, 0x65, 0x6E, 0x74, 0x69, 0x6E, 0x65, 0x6C,
0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20,
0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x20, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x00,
0x50, 0x44, 0x2D, 0x35, 0x2E, 0x31, 0x37
}, "Rainbow Sentinel (Unknown Version - Please report this to us on GitHub)"),
};
var match = MatchUtil.GetFirstMatch(file, data, neMatchSets, includeDebug);
if (!string.IsNullOrEmpty(match))
return match;
// Check the nonresident-name table
// Found in "SSWIN.dll" in IA item "pcwkcd-1296".
bool nonresidentNameTableEntries = nex.Model.NonResidentNameTable?
.Select(nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString))
.Any(s => s.Contains("SentinelPro Windows Driver DLL")) ?? false;
if (nonresidentNameTableEntries)
return "Rainbow SentinelPro";
// Found in "INSTALL.EXE" in IA item "czchip199707cd".
nonresidentNameTableEntries = nex.Model.NonResidentNameTable?
.Select(nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString))
.Any(s => s.Contains("Rainbow Technologies Installation Program")) ?? false;
if (nonresidentNameTableEntries)
return "Rainbow Sentinel";
// Found in "WNCEDITD.EXE" and "WNCEDITO.EXE" in IA item "czchip199707cd".
nonresidentNameTableEntries = nex.Model.NonResidentNameTable?
.Select(nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString))
.Any(s => s.Contains("NetSentinel-C Editor for Windows")) ?? false;
if (nonresidentNameTableEntries)
return "NetSentinel-C Editor for Windows";
// TODO: Investigate "SentinelScribe Windows Driver DLL" found in "NKWIN.DLL" in IA item "czchip199707cd".
return null;
}
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
@@ -33,30 +140,6 @@ namespace BinaryObjectScanner.Protection
if (sections == null)
return null;
// Get the .data/DATA section strings, if they exist
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
// Found in "ADESKSYS.DLL"/"WINADMIN.EXE"/"WINQUERY.EXE" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]", folder "\netsetup\SUPPORT\IPX".
if (strs.Any(s => s.Contains("Rainbow SentinelSuperPro")))
return "Rainbow Sentinel SuperPro";
}
// Get the .text section strings, if they exist
strs = pex.GetFirstSectionStrings(".text");
if (strs != null)
{
// Found in "ACLT.HWL" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]", folder "\aclt\DRV\W95LOCK".
// Found in "ACAD.HWL" in BA entry "Autodesk AutoCAD r14 (1997)" and IA item "auto-cad-r14-cdrom".
if (strs.Any(s => s.Contains("SENTINEL.VXD")))
return "Rainbow Sentinel SuperPro";
// Found in "ADESKSYS.DLL" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]", folder "\netsetup\SUPPORT\IPX".
// TODO: Investigate "Elan License Manager" mentioned here.
if (strs.Any(s => s.Contains("Rainbow SentinelSuperPro")))
return "Rainbow Sentinel SuperPro";
}
// TODO: Figure out why resources for "RNBOVTMP.DLL", "SENTTEMP.DLL", "SNTI386.DLL", and "SX32W.DL_"/"SX32W.DLL" aren't getting read properly, causing checks for these files to not work.
var name = pex.FileDescription;
@@ -81,6 +164,14 @@ namespace BinaryObjectScanner.Protection
if (name?.Equals("Rainbow Technologies SentinelSuperPro WIN32 DLL", StringComparison.OrdinalIgnoreCase) == true)
return $"Rainbow Sentinel SuperPro {pex.ProductVersion}";
// Found in "SP32W.DLL" in IA item "pcwkcd-1296".
if (name?.Equals("Rainbow Technologies SentinelPro WIN32 DLL", StringComparison.OrdinalIgnoreCase) == true)
return $"Rainbow SentinelPro {pex.ProductVersion}";
// Found in "NSRVGX.EXE" in IA item "czchip199707cd".
if (name?.Equals("NetSentinel Server for WIN 32", StringComparison.OrdinalIgnoreCase) == true)
return "Rainbow NetSentinel Server for Win32";
name = pex.ProductName;
// Found in multiple files in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]", including "RNBOVTMP.DLL", "SENTTEMP.DLL", and "SNTI386.DLL".
@@ -95,6 +186,93 @@ namespace BinaryObjectScanner.Protection
if (name?.Equals("Rainbow Technologies SentinelSuperPro WIN32 DLL", StringComparison.OrdinalIgnoreCase) == true)
return $"Rainbow Sentinel SuperPro {pex.ProductVersion}";
// Found in "SP32W.DLL" in IA item "pcwkcd-1296".
if (name?.Equals("Rainbow Technologies SentinelPro WIN32 DLL", StringComparison.OrdinalIgnoreCase) == true)
return $"Rainbow SentinelPro {pex.ProductVersion}";
// Found in "F481_SetupSysDriver.exe.B391C18A_6953_11D4_82CB_00D0B72E1DB9"/"SetupSysDriver.exe" in IA item "chip-cds-2001-08".
if (name?.Equals("Sentinel System Driver", StringComparison.OrdinalIgnoreCase) == true)
return $"Rainbow Sentinel {pex.ProductVersion}";
// Get the .data/DATA section strings, if they exist
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
// Found in "ADESKSYS.DLL"/"WINADMIN.EXE"/"WINQUERY.EXE" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]", folder "\netsetup\SUPPORT\IPX".
if (strs.Any(s => s.Contains("Rainbow SentinelSuperPro")))
return "Rainbow Sentinel SuperPro";
// Found in "SETUPAXP.EXE", "SETUPMPS.EXE", and "SETUPPPC.EXE" in IA item "czchip199707cd".
if (strs.Any(s => s.Contains("Sentinel Driver Setup Program")))
return "Rainbow Sentinel";
}
// Get the .rdata section strings, if they exist
strs = pex.GetFirstSectionStrings(".rdata");
if (strs != null)
{
// Found in "SP32W.DLL" in IA item "pcwkcd-1296".
if (strs.Any(s => s.Contains("SentinelPro WIN32 DLL")))
return "Rainbow SentinelPro";
// Found in "NKWIN32.DLL" in IA item "czchip199707cd".
if (strs.Any(s => s.Contains("NetSentinel-C Windows NT Driver DLL")))
return "Rainbow NetSentinel-C Windows NT Driver";
// Found in "NSLMS32.DLL" in IA item "czchip199707cd".
if (strs.Any(s => s.Contains("NetSentinel 32-Bit Windows DLL")))
return "Rainbow NetSentinel Win32 Driver";
// Found in "W32EDITD.EXE" and "W32EDITO.EXE" in IA item "czchip199707cd".
if (strs.Any(s => s.Contains("NetSentinel-C Editor for Windows")))
return "NetSentinel-C Editor for Win32";
// Generic case to catch undetected versions.
if (strs.Any(s => s.Contains("SentinelPro")))
return "Rainbow SentinelPro (Unknown Version - Please report to us on GitHub)";
}
// Get the .rsrc section strings, if they exist
strs = pex.GetFirstSectionStrings(".rsrc");
if (strs != null)
{
// Found in "WINMON.exe" in IA item "czchip199707cd".
if (strs.Any(s => s.Contains("NetSentinel Monitor")))
return "Rainbow NetSentinel Monitor";
}
// Get the .text section strings, if they exist
strs = pex.GetFirstSectionStrings(".text");
if (strs != null)
{
// Found in "ACLT.HWL" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]", folder "\aclt\DRV\W95LOCK".
// Found in "ACAD.HWL" in BA entry "Autodesk AutoCAD r14 (1997)" and IA item "auto-cad-r14-cdrom".
if (strs.Any(s => s.Contains("\\\\.\\SENTINEL.VXD")))
return "Rainbow Sentinel";
// Found in "ADESKSYS.DLL" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]", folder "\netsetup\SUPPORT\IPX".
// TODO: Investigate "Elan License Manager" mentioned here.
if (strs.Any(s => s.Contains("Rainbow SentinelSuperPro")))
return "Rainbow Sentinel SuperPro";
// Found in "F1321_dorapro.exe" in IA item "chip-cds-2001-08".
if (strs.Any(s => s.Contains("modSentinelSuperPro")))
return "Rainbow Sentinel SuperPro";
// Found in "F1321_dorapro.exe" in IA item "chip-cds-2001-08".
if (strs.Any(s => s.Contains("clsSentinelSuperPro")))
return "Rainbow Sentinel SuperPro";
// Found in "SENTSTRT.EXE" in IA item "czchip199707cd".
if (strs.Any(s => s.Contains("Sentinel Driver Startup Program")))
return "Rainbow Sentinel";
// Found in "SETUPX86.EXE" in IA item "czchip199707cd".
if (strs.Any(s => s.Contains("Sentinel Windows NT Driver Setup")))
return "Rainbow Sentinel";
}
return null;
}
@@ -107,8 +285,11 @@ namespace BinaryObjectScanner.Protection
{
var matchers = new List<PathMatchSet>
{
// The Parallel Port driver for Rainbow Sentinel on Win9x (https://www.rainbow.com.my/document/endusertroubleshooting.pdf).
// Unfortunately, the file name overlaps with a file used by Clam Sentinel (https://clamsentinel.sourceforge.net/).
// new(new FilePathMatch("SENTINEL.VXD"), "Rainbow Sentinel"),
// Found in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]" and IA item "auto-cad-r14-cdrom".
new(new FilePathMatch("SENTINEL.VXD"), "Rainbow Sentinel"),
new(new FilePathMatch("SENTSTRT.EXE"), "Rainbow Sentinel"),
new(new FilePathMatch("SENTW95.DLL"), "Rainbow Sentinel"),
new(new FilePathMatch("SENTW95.EXE"), "Rainbow Sentinel"),
@@ -127,9 +308,69 @@ namespace BinaryObjectScanner.Protection
new(new FilePathMatch("RAINBNT.Z"), "Rainbow Sentinel"),
// Found in "wd126.zip/WDSHARE.EXE" in IA item "ASMEsMechanicalEngineeringToolkit1997December" and "WDSHARE.ZIP/WDSHARE.EXE/SX32W.DL_" in IA item "aplicaciones-windows".
new(new FilePathMatch("RainbowSentinel.386"), "Rainbow Sentinel"),
new(new FilePathMatch("SX32W.DL_"), "Rainbow Sentinel"),
new(new FilePathMatch("SX32W.DLL"), "Rainbow Sentinel"),
new(new FilePathMatch("RainbowSentinel.386"), "Rainbow Sentinel"),
new(new FilePathMatch("SX32W.DL_"), "Rainbow Sentinel"),
new(new FilePathMatch("SX32W.DLL"), "Rainbow Sentinel"),
// Found in IA item "pcwkcd-1296".
new(new FilePathMatch("SP32W.DLL"), "Rainbow Sentinel"),
new(new FilePathMatch("SSWIN.DLL"), "Rainbow Sentinel"),
// Found in IA item "czchip199707cd".
new(new FilePathMatch("SENTINEL.DPP"), "Rainbow Sentinel OS/2 Installation Script"),
new(new FilePathMatch("SENTDOS.SYS"), "Rainbow Sentinel DOS Driver"),
new(new FilePathMatch("SENTINEL.386"), "Rainbow Sentinel Windows 3.1 Driver"),
new(new FilePathMatch("SNTALPHA.DLL"), "Rainbow Sentinel Windows NT Alpha Platform Driver"),
new(new FilePathMatch("SNTI386.DLL"), "Rainbow Sentinel Windows NT Intel Platform Driver"),
new(new FilePathMatch("SNTMIPS.DLL"), "Rainbow Sentinel Windows NT MIPS Platform Driver"),
new(new FilePathMatch("SNTPPC.DLL"), "Rainbow Sentinel Windows NT PowerPC Platform Driver"),
new(new FilePathMatch("NSRVDI.EXE"), "Rainbow NetSentinel Server for DOS"),
new(new FilePathMatch("NSRVDN.EXE"), "Rainbow NetSentinel Server for DOS"),
new(new FilePathMatch("NSRVNI.NLM"), "Rainbow NetSentinel Server for Novell NetWare"),
new(new FilePathMatch("NSRVOM.EXE"), "Rainbow NetSentinel Server for OS/2"),
new(new FilePathMatch("NSRVGX.EXE"), "Rainbow NetSentinel Server for Win32"),
// Found in IA item "czchip199707cd".
new(new List<PathMatch>
{
new FilePathMatch("DOSMON.EXE"),
new FilePathMatch("FIND.EXE"),
new FilePathMatch("NCEDIT.EXE"),
new FilePathMatch("NETEVAL.EXE"),
}, "Rainbow NetSentinel Monitor for DOS"),
// Found in IA item "czchip199707cd".
new(new List<PathMatch>
{
new FilePathMatch("OS2MON.EXE"),
new FilePathMatch("RHPANELP.DLL"),
}, "Rainbow NetSentinel Monitor for OS/2"),
// Found in IA item "czchip199707cd".
new(new List<PathMatch>
{
new FilePathMatch("MAPFILE.TXT"),
new FilePathMatch("NKWIN32.DLL"),
new FilePathMatch("NSLMS32.DLL"),
new FilePathMatch("W32EDITD.EXE"),
new FilePathMatch("W32EDITO.EXE"),
new FilePathMatch("WINMON.DOC"),
new FilePathMatch("WINMON.EXE"),
new FilePathMatch("WINMON.HLP"),
new FilePathMatch("WMON_DOC.EXE"),
}, "Rainbow NetSentinel Monitor for Win32"),
// Found in IA item "chip-cds-2001-08".
// File names for Rainbow Sentinel files sometimes found in ".cab" files.
new(new FilePathMatch("F194_rnbovdd.dll.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F195_sentinel.sys.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F225_sentinel.hlp.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F227_snti386.dll.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F288_sentinel.vxd.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F317_sentstrt.exe.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F344_sentw9x.hlp.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F481_SetupSysDriver.exe.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F766_SentinelDriverInstall_Start.htm.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
};
return MatchUtil.GetAllMatches(files, matchers, any: true);
@@ -140,8 +381,12 @@ namespace BinaryObjectScanner.Protection
{
var matchers = new List<PathMatchSet>
{
// The Parallel Port driver for Rainbow Sentinel (https://www.rainbow.com.my/document/endusertroubleshooting.pdf).
// Unforutnately, the file name overlaps with a file used by Clam Sentinel (https://clamsentinel.sourceforge.net/).
// TODO: Add LE check for "SENTINEL.VXD" once LE checks are implemented.
// new(new FilePathMatch("SENTINEL.VXD"), "Rainbow Sentinel"),
// Found in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]" and IA item "auto-cad-r14-cdrom".
new(new FilePathMatch("SENTINEL.VXD"), "Rainbow Sentinel"),
new(new FilePathMatch("SENTSTRT.EXE"), "Rainbow Sentinel"),
new(new FilePathMatch("SENTW95.DLL"), "Rainbow Sentinel"),
new(new FilePathMatch("SENTW95.EXE"), "Rainbow Sentinel"),
@@ -163,6 +408,31 @@ namespace BinaryObjectScanner.Protection
new(new FilePathMatch("RainbowSentinel.386"), "Rainbow Sentinel"),
new(new FilePathMatch("SX32W.DL_"), "Rainbow Sentinel"),
new(new FilePathMatch("SX32W.DLL"), "Rainbow Sentinel"),
// Found in IA item "pcwkcd-1296".
new(new FilePathMatch("SP32W.DLL"), "Rainbow Sentinel"),
new(new FilePathMatch("SSWIN.DLL"), "Rainbow Sentinel"),
// Found in IA item "czchip199707cd".
new(new FilePathMatch("SENTINEL.DPP"), "Rainbow Sentinel OS/2 Installation Script"),
new(new FilePathMatch("SENTDOS.SYS"), "Rainbow Sentinel DOS Driver"),
new(new FilePathMatch("SENTINEL.386"), "Rainbow Sentinel Windows 3.1 Driver"),
new(new FilePathMatch("SNTALPHA.DLL"), "Rainbow Sentinel Windows NT Alpha Platform Driver"),
new(new FilePathMatch("SNTI386.DLL"), "Rainbow Sentinel Windows NT Intel Platform Driver"),
new(new FilePathMatch("SNTMIPS.DLL"), "Rainbow Sentinel Windows NT MIPS Platform Driver"),
new(new FilePathMatch("SNTPPC.DLL"), "Rainbow Sentinel Windows NT PowerPC Platform Driver"),
// Found in IA item "chip-cds-2001-08".
// File names for Rainbow Sentinel files sometimes found in ".cab" files.
new(new FilePathMatch("F194_rnbovdd.dll.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F195_sentinel.sys.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F225_sentinel.hlp.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F227_snti386.dll.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F288_sentinel.vxd.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F317_sentstrt.exe.B391C188_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F344_sentw9x.hlp.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F481_SetupSysDriver.exe.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
new(new FilePathMatch("F766_SentinelDriverInstall_Start.htm.B391C18A_6953_11D4_82CB_00D0B72E1DB9"), "Rainbow Sentinel"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);

View File

@@ -0,0 +1,84 @@
using System;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// Roxxe was a Czech DRM. It appears to have been a simple disc check that also relied on unusual disc manufacturing and dummy files to attempt to prevent copying.
///
/// DRML: https://github.com/TheRogueArchivist/DRML/blob/main/entries/Roxxe/Roxxe.md
/// </summary>
public class Roxxe : IPathCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// Get the code/CODE section strings, if they exist
var strs = pex.GetFirstSectionStrings("code") ?? pex.GetFirstSectionStrings("CODE");
if (strs != null)
{
// Found in "Owar.exe" in IA item "game4u-22-cd".
if (strs.Any(s => s.Contains("TRCHANGER.INI")))
return "Roxxe";
}
// Get the .rsrc section strings, if they exist
// TODO: Check for these strings specifically within the application-defined resource that they're found in, not just the generic resource section.
strs = pex.GetFirstSectionStrings(".rsrc");
if (strs != null)
{
// Found in "Owar.exe" in IA items "game4u-22-cd" and "original-war".
// These checks are less reliable, as they are still found in a version of the game that appears to have patched out Roxxe (the version present in IA item "original-war").
if (strs.Any(s => s.Contains("PRRT01")))
return "Roxxe (Possibly remnants)";
if (strs.Any(s => s.Contains("CommonPRRT")))
return "Roxxe (Possibly remnants)";
// Currently overmatches, will likely be a viable check when better Delphi executable parsing is available.
// if (strs.Any(s => s.Contains("roxe")))
// return "Roxxe (Possibly remnants)";
}
return null;
}
/// <inheritdoc/>
#if NET20 || NET35
public Queue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
#else
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
#endif
{
var matchers = new List<PathMatchSet>
{
// Files such as "TRCHANGER.INI" may be present, but haven't been found yet.
};
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string? CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
// Files such as "TRCHANGER.INI" may be present, but haven't been found yet.
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
}
}

View File

@@ -226,31 +226,40 @@ namespace BinaryObjectScanner.Protection
// These live in the MS-DOS stub, for some reason
private static string GetV7Version(PortableExecutable pex)
{
int index = 172; // 64 bytes for DOS stub, 236 bytes in total
try
{
int index = 172; // 64 bytes for DOS stub, 236 bytes in total
#if NETFRAMEWORK
byte[] bytes = new byte[4];
Array.Copy(pex.StubExecutableData, index, bytes, 0, 4);
byte[] bytes = new byte[4];
Array.Copy(pex.StubExecutableData, index, bytes, 0, 4);
#else
byte[] bytes = new ReadOnlySpan<byte>(pex.StubExecutableData, index, 4).ToArray();
byte[] bytes = new ReadOnlySpan<byte>(pex.StubExecutableData, index, 4).ToArray();
#endif
//SecuROM 7 new and 8
if (bytes[3] == 0x5C) // if (bytes[0] == 0xED && bytes[3] == 0x5C {
{
return $"{bytes[0] ^ 0xEA}.{bytes[1] ^ 0x2C:00}.{bytes[2] ^ 0x8:0000}";
//SecuROM 7 new and 8
if (bytes[3] == 0x5C) // if (bytes[0] == 0xED && bytes[3] == 0x5C {
{
return $"{bytes[0] ^ 0xEA}.{bytes[1] ^ 0x2C:00}.{bytes[2] ^ 0x8:0000}";
}
// SecuROM 7 old
else
{
index = 58; // 64 bytes for DOS stub, 122 bytes in total
#if NETFRAMEWORK
bytes = new byte[2];
Array.Copy(pex.StubExecutableData, index, bytes, 0, 2);
#else
bytes = new ReadOnlySpan<byte>(pex.StubExecutableData, index, 2).ToArray();
#endif
return $"7.{bytes[0] ^ 0x10:00}.{bytes[1] ^ 0x10:0000}"; //return "7.01-7.10"
}
}
// SecuROM 7 old
else
catch (ArgumentException)
{
index = 58; // 64 bytes for DOS stub, 122 bytes in total
#if NETFRAMEWORK
bytes = new byte[2];
Array.Copy(pex.StubExecutableData, index, bytes, 0, 2);
#else
bytes = new ReadOnlySpan<byte>(pex.StubExecutableData, index, 2).ToArray();
#endif
return $"7.{bytes[0] ^ 0x10:00}.{bytes[1] ^ 0x10:0000}"; //return "7.01-7.10"
// If SecuROM is stripped, the MS-DOS stub might be shorter.
// We then know that SecuROM -was- there, but we don't know what exact version.
return "7 remnants";
}
}

View File

@@ -457,7 +457,7 @@ namespace BinaryObjectScanner
AppendToDictionary(protections, fileName, subProtections.Values.ToArray());
// If we have any extractable packers
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream);
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, mz);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
}
@@ -471,7 +471,7 @@ namespace BinaryObjectScanner
AppendToDictionary(protections, fileName, subProtections.Values.ToArray());
// If we have any extractable packers
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream);
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, lex);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
}
@@ -485,7 +485,7 @@ namespace BinaryObjectScanner
AppendToDictionary(protections, fileName, subProtections.Values.ToArray());
// If we have any extractable packers
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream);
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, nex);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
}
@@ -499,7 +499,7 @@ namespace BinaryObjectScanner
AppendToDictionary(protections, fileName, subProtections.Values.ToArray());
// If we have any extractable packers
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream);
var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, pex);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
}
@@ -512,12 +512,12 @@ namespace BinaryObjectScanner
/// </summary>
/// <param name="classes">Set of classes returned from Exectuable scans</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="stream">Stream to scan the contents of</param>
/// <param name="mz">MSDOS to scan the contents of</param>
/// <returns>Set of protections found from extraction, null on error</returns>
#if NET20 || NET35
private Dictionary<string, Queue<string>>? HandleExtractableProtections<T>(Dictionary<T, string>.KeyCollection? classes, string fileName, Stream stream)
private Dictionary<string, Queue<string>>? HandleExtractableProtections<T>(Dictionary<T, string>.KeyCollection? classes, string fileName, MSDOS mz)
#else
private ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractableProtections(IEnumerable<object>? classes, string fileName, Stream stream)
private ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractableProtections(IEnumerable<object>? classes, string fileName, MSDOS mz)
#endif
{
// If we have an invalid set of classes
@@ -532,7 +532,7 @@ namespace BinaryObjectScanner
#endif
// If we have any extractable packers
var extractables = classes.Where(c => c is IExtractable).Select(c => c as IExtractable);
var extractables = classes.Where(c => c is IExtractableMSDOSExecutable).Select(c => c as IExtractableMSDOSExecutable);
#if NET20 || NET35
foreach (var extractable in extractables)
#else
@@ -548,7 +548,166 @@ namespace BinaryObjectScanner
#endif
// Get the protection for the class, if possible
var extractedProtections = Handler.HandleExtractable(extractable, fileName, stream, this);
var extractedProtections = Handler.HandleExtractable(extractable, fileName, mz, this);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
#if NET20 || NET35
}
#else
});
#endif
return protections;
}
/// <summary>
/// Handle extractable protections, such as executable packers
/// </summary>
/// <param name="classes">Set of classes returned from Exectuable scans</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="lex">LinearExecutable to scan the contents of</param>
/// <returns>Set of protections found from extraction, null on error</returns>
#if NET20 || NET35
private Dictionary<string, Queue<string>>? HandleExtractableProtections<T>(Dictionary<T, string>.KeyCollection? classes, string fileName, LinearExecutable lex)
#else
private ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractableProtections(IEnumerable<object>? classes, string fileName, LinearExecutable lex)
#endif
{
// If we have an invalid set of classes
if (classes == null || !classes.Any())
return null;
// Create the output dictionary
#if NET20 || NET35
var protections = new Dictionary<string, Queue<string>>();
#else
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
#endif
// If we have any extractable packers
var extractables = classes.Where(c => c is IExtractableLinearExecutable).Select(c => c as IExtractableLinearExecutable);
#if NET20 || NET35
foreach (var extractable in extractables)
#else
Parallel.ForEach(extractables, extractable =>
#endif
{
// If we have an invalid extractable somehow
if (extractable == null)
#if NET20 || NET35
continue;
#else
return;
#endif
// Get the protection for the class, if possible
var extractedProtections = Handler.HandleExtractable(extractable, fileName, lex, this);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
#if NET20 || NET35
}
#else
});
#endif
return protections;
}
/// <summary>
/// Handle extractable protections, such as executable packers
/// </summary>
/// <param name="classes">Set of classes returned from Exectuable scans</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="nex">NewExecutable to scan the contents of</param>
/// <returns>Set of protections found from extraction, null on error</returns>
#if NET20 || NET35
private Dictionary<string, Queue<string>>? HandleExtractableProtections<T>(Dictionary<T, string>.KeyCollection? classes, string fileName, NewExecutable nex)
#else
private ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractableProtections(IEnumerable<object>? classes, string fileName, NewExecutable nex)
#endif
{
// If we have an invalid set of classes
if (classes == null || !classes.Any())
return null;
// Create the output dictionary
#if NET20 || NET35
var protections = new Dictionary<string, Queue<string>>();
#else
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
#endif
// If we have any extractable packers
var extractables = classes.Where(c => c is IExtractableNewExecutable).Select(c => c as IExtractableNewExecutable);
#if NET20 || NET35
foreach (var extractable in extractables)
#else
Parallel.ForEach(extractables, extractable =>
#endif
{
// If we have an invalid extractable somehow
if (extractable == null)
#if NET20 || NET35
continue;
#else
return;
#endif
// Get the protection for the class, if possible
var extractedProtections = Handler.HandleExtractable(extractable, fileName, nex, this);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
#if NET20 || NET35
}
#else
});
#endif
return protections;
}
/// <summary>
/// Handle extractable protections, such as executable packers
/// </summary>
/// <param name="classes">Set of classes returned from Exectuable scans</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="pex">PortableExecutable to scan the contents of</param>
/// <returns>Set of protections found from extraction, null on error</returns>
#if NET20 || NET35
private Dictionary<string, Queue<string>>? HandleExtractableProtections<T>(Dictionary<T, string>.KeyCollection? classes, string fileName, PortableExecutable pex)
#else
private ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractableProtections(IEnumerable<object>? classes, string fileName, PortableExecutable pex)
#endif
{
// If we have an invalid set of classes
if (classes == null || !classes.Any())
return null;
// Create the output dictionary
#if NET20 || NET35
var protections = new Dictionary<string, Queue<string>>();
#else
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
#endif
// If we have any extractable packers
var extractables = classes.Where(c => c is IExtractablePortableExecutable).Select(c => c as IExtractablePortableExecutable);
#if NET20 || NET35
foreach (var extractable in extractables)
#else
Parallel.ForEach(extractables, extractable =>
#endif
{
// If we have an invalid extractable somehow
if (extractable == null)
#if NET20 || NET35
continue;
#else
return;
#endif
// Get the protection for the class, if possible
var extractedProtections = Handler.HandleExtractable(extractable, fileName, pex, this);
if (extractedProtections != null)
AppendToDictionary(protections, extractedProtections);
#if NET20 || NET35

View File

@@ -1,53 +0,0 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace BinaryObjectScanner.Utilities
{
/// <summary>
/// Data hashing methods
/// </summary>
public static class Hashing
{
/// <summary>
/// Get the SHA1 hash of a file, if possible
/// </summary>
/// <param name="path">Path to the file to be hashed</param>
/// <returns>SHA1 hash as a string on success, null on error</returns>
public static string? GetFileSHA1(string? path)
{
if (string.IsNullOrEmpty(path))
return null;
try
{
var sha1 = SHA1.Create();
using (Stream fileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byte[] buffer = new byte[32768];
while (true)
{
int bytesRead = fileStream.Read(buffer, 0, 32768);
if (bytesRead == 32768)
{
sha1.TransformBlock(buffer, 0, bytesRead, null, 0);
}
else
{
sha1.TransformFinalBlock(buffer, 0, bytesRead);
break;
}
}
}
string hash = BitConverter.ToString(sha1.Hash!);
hash = hash.Replace("-", string.Empty);
return hash;
}
catch
{
return null;
}
}
}
}

View File

@@ -1,16 +1,17 @@
# BinaryObjectScanner
# Binary Object Scanner
[![Build status](https://ci.appveyor.com/api/projects/status/gmdft5bk1h8a1c31?svg=true)](https://ci.appveyor.com/project/mnadareski/BinaryObjectScanner)
[![Build Test](https://github.com/SabreTools/BinaryObjectScanner/actions/workflows/build_test.yml/badge.svg)](https://github.com/SabreTools/BinaryObjectScanner/actions/workflows/build_test.yml)
[![Nuget Pack](https://github.com/SabreTools/BinaryObjectScanner/actions/workflows/build_nupkg.yml/badge.svg)](https://github.com/SabreTools/BinaryObjectScanner/actions/workflows/build_nupkg.yml)
C# protection, packer, and archive scanning library. This currently compiles as a library so it can be used in any C# application. A reference application called `Test` is also included to demonstrate the abilities of the library. For an example of a program implementing the library, see [MPF](https://github.com/SabreTools/MPF).
The following libraries (or ports thereof) are used for file handling:
The following non-project libraries (or ports thereof) are used for file handling:
- [LessIO](https://github.com/activescott/LessIO) - Used by libmspack4n for IO handling
- [libmspack4n](https://github.com/activescott/libmspack4n) MS-CAB extraction [Unused in .NET Frawework 2.0/3.5/4.0 and non-Windows builds due to Windows-specific libraries]
- [openmcdf](https://github.com/ironfede/openmcdf) - MSI extraction
- [SharpCompress](https://github.com/adamhathcock/sharpcompress) - Common archive format extraction
- [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) - zlib-based extraction
- [StormLibSharp](https://github.com/robpaveza/stormlibsharp) - MoPaQ extraction [Unused in .NET Frawework 2.0/3.5/4.0 and non-Windows builds due to Windows-specific libraries]
- [UnshieldSharp](https://github.com/mnadareski/UnshieldSharp) - InstallShield CAB extraction
- [WiseUnpacker](https://github.com/mnadareski/WiseUnpacker) - Wise Installer extraction
@@ -93,6 +94,7 @@ Below is a list of protections detected by BinaryObjectScanner. The two columns
| Rainbow Sentinel | True | True | |
| Ring PROTECH / ProRing | True | True | Partially unconfirmed² |
| RipGuard | True | True | Partially unconfirmed² |
| Roxxe | True | False | |
| SafeDisc / SafeCast | True | True | Can't distinguish between some versions of SafeDisc and SafeCast |
| SafeLock | False | True | |
| SecuROM | True | True | v8.x and White Label detected partially² |

View File

@@ -19,10 +19,8 @@ using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Xz;
#endif
#if NET40_OR_GREATER || NETCOREAPP
using UnshieldSharp.Archive;
using UnshieldSharp.Cabinet;
#endif
namespace Test
{
@@ -320,7 +318,6 @@ namespace Test
Console.WriteLine("Extracting InstallShield Archive V3 contents");
Console.WriteLine();
#if NET40_OR_GREATER || NETCOREAPP
// If the cab file itself fails
try
{
@@ -357,7 +354,6 @@ namespace Test
Console.WriteLine($"Something went wrong extracting InstallShield Archive V3: {ex}");
Console.WriteLine();
}
#endif
}
// IS-CAB archive
@@ -367,10 +363,6 @@ namespace Test
Console.WriteLine("Extracting IS-CAB contents");
Console.WriteLine();
#if NET20 || NET35 || NET40
Console.WriteLine("Extraction is not supported for this framework!");
Console.WriteLine();
#else
// If the cab file itself fails
try
{
@@ -405,7 +397,6 @@ namespace Test
Console.WriteLine($"Something went wrong extracting IS-CAB: {ex}");
Console.WriteLine();
}
#endif
}
#if ((NETFRAMEWORK && !NET20 && !NET35 && !NET40) || NETCOREAPP) && WIN

View File

@@ -49,60 +49,68 @@ namespace Test
{
Console.WriteLine($"Attempting to print info for {file}");
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// Read the first 8 bytes
byte[]? magic = stream.ReadBytes(8);
stream.Seek(0, SeekOrigin.Begin);
// Get the file type
SupportedFileType ft = FileTypes.GetFileType(magic ?? []);
if (ft == SupportedFileType.UNKNOWN)
try
{
string extension = Path.GetExtension(file).TrimStart('.');
ft = FileTypes.GetFileType(extension);
}
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// Print out the file format
Console.WriteLine($"File format found: {ft}");
// Read the first 8 bytes
byte[]? magic = stream.ReadBytes(8);
stream.Seek(0, SeekOrigin.Begin);
// Setup the wrapper to print
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
// Get the file type
SupportedFileType ft = FileTypes.GetFileType(magic ?? []);
if (ft == SupportedFileType.UNKNOWN)
{
string extension = Path.GetExtension(file).TrimStart('.');
ft = FileTypes.GetFileType(extension);
}
// If we don't have a wrapper
if (wrapper == null)
{
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
Console.WriteLine();
return;
}
// Print out the file format
Console.WriteLine($"File format found: {ft}");
// Print the wrapper name
Console.WriteLine($"{wrapper.Description()} wrapper created successfully!");
// Setup the wrapper to print
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
// Get the base info output name
string filenameBase = $"info-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}";
// If we don't have a wrapper
if (wrapper == null)
{
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
Console.WriteLine();
return;
}
// Print the wrapper name
Console.WriteLine($"{wrapper.Description()} wrapper created successfully!");
// Get the base info output name
string filenameBase = $"info-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}";
#if NET6_0_OR_GREATER
// If we have the JSON flag
if (json)
{
// Create the output data
string serializedData = wrapper.ExportJSON();
Console.WriteLine(serializedData);
// If we have the JSON flag
if (json)
{
// Create the output data
string serializedData = wrapper.ExportJSON();
Console.WriteLine(serializedData);
// Write the output data
using var jsw = new StreamWriter(File.OpenWrite($"{filenameBase}.json"));
jsw.WriteLine(serializedData);
}
// Write the output data
using var jsw = new StreamWriter(File.OpenWrite($"{filenameBase}.json"));
jsw.WriteLine(serializedData);
}
#endif
// Create the output data
var builder = wrapper.PrettyPrint();
Console.WriteLine(builder);
// Create the output data
var builder = wrapper.PrettyPrint();
Console.WriteLine(builder);
// Write the output data
using var sw = new StreamWriter(File.OpenWrite($"{filenameBase}.txt"));
sw.WriteLine(builder.ToString());
// Write the output data
using var sw = new StreamWriter(File.OpenWrite($"{filenameBase}.txt"));
sw.WriteLine(builder.ToString());
}
catch (Exception ex)
{
Console.WriteLine(debug ? ex : "[Exception opening file, please try again]");
Console.WriteLine();
}
}
#region Printing Implementations

View File

@@ -23,17 +23,17 @@
<!-- Support for old .NET versions -->
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`))">
<PackageReference Include="OpenMcdf" Version="2.3.0" />
<PackageReference Include="UnshieldSharp" Version="1.7.2" />
<PackageReference Include="OpenMcdf" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.Compression" Version="0.3.0" />
<PackageReference Include="SabreTools.IO" Version="1.3.0" />
<PackageReference Include="SabreTools.Matching" Version="1.3.0" />
<PackageReference Include="SabreTools.Models" Version="1.3.0" />
<PackageReference Include="SabreTools.Printing" Version="1.3.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.3.2" />
<PackageReference Include="SabreTools.Compression" Version="0.4.2" />
<PackageReference Include="SabreTools.IO" Version="1.3.3" />
<PackageReference Include="SabreTools.Matching" Version="1.3.1" />
<PackageReference Include="SabreTools.Models" Version="1.4.2" />
<PackageReference Include="SabreTools.Printing" Version="1.3.5" />
<PackageReference Include="SabreTools.Serialization" Version="1.5.0" />
<PackageReference Include="UnshieldSharp" Version="1.7.4" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,5 @@
# version format
version: 3.0.0-{build}
version: 3.1.0-{build}
# pull request template
pull_requests:
@@ -15,33 +15,7 @@ install:
# build step
build_script:
- dotnet restore
# Debug
- dotnet publish Test\Test.csproj -f net8.0 -r win-x86 -c Debug --self-contained true -p:PublishSingleFile=true
- dotnet publish Test\Test.csproj -f net8.0 -r win-x64 -c Debug --self-contained true -p:PublishSingleFile=true
- dotnet publish Test\Test.csproj -f net8.0 -r linux-x64 -c Debug --self-contained true -p:PublishSingleFile=true
- dotnet publish Test\Test.csproj -f net8.0 -r osx-x64 -c Debug --self-contained true -p:PublishSingleFile=true
# Release
- dotnet publish Test\Test.csproj -f net8.0 -r win-x86 -c Release --self-contained true -p:PublishSingleFile=true -p:DebugSymbols=false
- dotnet publish Test\Test.csproj -f net8.0 -r win-x64 -c Release --self-contained true -p:PublishSingleFile=true -p:DebugSymbols=false
- dotnet publish Test\Test.csproj -f net8.0 -r linux-x64 -c Release --self-contained true -p:PublishSingleFile=true -p:DebugSymbols=false
- dotnet publish Test\Test.csproj -f net8.0 -r osx-x64 -c Release --self-contained true -p:PublishSingleFile=true -p:DebugSymbols=false
# Nuget Package
- dotnet pack BinaryObjectScanner\BinaryObjectScanner.csproj --output %APPVEYOR_BUILD_FOLDER%
# post-build script
after_build:
- cd %APPVEYOR_BUILD_FOLDER%\Test\bin\Debug\net8.0\win-x86\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_win-x86.zip *
- cd %APPVEYOR_BUILD_FOLDER%\Test\bin\Debug\net8.0\win-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_win-x64.zip *
- cd %APPVEYOR_BUILD_FOLDER%\Test\bin\Debug\net8.0\linux-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_linux-x64.zip *
- cd %APPVEYOR_BUILD_FOLDER%\Test\bin\Debug\net8.0\osx-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_osx-x64.zip *
- dotnet build
# success/failure tracking
on_success:
@@ -49,17 +23,4 @@ on_success:
- ps: ./send.ps1 success $env:WEBHOOK_URL
on_failure:
- ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
- ps: ./send.ps1 failure $env:WEBHOOK_URL
# artifact linking
artifacts:
- path: BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_win-x86.zip
name: BinaryObjectScanner (.NET 8.0, Windows x86)
- path: BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_win-x64.zip
name: BinaryObjectScanner (.NET 8.0, Windows x64)
- path: BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_linux-x64.zip
name: BinaryObjectScanner (.NET 8.0, Linux x64)
- path: BinaryObjectScanner_%APPVEYOR_REPO_COMMIT%_net8.0_osx-x64.zip
name: BinaryObjectScanner (.NET 8.0, OSX x64)
- path: '*.nupkg'
name: Nuget Packages
- ps: ./send.ps1 failure $env:WEBHOOK_URL