Compare commits

..

35 Commits
3.4.6 ... 3.6.0

Author SHA1 Message Date
Matt Nadareski
384db5dcf4 Bump version 2026-03-17 19:13:16 -04:00
Matt Nadareski
b96af0a3c2 Fix copy-paste issue with GCF test 2026-03-17 14:36:49 -04:00
Matt Nadareski
95f152c105 Add GCF tests 2026-03-17 14:30:34 -04:00
HeroponRikiBestest
ff02fdb5c3 Add BOS gcf output (#413) 2026-03-17 14:28:29 -04:00
Matt Nadareski
53d4757e5c Fix CEG test 2026-03-17 13:20:30 -04:00
Matt Nadareski
0181917850 Minor cleanup 2026-03-17 13:10:59 -04:00
HeroponRikiBestest
008d53ad8a Final Major CEG pass (#411)
* Reorganize Steam3 section in a more sensibible way, add/clean up comments.

* Finally

* Cleaned up a few things I missed, added some more comments.

* Reformat

* The future is now, old man.
Also added more comments, and removed a TODO that needed to be moved to a github question.

* Forgot to use the GUIDs for some reason

* One last TODO for something to think about in the future after this PR.

* The lack of spacing was bothering me, forgot it before.

* Updated table with more steam2 storages. Formatting changed slightly because I made a more advanced script that allows me to update the table more easily and reliably locally.

* fix spacing
2026-03-17 13:06:11 -04:00
Matt Nadareski
be5169b935 Update packages 2026-03-15 20:47:59 -04:00
HeroponRikiBestest
245e840063 Add iso-level ripguard detection. (#412) 2026-03-07 15:19:00 -05:00
HeroponRikiBestest
9be63e9e9a Change version output for protectdisc iso scan (#410)
Existing one was found to be inaccurate
2026-02-11 18:44:30 -05:00
HeroponRikiBestest
57797496cf Add steam3 samples to CEG (#409)
* forgot these 2 last time

* :(

* make todos more professional
2026-02-11 08:10:56 -05:00
HeroponRikiBestest
8490d3804f Add all my current steam2 ceg samples (#408)
* Add current steam2 entries

* Finish this round
2026-02-09 19:50:31 -05:00
HeroponRikiBestest
8df070ad3d Make CEG actually work (#407)
I wrote the original line wrong without realizing somehow.
2026-02-09 09:25:01 -05:00
HeroponRikiBestest
6cc6f66c95 Add CEG protection detection (#406)
* Initial work

* Finish PR

* Update test count
2026-02-07 18:07:36 -05:00
HeroponRikiBestest
fbee5a1c52 Add RC hash (#405)
Small update, found one more, needed to update the dictionary
2026-02-04 19:52:50 -05:00
Matt Nadareski
4807520169 This doesn't need private set 2026-01-30 22:32:44 -05:00
Matt Nadareski
91da3068b6 Clean up now-empty region 2026-01-30 22:31:25 -05:00
Matt Nadareski
c9316651c5 Use auto properties for static check lists 2026-01-30 22:30:59 -05:00
Matt Nadareski
2b3917a1ee These can't be null anymore 2026-01-30 22:15:34 -05:00
Matt Nadareski
efef8d494e More cleanup 2026-01-27 09:30:58 -05:00
Matt Nadareski
6c1efccb00 Fix incorrect formatting issue 2026-01-25 17:30:54 -05:00
Matt Nadareski
0d2f81773d Add editorconfig, fix issues 2026-01-25 17:27:42 -05:00
Morlit55
7058c7a150 More info on Dinamic Multimedia Protection (#404)
* More info on Dinamic Multimedia Protection

Added comments regarding related DRM and patents for Dinamic Multimedia Protection.

* Removed MPO references

MPO ring protection has since been proven to be unrelated.
2026-01-23 11:54:59 -05:00
HeroponRikiBestest
417c1f8208 Fix scanning for Solidshield 3.0.0.0 DLLs. (#403)
* Fix scanning for Solidshield 3.0.0.0 DLLs.

* Add curly braces for multiline if
2026-01-10 16:10:58 -05:00
HeroponRikiBestest
82fc850b57 minor starforce fix (#402)
* minor starforce fix

* combine regex checks
2025-12-29 22:58:12 -05:00
HeroponRikiBestest
973b0118b6 Fix minor nested json issue (#398) 2025-12-02 12:15:39 -05:00
HeroponRikiBestest
c2368661cb Fix oversight where Scanner.cs won't scan for files if an archive extracts to one or more directories (#397)
* Scanner.cs won't scan for files if an archive extracts to a directory

* use fsentries
2025-11-29 14:48:47 -05:00
Matt Nadareski
05bddd6005 Bump version 2025-11-25 08:40:36 -05:00
Matt Nadareski
777232301d Update Serialization to 2.2.1 2025-11-25 07:56:38 -05:00
Matt Nadareski
41a705c343 Add support for .NET 10 2025-11-24 11:57:17 -05:00
HeroponRikiBestest
805beb3418 Improve starforce keyless detection (#396) 2025-11-17 22:00:44 -05:00
HeroponRikiBestest
cc7592e07a consistent alpharom stylization (#395)
I forgot that Alpha-ROM is stylized with a dash. Mainly PRing so it'll also be consistent with how it already is on redump.
2025-11-17 12:29:26 -05:00
HeroponRikiBestest
41ad8adf6e Small TAGES and AlphaROM improvements (#394)
* Small improvement to early TAGES detection to eliminate small handful of false positives. Also threw in a note about alpharom

* Improve alpharom handling

* Initial round of fixes
2025-11-16 15:55:32 -05:00
HeroponRikiBestest
201badd434 Update RC dictionary name now that one of the entries is in redump (#393) 2025-11-14 07:28:46 -05:00
Matt Nadareski
f58003b3fc Ensure JSON added to help print 2025-11-13 13:59:25 -05:00
127 changed files with 10435 additions and 578 deletions

167
.editorconfig Normal file
View File

@@ -0,0 +1,167 @@
# top-most EditorConfig file
root = true
# C# files
[*.cs]
# Indentation and spacing
charset = utf-8
indent_size = 4
indent_style = space
tab_width = 4
trim_trailing_whitespace = true
# New line preferences
end_of_line = lf
insert_final_newline = true
max_line_length = unset
# using directive preferences
csharp_using_directive_placement = outside_namespace
dotnet_diagnostic.IDE0005.severity = error
# Code-block preferences
csharp_style_namespace_declarations = block_scoped
csharp_style_prefer_method_group_conversion = true
csharp_style_prefer_top_level_statements = false
# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_inlined_variable_declaration = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable
dotnet_diagnostic.IDE0001.severity = warning
dotnet_diagnostic.IDE0002.severity = warning
dotnet_diagnostic.IDE0004.severity = warning
dotnet_diagnostic.IDE0010.severity = error
dotnet_diagnostic.IDE0051.severity = warning
dotnet_diagnostic.IDE0052.severity = warning
dotnet_diagnostic.IDE0072.severity = warning
dotnet_diagnostic.IDE0080.severity = warning
dotnet_diagnostic.IDE0100.severity = error
dotnet_diagnostic.IDE0110.severity = error
dotnet_diagnostic.IDE0120.severity = warning
dotnet_diagnostic.IDE0121.severity = warning
dotnet_diagnostic.IDE0240.severity = error
dotnet_diagnostic.IDE0241.severity = error
dotnet_style_coalesce_expression = true
dotnet_style_namespace_match_folder = false
dotnet_style_null_propagation = true
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_compound_assignment = true
csharp_style_prefer_simple_property_accessors = true
dotnet_style_prefer_simplified_interpolation = true
dotnet_style_prefer_simplified_boolean_expressions = true
csharp_style_prefer_unbound_generic_type_in_nameof = true
# Field preferences
dotnet_diagnostic.IDE0044.severity = warning
dotnet_style_readonly_field = true
# Language keyword vs. framework types preferences
dotnet_diagnostic.IDE0049.severity = error
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true
# Modifier preferences
csharp_prefer_static_local_function = true
csharp_style_prefer_readonly_struct = true
dotnet_diagnostic.IDE0036.severity = warning
dotnet_diagnostic.IDE0040.severity = error
dotnet_diagnostic.IDE0380.severity = error
dotnet_style_require_accessibility_modifiers = always
# New-line preferences
dotnet_diagnostic.IDE2000.severity = warning
dotnet_diagnostic.IDE2002.severity = warning
dotnet_diagnostic.IDE2003.severity = warning
dotnet_diagnostic.IDE2004.severity = warning
dotnet_diagnostic.IDE2005.severity = warning
dotnet_diagnostic.IDE2006.severity = warning
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
dotnet_style_allow_multiple_blank_lines_experimental = false
dotnet_style_allow_statement_immediately_after_block_experimental = false
# Null-checking preferences
csharp_style_conditional_delegate_call = true
# Parameter preferences
dotnet_code_quality_unused_parameters = all
dotnet_diagnostic.IDE0280.severity = error
# Parentheses preferences
dotnet_diagnostic.IDE0047.severity = warning
dotnet_diagnostic.IDE0048.severity = warning
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = always_for_clarity
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
# Pattern-matching preferences
dotnet_diagnostic.IDE0019.severity = warning
dotnet_diagnostic.IDE0020.severity = warning
dotnet_diagnostic.IDE0038.severity = warning
dotnet_diagnostic.IDE0066.severity = none
dotnet_diagnostic.IDE0083.severity = warning
dotnet_diagnostic.IDE0260.severity = warning
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
# this. and Me. preferences
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
# var preferences
csharp_style_var_for_built_in_types = false
csharp_style_var_when_type_is_apparent = true
# .NET formatting options
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
# C# formatting options
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false

View File

@@ -1,48 +1,48 @@
name: Build and Test
on:
push:
branches: [ "master" ]
push:
branches: ["master"]
jobs:
build:
runs-on: ubuntu-latest
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh -d
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Update rolling tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -f rolling
git push origin :refs/tags/rolling || true
git push origin rolling --force
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: "*.nupkg,*.snupkg,*.zip"
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh -d
- name: Update rolling tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -f rolling
git push origin :refs/tags/rolling || true
git push origin rolling --force
- name: Upload to rolling
uses: ncipollo/release-action@v1.20.0
with:
allowUpdates: True
artifacts: "*.nupkg,*.snupkg,*.zip"
body: "Last built commit: ${{ github.sha }}"
name: "Rolling Release"
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

View File

@@ -3,21 +3,21 @@ 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: |
6.0.x
8.0.x
9.0.x
- name: Build
run: dotnet build
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Run tests
run: dotnet test
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Build
run: dotnet build
- name: Run tests
run: dotnet test

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}/ProtectionScan/bin/Debug/net9.0/ProtectionScan.dll",
"program": "${workspaceFolder}/ProtectionScan/bin/Debug/net10.0/ProtectionScan.dll",
"args": [],
"cwd": "${workspaceFolder}/ProtectionScan",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
@@ -12,12 +12,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PackageReference Include="coverlet.collector" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="SabreTools.Serialization" Version="[2.1.0]" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="SabreTools.Serialization" Version="[2.3.0]" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -44,7 +44,7 @@ namespace BinaryObjectScanner.Test.Data
public void PortableExecutableCheckClasses_Populated()
{
var actual = StaticChecks.PortableExecutableCheckClasses;
Assert.Equal(105, actual.Length);
Assert.Equal(106, actual.Length);
}
}
}

View File

@@ -31,4 +31,3 @@ namespace BinaryObjectScanner.Test.FileType
}
}
}

View File

@@ -31,4 +31,3 @@ namespace BinaryObjectScanner.Test.FileType
}
}
}

View File

@@ -0,0 +1,33 @@
using System.IO;
using BinaryObjectScanner.FileType;
using Xunit;
namespace BinaryObjectScanner.Test.FileType
{
public class GCFTests
{
private static readonly SabreTools.Serialization.Wrappers.GCF wrapper
= new(new SabreTools.Data.Models.GCF.File(), new MemoryStream(new byte[1024]));
[Fact]
public void DetectFile_EmptyString_Null()
{
string file = string.Empty;
var detectable = new GCF(wrapper);
string? actual = detectable.Detect(file, includeDebug: false);
Assert.Null(actual);
}
[Fact]
public void DetectStream_EmptyStream_DefaultValue()
{
Stream? stream = new MemoryStream();
string file = string.Empty;
var detectable = new GCF(wrapper);
string? actual = detectable.Detect(stream, file, includeDebug: false);
Assert.Equal(" - 0 (v0)", actual);
}
}
}

View File

@@ -31,4 +31,3 @@ namespace BinaryObjectScanner.Test.FileType
}
}
}

View File

@@ -31,4 +31,3 @@ namespace BinaryObjectScanner.Test.FileType
}
}
}

View File

@@ -31,4 +31,3 @@ namespace BinaryObjectScanner.Test.FileType
}
}
}

View File

@@ -31,4 +31,3 @@ namespace BinaryObjectScanner.Test.FileType
}
}
}

View File

@@ -28,4 +28,3 @@ namespace BinaryObjectScanner.Test.FileType
}
}
}

View File

@@ -0,0 +1,22 @@
using System.IO;
using BinaryObjectScanner.Protection;
using Xunit;
namespace BinaryObjectScanner.Test.Protection
{
public class CEGTests
{
[Fact]
public void CheckPortableExecutableTest()
{
string file = "filename";
SabreTools.Data.Models.PortableExecutable.Executable model = new();
Stream source = new MemoryStream(new byte[1024]);
SabreTools.Serialization.Wrappers.PortableExecutable exe = new(model, source);
var checker = new CEG();
string? actual = checker.CheckExecutable(file, exe, includeDebug: false);
Assert.Null(actual);
}
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
@@ -13,7 +13,7 @@
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>3.4.6</Version>
<Version>3.6.0</Version>
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
<!-- Package Properties -->
@@ -34,11 +34,11 @@
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
@@ -46,12 +46,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="GrindCore.SharpCompress" Version="0.40.4-alpha" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="GrindCore.SharpCompress" Version="0.46.2" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="MinThreadingBridge" Version="0.11.4" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))" />
<PackageReference Include="SabreTools.Hashing" Version="[1.5.1]" />
<PackageReference Include="SabreTools.IO" Version="[1.8.0]" />
<PackageReference Include="SabreTools.Serialization" Version="[2.1.0]" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.10" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="SabreTools.Hashing" Version="[1.6.1]" />
<PackageReference Include="SabreTools.IO" Version="[1.9.1]" />
<PackageReference Include="SabreTools.Serialization" Version="[2.3.0]" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.5" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>
</Project>

View File

@@ -13,7 +13,7 @@ namespace BinaryObjectScanner.Data
/// <remarks>Handles the proper Add implementation</remarks>
public void Append(T key, string? value)
{
if (value == null || value.Trim().Length == 0)
if (value is null || value.Trim().Length == 0)
return;
#if NET20 || NET35

View File

@@ -26,7 +26,7 @@ namespace BinaryObjectScanner.Data
public void Append(string key, string? value)
{
// If the value is empty, don't add it
if (value == null || value.Trim().Length == 0)
if (value is null || value.Trim().Length == 0)
return;
EnsureKey(key);
@@ -47,7 +47,7 @@ namespace BinaryObjectScanner.Data
EnsureKey(key);
foreach (string value in values)
{
if (value == null || value.Trim().Length == 0)
if (value is null || value.Trim().Length == 0)
continue;
foreach (string subValue in ProcessProtectionString(value))
@@ -79,7 +79,7 @@ namespace BinaryObjectScanner.Data
public void Append(ProtectionDictionary? addition)
{
// If the dictionary is missing, just return
if (addition == null)
if (addition is null)
return;
// Loop through each of the addition keys and add accordingly
@@ -105,10 +105,11 @@ namespace BinaryObjectScanner.Data
string key = keys[i];
// If the key is empty, remove it
if (this[key] == null || this[key].Count == 0)
#if NET20 || NET35
if (this[key] is null || this[key].Count == 0)
Remove(key);
#else
if (this[key] is null || this[key].IsEmpty)
TryRemove(key, out _);
#endif
}
@@ -167,7 +168,11 @@ namespace BinaryObjectScanner.Data
continue;
// Otherwise, get the new key name and transfer over
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
string newKey = currentKey[pathToStrip!.Length..];
#else
string newKey = currentKey.Substring(pathToStrip!.Length);
#endif
this[newKey] = this[currentKey];
#if NET20 || NET35
Remove(currentKey);
@@ -184,13 +189,13 @@ namespace BinaryObjectScanner.Data
/// <param name="values">Queue to get data from</param>
private void AddRangeToKey(string key, IEnumerable<string> values)
{
if (values == null)
if (values is null)
return;
EnsureKey(key);
foreach (string value in values)
{
if (value == null || value.Trim().Length == 0)
if (value is null || value.Trim().Length == 0)
continue;
foreach (string subValue in ProcessProtectionString(value))
@@ -243,7 +248,11 @@ namespace BinaryObjectScanner.Data
var protections = new List<string>();
// If we have an indicator of multiple protections
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
if (protection!.Contains(';'))
#else
if (protection!.Contains(";"))
#endif
{
var splitProtections = protection.Split(';');
protections.AddRange(splitProtections);

View File

@@ -17,8 +17,8 @@ namespace BinaryObjectScanner.Data
{
get
{
contentCheckClasses ??= InitCheckClasses<IContentCheck>();
return contentCheckClasses;
field ??= InitCheckClasses<IContentCheck>();
return field;
}
}
@@ -29,8 +29,8 @@ namespace BinaryObjectScanner.Data
{
get
{
iso9660CheckClasses ??= InitCheckClasses<IDiskImageCheck<ISO9660>>();
return iso9660CheckClasses;
field ??= InitCheckClasses<IDiskImageCheck<ISO9660>>();
return field;
}
}
@@ -41,8 +41,8 @@ namespace BinaryObjectScanner.Data
{
get
{
linearExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<LinearExecutable>>();
return linearExecutableCheckClasses;
field ??= InitCheckClasses<IExecutableCheck<LinearExecutable>>();
return field;
}
}
@@ -53,8 +53,8 @@ namespace BinaryObjectScanner.Data
{
get
{
msdosExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<MSDOS>>();
return msdosExecutableCheckClasses;
field ??= InitCheckClasses<IExecutableCheck<MSDOS>>();
return field;
}
}
@@ -65,8 +65,8 @@ namespace BinaryObjectScanner.Data
{
get
{
newExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<NewExecutable>>();
return newExecutableCheckClasses;
field ??= InitCheckClasses<IExecutableCheck<NewExecutable>>();
return field;
}
}
@@ -77,8 +77,8 @@ namespace BinaryObjectScanner.Data
{
get
{
pathCheckClasses ??= InitCheckClasses<IPathCheck>();
return pathCheckClasses;
field ??= InitCheckClasses<IPathCheck>();
return field;
}
}
@@ -89,52 +89,15 @@ namespace BinaryObjectScanner.Data
{
get
{
portableExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<PortableExecutable>>();
return portableExecutableCheckClasses;
field ??= InitCheckClasses<IExecutableCheck<PortableExecutable>>();
return field;
}
private set;
}
#endregion
#region Internal Instances
/// <summary>
/// Cache for all IContentCheck types
/// </summary>
private static IContentCheck[]? contentCheckClasses;
/// <summary>
/// Cache for all IISOCheck<ISO9660> types
/// </summary>
private static IDiskImageCheck<ISO9660>[]? iso9660CheckClasses;
/// <summary>
/// Cache for all IExecutableCheck<LinearExecutable> types
/// </summary>
private static IExecutableCheck<LinearExecutable>[]? linearExecutableCheckClasses;
/// <summary>
/// Cache for all IExecutableCheck<MSDOS> types
/// </summary>
private static IExecutableCheck<MSDOS>[]? msdosExecutableCheckClasses;
/// <summary>
/// Cache for all IExecutableCheck<NewExecutable> types
/// </summary>
private static IExecutableCheck<NewExecutable>[]? newExecutableCheckClasses;
/// <summary>
/// Cache for all IPathCheck types
/// </summary>
private static IPathCheck[]? pathCheckClasses;
/// <summary>
/// Cache for all IExecutableCheck<PortableExecutable> types
/// </summary>
private static IExecutableCheck<PortableExecutable>[]? portableExecutableCheckClasses;
#endregion
#region Helpers
/// <summary>
@@ -150,7 +113,7 @@ namespace BinaryObjectScanner.Data
{
// Get information from the type param
string? interfaceName = typeof(T).FullName;
if (interfaceName == null)
if (interfaceName is null)
return [];
// If not all types can be loaded, use the ones that could be
@@ -173,7 +136,7 @@ namespace BinaryObjectScanner.Data
foreach (Type? type in assemblyTypes)
{
// Skip invalid types
if (type == null)
if (type is null)
continue;
// If the type isn't a class
@@ -187,7 +150,7 @@ namespace BinaryObjectScanner.Data
// Try to create a concrete instance of the type
var instance = (T?)Activator.CreateInstance(type);
if (instance != null)
if (instance is not null)
classTypes.Add(instance);
}

View File

@@ -1,5 +1,6 @@
using System.IO;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -1,5 +1,6 @@
using System.IO;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -1,6 +1,7 @@
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -4,6 +4,7 @@ using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -6,6 +6,7 @@ using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>
@@ -47,7 +48,7 @@ namespace BinaryObjectScanner.FileType
stream.Seek(0, SeekOrigin.Begin);
fileContent = stream.ReadBytes((int)stream.Length);
if (fileContent == null)
if (fileContent is null)
return protections;
}
catch (Exception ex)

View File

@@ -0,0 +1,35 @@
using System.IO;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// Game Cache File (GCF)
/// </summary>
public class GCF : DetectableBase<SabreTools.Serialization.Wrappers.GCF>
{
/// <inheritdoc/>
public GCF(SabreTools.Serialization.Wrappers.GCF wrapper) : base(wrapper) { }
/// <inheritdoc/>
public override string? Detect(Stream stream, string file, bool includeDebug)
{
// Filename is worth reporting, since it's descriptive and has to match the Steam2 CDN in order to work.
string fileName = Path.GetFileName(file);
uint depotId = _wrapper.Model.Header.CacheID;
uint manifestVersion = _wrapper.Model.Header.LastVersionPlayed;
// At the moment, all samples of GCF files on redump are unencrypted. Combined with being uncertain about
// whether this is the best way to check whether the GCF is encrypted, this block will be left commented
// out until further research is done.
// bool encrypted = false;
// if (_wrapper.Files != null && _wrapper.Files.Length > 0)
// encrypted = _wrapper.Files[0].Encrypted;
// string encryptedString = encrypted ? "encrypted" : "unencrypted";
// string returnString = $"{fileName} - {depotId} (v{manifestVersion}, {encryptedString})";
string returnString = $"{fileName} - {depotId} (v{manifestVersion})";
return returnString;
}
}
}

View File

@@ -7,6 +7,7 @@ using BinaryObjectScanner.Data;
using SabreTools.Data.Models.ISO9660;
using SabreTools.IO.Extensions;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>
@@ -68,7 +69,7 @@ namespace BinaryObjectScanner.FileType
// Currently-found worst cases:
// "Y:1BY:1BC" in Redump ID 23339
var strings = bytes.ReadStringsWithEncoding(charLimit: 7, Encoding.ASCII);
var rgx = new Regex("[^a-zA-Z0-9 -'!,.]");
var rgx = new Regex("[^a-zA-Z0-9 -'!,.]", RegexOptions.Compiled);
foreach (string str in strings)
{
if (rgx.Replace(str, "").Length > 7)
@@ -90,7 +91,7 @@ namespace BinaryObjectScanner.FileType
int offset = 0;
string? potentialAppUseString = applicationUse.ReadNullTerminatedAnsiString(ref offset);
if (potentialAppUseString != null && potentialAppUseString.Length > 0) // Some image authoring programs add a starting string to AU data
if (potentialAppUseString is not null && potentialAppUseString.Length > 0) // Some image authoring programs add a starting string to AU data
{
if (potentialAppUseString.StartsWith("ImgBurn"))
return false;
@@ -107,6 +108,12 @@ namespace BinaryObjectScanner.FileType
// character. If these are found to be causing issues they can be added.
}
// Seems to come from "FERGUS_MCNEILL - ISOCD 1.00 by Pantaray, Inc. USA -"
offset = 1;
potentialAppUseString = applicationUse.ReadNullTerminatedAnsiString(ref offset);
if (potentialAppUseString == "FS")
return false;
offset = 141;
potentialAppUseString = applicationUse.ReadNullTerminatedAnsiString(ref offset);
if (potentialAppUseString == "CD-XA001")

View File

@@ -1,5 +1,6 @@
using System.IO;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Data;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Data;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Data;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -1,6 +1,7 @@
using System.IO;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Data;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -1,5 +1,6 @@
using System.IO;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -1,5 +1,6 @@
using System.IO;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -1,5 +1,6 @@
using System.IO;
#pragma warning disable IDE0290 // Use primary constructor
namespace BinaryObjectScanner.FileType
{
/// <summary>

View File

@@ -153,7 +153,6 @@ namespace BinaryObjectScanner.FileType
protections.Add("SafeDiscLT for Macintosh (Unknown Version - Please report to us on GitHub)");
}
// The full line from a sample is as follows:
//
// The files securom_v7_01.dat and securom_v7_01.bak have been created during the installation of a SecuROM protected application.

View File

@@ -12,6 +12,6 @@
/// <param name="fileContent">Byte array representing the file contents</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>String containing any protections found in the file</returns>
string? CheckContents(string file, byte[] fileContent, bool includeDebug);
public string? CheckContents(string file, byte[] fileContent, bool includeDebug);
}
}

View File

@@ -14,7 +14,7 @@ namespace BinaryObjectScanner.Interfaces
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Detected file or protection type, null on error</returns>
/// <remarks>Ideally, this should just point to the other detect implementation.</remarks>
string? Detect(string file, bool includeDebug);
public string? Detect(string file, bool includeDebug);
/// <summary>
/// Check if a stream is detected as this file type
@@ -23,6 +23,6 @@ namespace BinaryObjectScanner.Interfaces
/// <param name="file">Path to the input file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Detected file or protection type, null on error</returns>
string? Detect(Stream stream, string file, bool includeDebug);
public string? Detect(Stream stream, string file, bool includeDebug);
}
}

View File

@@ -14,6 +14,6 @@ namespace BinaryObjectScanner.Interfaces
/// <param name="diskImage">Disk image representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>String containing any protections found in the file</returns>
string? CheckDiskImage(string file, T diskImage, bool includeDebug);
public string? CheckDiskImage(string file, T diskImage, bool includeDebug);
}
}

View File

@@ -14,6 +14,6 @@ namespace BinaryObjectScanner.Interfaces
/// <param name="exe">Executable representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>String containing any protections found in the file</returns>
string? CheckExecutable(string file, T exe, bool includeDebug);
public string? CheckExecutable(string file, T exe, bool includeDebug);
}
}

View File

@@ -16,13 +16,13 @@ namespace BinaryObjectScanner.Interfaces
/// <param name="path">Path to check for protection indicators</param>
/// <param name="files">List of strings representing files in a directory</param>
/// <remarks>This can do some limited content checking as well, but it's suggested to use a content check instead, if possible</remarks>
List<string> CheckDirectoryPath(string path, List<string>? files);
public List<string> CheckDirectoryPath(string path, List<string>? files);
/// <summary>
/// Check a file path for protections based on path name
/// </summary>
/// <param name="path">Path to check for protection indicators</param>
/// <remarks>This can do some limited content checking as well, but it's suggested to use a content check instead, if possible</remarks>
string? CheckFilePath(string path);
public string? CheckFilePath(string path);
}
}

View File

@@ -18,7 +18,7 @@ namespace BinaryObjectScanner.Packer
return "ASPack 2.29";
// Use the entry point data, if it exists
if (exe.EntryPointData != null)
if (exe.EntryPointData is not null)
{
var matchers = GenerateMatchers();
var match = MatchUtil.GetFirstMatch(file, exe.EntryPointData, matchers, includeDebug);
@@ -28,7 +28,7 @@ namespace BinaryObjectScanner.Packer
// Get the .adata* section, if it exists
var adataSectionRaw = exe.GetFirstSectionData(".adata", exact: false);
if (adataSectionRaw != null)
if (adataSectionRaw is not null)
{
var matchers = GenerateMatchers();
var match = MatchUtil.GetFirstMatch(file, adataSectionRaw, matchers, includeDebug);
@@ -38,7 +38,7 @@ namespace BinaryObjectScanner.Packer
// Get the .data/DATA section, if it exists
var dataSectionRaw = exe.GetFirstSectionData(".data") ?? exe.GetFirstSectionData("DATA");
if (dataSectionRaw != null)
if (dataSectionRaw is not null)
{
var matchers = GenerateMatchers();
var match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);

View File

@@ -11,7 +11,7 @@ namespace BinaryObjectScanner.Packer
{
// Get the .rdata section strings, if they exist
var strs = exe.GetFirstSectionStrings(".rdata");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("Software\\Caphyon\\Advanced Installer")))
return "Caphyon Advanced Installer SFX";

View File

@@ -18,7 +18,7 @@ namespace BinaryObjectScanner.Packer
if (exe.FindResourceByNamedType("99, ").Count == 2)
return "CExe";
if (exe.StubExecutableData != null)
if (exe.StubExecutableData is not null)
{
var matchers = new List<ContentMatchSet>
{

View File

@@ -14,9 +14,9 @@ namespace BinaryObjectScanner.Packer
// TODO: Investigate if this can be found by aligning to section containing entry point
// Get the last section strings, if they exist
var sections = exe.SectionTable ?? [];
var sections = exe.SectionTable;
var strs = exe.GetSectionStrings(sections.Length - 1);
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("BITARTS")))
return "Crunch";

View File

@@ -11,7 +11,7 @@ namespace BinaryObjectScanner.Packer
{
// Get the .text section strings, if they exist
var strs = exe.GetFirstSectionStrings(".text");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("DotfuscatorAttribute")))
return "dotFuscator";

View File

@@ -25,7 +25,7 @@ namespace BinaryObjectScanner.Packer
// Get the .text section, if it exists
var textData = exe.GetFirstSectionData(".text");
if (textData != null)
if (textData is not null)
{
var matchers = new List<ContentMatchSet>
{

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using BinaryObjectScanner.Interfaces;
using SabreTools.Data.Models.PortableExecutable.Resource.Entries;
using SabreTools.IO.Extensions;
using SabreTools.Serialization.Wrappers;
@@ -19,7 +20,7 @@ namespace BinaryObjectScanner.Packer
// Check the overlay, if it exists
byte[]? overlayData = exe.OverlayData;
if (overlayData != null && overlayData.Length > 0)
if (overlayData is not null && overlayData.Length > 0)
{
// Set the output variables
int overlayOffset = 0;
@@ -111,12 +112,16 @@ namespace BinaryObjectScanner.Packer
// Only process the resources if they are recognized
var resourceData = exe.ResourceData;
if (resourceData != null)
if (resourceData is not null)
{
// Get the resources that have an archive signature
foreach (var value in resourceData.Values)
{
if (value == null || value is not byte[] ba || ba.Length == 0)
if (value is null || value is not GenericResourceEntry gre)
continue;
byte[] ba = gre.Data;
if (ba.Length == 0)
continue;
// Set the output variables
@@ -162,7 +167,7 @@ namespace BinaryObjectScanner.Packer
// Check the overlay, if it exists
byte[]? overlayData = exe.OverlayData;
if (overlayData != null && overlayData.Length > 0)
if (overlayData is not null && overlayData.Length > 0)
{
// Set the output variables
int overlayOffset = 0;

View File

@@ -22,7 +22,7 @@ namespace BinaryObjectScanner.Packer
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("Gentee installer")))
return "Gentee Installer";

View File

@@ -14,7 +14,7 @@ namespace BinaryObjectScanner.Packer
{
// <see href="https://www.virustotal.com/gui/file/b2fc4cffe5131195baf419e96c9fa68c3f23208986fb14e3c5b458b1e7d6af89/details"/>
var overlayData = exe.OverlayData;
if (overlayData != null)
if (overlayData is not null)
{
// GIPEND
if (overlayData.EndsWith([0x47, 0x49, 0x50, 0x45, 0x4E, 0x44]))

View File

@@ -15,7 +15,7 @@ namespace BinaryObjectScanner.Packer
{
// Check for "Inno" in the reserved words
var reserved2 = exe.Stub?.Header?.Reserved2;
if (reserved2 != null && reserved2.Length > 5)
if (reserved2 is not null && reserved2.Length > 5)
{
if (reserved2[4] == 0x6E49 && reserved2[5] == 0x6F6E)
{
@@ -35,10 +35,10 @@ namespace BinaryObjectScanner.Packer
{
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
var str = strs.Find(s => s.StartsWith("Inno Setup Setup Data"));
if (str != null)
if (str is not null)
{
return str.Replace("Inno Setup Setup Data", "Inno Setup")
.Replace("(u)", "[Unicode]")
@@ -60,8 +60,9 @@ namespace BinaryObjectScanner.Packer
// TODO: Don't read entire file
// TODO: Only 64 bytes at the end of the file is needed
#pragma warning disable CS0618
byte[]? data = exe.ReadArbitraryRange();
if (data == null)
if (data is null)
return "Unknown 1.X";
var matchers = new List<ContentMatchSet>

View File

@@ -13,7 +13,7 @@ namespace BinaryObjectScanner.Packer
{
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("ViseMain")))
return "Installer VISE";

View File

@@ -23,7 +23,7 @@ namespace BinaryObjectScanner.Packer
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("wextract_cleanup")))
return $"Microsoft CAB SFX {GetVersion(exe)}";
@@ -31,7 +31,7 @@ namespace BinaryObjectScanner.Packer
// Get the .text section strings, if they exist
strs = exe.GetFirstSectionStrings(".text");
if (strs != null)
if (strs is not null)
{
// This detects a different but similar type of SFX that uses Microsoft CAB files.
// Further research is needed to see if it's just a different version or entirely separate.

View File

@@ -17,7 +17,11 @@ namespace BinaryObjectScanner.Packer
string? name = exe.AssemblyDescription;
if (name.OptionalStartsWith("Nullsoft Install System"))
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
return $"NSIS {name!["Nullsoft Install System".Length..].Trim()}";
#else
return $"NSIS {name!.Substring("Nullsoft Install System".Length).Trim()}";
#endif
name = exe.AssemblyName;
@@ -26,7 +30,7 @@ namespace BinaryObjectScanner.Packer
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("NullsoftInst")))
return "NSIS";
@@ -34,7 +38,7 @@ namespace BinaryObjectScanner.Packer
// Get the overlay strings, if they exist
strs = exe.OverlayStrings;
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("NullsoftInst")))
return "NSIS";

View File

@@ -16,7 +16,7 @@ namespace BinaryObjectScanner.Packer
{
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
// <see href="https://www.virustotal.com/gui/file/33b98b675d78b88ed317e7e52dca21ca07bd84e79211294fcec72cab48d11184"/>
if (strs.Exists(s => s.Contains("ReflexiveArcade")))

View File

@@ -12,7 +12,11 @@ namespace BinaryObjectScanner.Packer
string? name = exe.AssemblyDescription;
if (name.OptionalStartsWith("7-Zip Self-extracting Archive"))
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
return $"7-Zip SFX {exe.AssemblyDescription!["7-Zip Self-extracting Archive ".Length..]}";
#else
return $"7-Zip SFX {exe.AssemblyDescription!.Substring("7-Zip Self-extracting Archive ".Length)}";
#endif
name = exe.FileDescription;

View File

@@ -4,28 +4,30 @@ using System.Text.RegularExpressions;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class UPX : IExecutableCheck<PortableExecutable>
{
private static readonly Regex _oldUpxVersionMatch = new Regex(@"\$Id: UPX (.*?) Copyright \(C\)", RegexOptions.Compiled);
private static readonly Regex _oldUpxVersionMatch = new(@"\$Id: UPX (.*?) Copyright \(C\)", RegexOptions.Compiled);
private static readonly Regex _upxVersionMatch = new Regex(@"^([0-9]\.[0-9]{2})$", RegexOptions.Compiled);
private static readonly Regex _upxVersionMatch = new(@"^([0-9]\.[0-9]{2})$", RegexOptions.Compiled);
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
// Check header padding strings
if (exe.HeaderPaddingStrings != null && exe.HeaderPaddingStrings.Count > 0)
if (exe.HeaderPaddingStrings is not null && exe.HeaderPaddingStrings.Count > 0)
{
var match = exe.HeaderPaddingStrings.Find(s => s.Contains("UPX!"));
//if (match != null)
//if (match is not null)
// return "UPX";
match = exe.HeaderPaddingStrings.Find(s => s.StartsWith("$Id: UPX"));
if (match != null)
if (match is not null)
{
var regexMatch = _oldUpxVersionMatch.Match(match);
if (regexMatch.Success)
@@ -35,7 +37,7 @@ namespace BinaryObjectScanner.Packer
}
match = exe.HeaderPaddingStrings.Find(s => _upxVersionMatch.IsMatch(s));
if (match != null && exe.HeaderPaddingStrings.Exists(s => s == "UPX!"))
if (match is not null && exe.HeaderPaddingStrings.Exists(s => s == "UPX!"))
{
var regexMatch = _upxVersionMatch.Match(match);
if (regexMatch.Success)
@@ -43,7 +45,7 @@ namespace BinaryObjectScanner.Packer
else
return "UPX (Unknown Version)";
}
else if (match != null && exe.HeaderPaddingStrings.Exists(s => s == "NOS "))
else if (match is not null && exe.HeaderPaddingStrings.Exists(s => s == "NOS "))
{
var regexMatch = _upxVersionMatch.Match(match);
if (regexMatch.Success)

View File

@@ -11,14 +11,14 @@ namespace BinaryObjectScanner.Packer
public string? CheckExecutable(string file, NewExecutable exe, bool includeDebug)
{
// If the resident-name table doesnt exist
if (exe.ResidentNameTable == null)
if (exe.ResidentNameTable is null)
return null;
// Get the resident and non-resident name table strings
var rntStrs = Array.ConvertAll(exe.ResidentNameTable,
rnte => rnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(rnte.NameString));
rnte => rnte?.NameString is null ? string.Empty : Encoding.ASCII.GetString(rnte.NameString));
var nrntStrs = Array.ConvertAll(exe.NonResidentNameTable ?? [],
nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString));
nrnte => nrnte?.NameString is null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString));
// Check for the WinZip name strings
bool winZipNameFound = Array.Exists(rntStrs, s => s.Contains("WZ-SE-01"));
@@ -40,7 +40,7 @@ namespace BinaryObjectScanner.Packer
public string? CheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
// Check the export directory table, if it exists
if (exe.ExportDirectoryTable != null)
if (exe.ExportDirectoryTable is not null)
{
var version = GetPEExportDirectoryVersion(exe);
if (!string.IsNullOrEmpty(version))

View File

@@ -24,7 +24,7 @@ namespace BinaryObjectScanner.Packer
return "Wise Installation Wizard Module";
// If the section header can be found
if (exe.WiseSection != null)
if (exe.WiseSection is not null)
return "Wise Installation Wizard Module";
return null;

View File

@@ -37,7 +37,7 @@ namespace BinaryObjectScanner.Protection
// - In the stub data, there's a string for the download: "http://d.trymedia.com/dd/..."
// Get the entry point data, if it exists
if (exe.EntryPointData != null)
if (exe.EntryPointData is not null)
{
// Found in "Zuma.exe"
if (exe.EntryPointData.StartsWith(new byte?[] { 0x89, 0x25, 0x04, 0xF0, 0x86, 0x00, 0x68, 0x30 }))
@@ -46,7 +46,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data section strings, if they exist
var strs = exe.GetLastSectionStrings(".data");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("MPRMMGVA"))
&& strs.Exists(s => s.Contains("This application cannot run with an active debugger in memory.")))
@@ -60,14 +60,14 @@ namespace BinaryObjectScanner.Protection
if (resources.Count > 0)
{
bool match = resources
.ConvertAll(r => r == null ? string.Empty : Encoding.ASCII.GetString(r))
.ConvertAll(r => r is null ? string.Empty : Encoding.ASCII.GetString(r))
.FindIndex(r => r.Contains("ActiveMARK")) > -1;
if (match)
return "ActiveMARK";
}
// Get the overlay data, if it exists
if (exe.OverlayStrings != null)
if (exe.OverlayStrings is not null)
{
if (exe.OverlayStrings.Exists(s => s.Contains("TMSAMVOH")))
return "ActiveMARK";
@@ -75,7 +75,7 @@ namespace BinaryObjectScanner.Protection
// Get the last .bss section strings, if they exist
strs = exe.GetLastSectionStrings(".bss");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("TMSAMVOF")))
return "ActiveMARK";

View File

@@ -39,7 +39,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section, if it exists
var dataSectionRaw = exe.GetFirstSectionData(".data") ?? exe.GetFirstSectionData("DATA");
if (dataSectionRaw != null)
if (dataSectionRaw is not null)
{
var matchers = new List<ContentMatchSet>
{

View File

@@ -1,10 +1,12 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
using BinaryObjectScanner.Interfaces;
using SabreTools.Data.Models.ISO9660;
using SabreTools.IO.Extensions;
using SabreTools.Serialization.Wrappers;
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -56,38 +58,72 @@ namespace BinaryObjectScanner.Protection
if (diskImage.VolumeDescriptorSet[0] is not PrimaryVolumeDescriptor pvd)
return null;
// Alpharom disc check #1: disc has varying (but observed to at least always be larger than 14) length
// string made up of numbers and capital letters.
// Disc has varying (but observed to at least always be larger than 14) length
// application identifier string made up of numbers and capital letters.
// TODO: triple-check that length is never below 14
int offset = 0;
var applicationIdentifierString = pvd.ApplicationIdentifier.ReadNullTerminatedAnsiString(ref offset)?.Trim();
if (applicationIdentifierString == null || applicationIdentifierString.Length < 14)
if (applicationIdentifierString is null || applicationIdentifierString.Length < 14)
return null;
if (!Regex.IsMatch(applicationIdentifierString, "^[A-Z0-9]*$"))
if (!Regex.IsMatch(applicationIdentifierString, "^[A-Z0-9]*$", RegexOptions.Compiled))
return null;
// Alpharom disc check #2: disc has publisher identifier filled with varying amount of data (26-50 bytes
// have been observed) followed by spaces. There's a decent chance this is just a Japanese text string, but
// UTF, Shift-JIS, and EUC-JP all fail to display anything but garbage.
// While some alpharom discs have data in the publisher identifier that can be checked, not all of them do,
// so that can't reliably be used. There are two formats currently observed regarding the application
// identifier strings.
// #1 examples: DCOBG11C1B094961XN, DCXA9083CA554846GP, RCXA1107UD2510461A
// #2 examples: 2003120514103077LAHD, 20040326195254AVKC, 20051019163346WXUDCD
var publisherIdentifier = pvd.PublisherIdentifier;
int firstSpace = Array.FindIndex(publisherIdentifier, b => b == 0x20);
if (firstSpace <= 10 || firstSpace >= 120)
return null;
var applicationIdentifierStringBytes = Encoding.ASCII.GetBytes(applicationIdentifierString);
var publisherData = new byte[firstSpace];
var publisherSpaces = new byte[publisherData.Length - firstSpace];
Array.Copy(publisherIdentifier, 0, publisherData, 0, firstSpace);
Array.Copy(publisherIdentifier, firstSpace, publisherSpaces, 0, publisherData.Length - firstSpace);
// Type #1: 18 characters long, mix of letters and numbers. Since the string has already been confirmed
// to only consist of capital letters and numbers, a basic byte value check can be performed to ensure
// at least 5 bytes are numbers and 5 bytes are letters. Unfortunately, there doesn't seem to be quite
// enough of a pattern to have a better check than this, but it works well enough.
if (applicationIdentifierString.Length == 18
&& Array.FindAll(applicationIdentifierStringBytes, b => b < 60).Length >= 5
&& Array.FindAll(applicationIdentifierStringBytes, b => b > 60).Length >= 5)
{
return "Alpha-ROM";
}
if (!Array.TrueForAll(publisherSpaces, b => b == 0x20))
return null;
// Type #2: Usually 20 characters long, but Redump ID 124334 is 18 characters long. Validate that it
// starts with YYYYMMDD, followed by 6-8 more numbers, followed by letters.
if (applicationIdentifierString.Length >= 18 && applicationIdentifierString.Length <= 20)
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
if (!int.TryParse(applicationIdentifierString.AsSpan(0, 4), out int year)
|| !int.TryParse(applicationIdentifierString.AsSpan(4, 2), out int month)
|| !int.TryParse(applicationIdentifierString.AsSpan(6, 2), out int day)
|| !int.TryParse(applicationIdentifierString.AsSpan(8, 6), out int extraTime))
#else
if (!int.TryParse(applicationIdentifierString.Substring(0, 4), out int year)
|| !int.TryParse(applicationIdentifierString.Substring(4, 2), out int month)
|| !int.TryParse(applicationIdentifierString.Substring(6, 2), out int day)
|| !int.TryParse(applicationIdentifierString.Substring(8, 6), out int extraTime))
#endif
{
return null;
}
if (!FileType.ISO9660.IsPureData(publisherData))
return null;
if (year >= 2009 || year < 2000 || month > 12 || day > 31)
return null;
return "AlphaROM";
int index = Array.FindIndex(applicationIdentifierStringBytes, b => b > 60);
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
var startingNumbers = Encoding.ASCII.GetBytes(applicationIdentifierString[..index]);
var finalCharacters = Encoding.ASCII.GetBytes(applicationIdentifierString[index..]);
#else
var startingNumbers = Encoding.ASCII.GetBytes(applicationIdentifierString.Substring(0, index));
var finalCharacters = Encoding.ASCII.GetBytes(applicationIdentifierString.Substring(index));
#endif
if (Array.TrueForAll(startingNumbers, b => b < 60) && Array.TrueForAll(finalCharacters, b => b > 60))
return "Alpha-ROM";
}
return null;
}
/// <inheritdoc/>
@@ -98,7 +134,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("\\SETTEC")))
return "Alpha-ROM";
@@ -109,7 +145,7 @@ namespace BinaryObjectScanner.Protection
// Get the .rdata section strings, if they exist
strs = exe.GetFirstSectionStrings(".rdata");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("This Game is Japan Only")))
return "Alpha-ROM";
@@ -124,7 +160,7 @@ namespace BinaryObjectScanner.Protection
}
// Get the overlay data, if it exists
if (exe.OverlayStrings != null)
if (exe.OverlayStrings is not null)
{
// Found in Redump entry 84122.
if (exe.OverlayStrings.Exists(s => s.Contains("SETTEC0000")))

View File

@@ -33,7 +33,7 @@ namespace BinaryObjectScanner.Protection
{
// Get the section strings, if they exist
var strs = exe.GetFirstSectionStrings(sectionName);
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("ARMDEBUG")))
return "Armadillo";

View File

@@ -89,7 +89,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
// Found in "LineRider2.exe" in Redump entry 6236
if (strs.Exists(s => s.OptionalContains("ByteShield")))
@@ -98,7 +98,7 @@ namespace BinaryObjectScanner.Protection
// Get the .rdata section strings, if they exist
strs = exe.GetFirstSectionStrings(".rdata");
if (strs != null)
if (strs is not null)
{
// Found in "ByteShield.dll" in Redump entry 6236
if (strs.Exists(s => s.OptionalContains("Byte|Shield")))
@@ -115,7 +115,7 @@ namespace BinaryObjectScanner.Protection
// Get the .ret section strings, if they exist
strs = exe.GetFirstSectionStrings(".ret");
if (strs != null)
if (strs is not null)
{
// TODO: Figure out if this specifically indicates if the file is encrypted
// Found in "LineRider2.bbz" in Redump entry 6236

View File

@@ -8,6 +8,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -72,8 +73,9 @@ namespace BinaryObjectScanner.Protection
public string? CheckExecutable(string file, NewExecutable exe, bool includeDebug)
{
// TODO: Don't read entire file
#pragma warning disable CS0618
byte[]? data = exe.ReadArbitraryRange();
if (data == null)
if (data is null)
return null;
// TODO: Figure out what NE section this lives in
@@ -120,11 +122,11 @@ namespace BinaryObjectScanner.Protection
// Get the resident and non-resident name table strings
var nrntStrs = Array.ConvertAll(exe.NonResidentNameTable ?? [],
nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString));
nrnte => nrnte?.NameString is null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString));
// Check the imported-name table
// Found in "h3blade.exe" in Redump entry 85077.
if (exe.ImportedNameTable != null)
if (exe.ImportedNameTable is not null)
{
foreach (var inte in exe.ImportedNameTable.Values)
{
@@ -149,7 +151,7 @@ namespace BinaryObjectScanner.Protection
public string? CheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
// Get the stub executable data, if it exists
if (exe.StubExecutableData != null)
if (exe.StubExecutableData is not null)
{
var matchers = new List<ContentMatchSet>
{
@@ -181,10 +183,10 @@ namespace BinaryObjectScanner.Protection
// This contains the version section that the Content Check looked for. There are likely other sections
// that may contain it. Update when more are found.
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
var match = strs.Find(s => s.Contains(" ver. ") && (s.Contains("CD-Cops, ") || s.Contains("DVD-Cops, ")));
if (match != null)
if (match is not null)
if (match.Contains("CD-Cops"))
return $"CD-Cops {GetVersionString(match)}";
else if (match.Contains("DVD-Cops"))
@@ -257,7 +259,7 @@ namespace BinaryObjectScanner.Protection
private static string? GetVersion(string file, byte[]? fileContent, List<int> positions)
{
// If we have no content
if (fileContent == null)
if (fileContent is null)
return null;
string version = Encoding.ASCII.GetString(fileContent, positions[0] + 15, 4);
@@ -272,7 +274,7 @@ namespace BinaryObjectScanner.Protection
// Full string ends with # (i.e. "CD-Cops, ver. 1.72, #"), use that to compensate for comma in version
// number cases (don't change the comma, see earlier to-do) like "DVD-Cops, ver. 1,60, #"
// TODO: improve regex via the starting "N" character? Possibly unnecessary?
var versionMatch = Regex.Match(match, @"(?<=D-Cops,\s{1,}ver. )(.*?)(?=,\s{1,}#)");
var versionMatch = Regex.Match(match, @"(?<=D-Cops,\s{1,}ver. )(.*?)(?=,\s{1,}#)", RegexOptions.Compiled);
if (versionMatch.Success)
return versionMatch.Value;

View File

@@ -13,10 +13,10 @@ namespace BinaryObjectScanner.Protection
/// It may have been developed by Russobit-M, though the same source also says that StarForce was created by the same company as well, which seems unlikely (https://m.linkdatasecurity.com/pnews6.htm).
/// Others online have been confused by this as well (https://forum.ixbt.com/topic.cgi?id=31:009712).
/// A game referred to as having CD-Guard by http://lastboss.ru/games/RUS/randevu-s-neznakomkoi-2 that was published by Russobit-M is known to have an early version of StarForce (Redump entry 97088).
/// The FAQ on the game's official website indicates that StarForce specifically is present (https://web.archive.org/web/20011220224222/http://www.aha.ru/~exe_soft/russian/exesoft.htm).
/// The FAQ on the game's official website indicates that StarForce specifically is present (https://web.archive.org/web/20011220224222/http://www.aha.ru/~exe_soft/russian/exesoft.htm).
/// It's unknown for sure if there were two separate versions of this game that contained separate protections, or if the game never actually contained CD-Guard, or if CD-Guard was an early name for the StarForce line of products.
/// There is a re-release of an earlier game by the same developer that seems to include both CD-Guard and StarForce drivers, with the CD-Guard driver seemingly not used during installation, nor installed onto the system (IA item "pahgeby-he3hakomkou").
///
///
/// Additional resources and references:
/// https://gamecopyworld.com/games/pc_omikron.shtml
/// https://forum.ixbt.com/topic.cgi?id=31:3985
@@ -29,7 +29,7 @@ namespace BinaryObjectScanner.Protection
// TODO: Investigate the numerous ".guard" sections present in "Randevu.exe" in Redump entry 97142.
// Get the export directory table
if (exe.ExportDirectoryTable != null)
if (exe.ExportDirectoryTable is not null)
{
// Found in "cdguard.dll" in Redump entry 97142 and IA item "pahgeby-he3hakomkou".
bool match = exe.ExportDirectoryTable.Name.OptionalEquals("cdguard.dll", StringComparison.OrdinalIgnoreCase);
@@ -38,10 +38,10 @@ namespace BinaryObjectScanner.Protection
}
// Get the import directory table
if (exe.ImportDirectoryTable != null)
if (exe.ImportDirectoryTable is not null)
{
// Found in "Randevu.exe" in Redump entry 97142.
bool match = Array.Exists(exe.ImportDirectoryTable, idte => idte?.Name != null && idte.Name.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase));
bool match = Array.Exists(exe.ImportDirectoryTable, idte => idte?.Name is not null && idte.Name.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase));
if (match)
return "CD-Guard Copy Protection System";
}

View File

@@ -31,7 +31,7 @@ namespace BinaryObjectScanner.Protection
{
// Get the .data/DATA section, if it exists
var dataSectionRaw = exe.GetFirstSectionData(".data") ?? exe.GetFirstSectionData("DATA");
if (dataSectionRaw != null)
if (dataSectionRaw is not null)
{
var matchers = new List<ContentMatchSet>
{

View File

@@ -10,7 +10,7 @@ namespace BinaryObjectScanner.Protection
{
// TODO: Indicates Hypertech Crack Proof as well?
//// Get the import directory table
//if (exe.ImportDirectoryTable != null)
//if (exe.ImportDirectoryTable is not null)
//{
// bool match = exe.ImportDirectoryTable.Any(idte => idte.Name == "KeRnEl32.dLl");
// if (match)
@@ -19,7 +19,7 @@ namespace BinaryObjectScanner.Protection
// Get the code/CODE section strings, if they exist
var strs = exe.GetFirstSectionStrings("code") ?? exe.GetFirstSectionStrings("CODE");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("~0017.tmp")))
return "CDSHiELD SE";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// Protection that used to be offered by Valve for games on Steam. By default, protected executables are "stripped"
/// of varying 4KiB "strips", and these "strips" would only be downloaded when the user attempted to run the game on
/// Steam.
/// </summary>
public partial class CEG : IExecutableCheck<PortableExecutable>
{
/// <summary>
/// General info on the contents of a CEG executable, as pertaining to detection.
/// CEG executables should have 3 resources in .rsrc: STEAM_GUIDD, STEAM_MINSTANCE, and STEAM_SPLIT_GUID.
/// STEAM_GUIDD's meaning is unknown, as it remains largely consistent within a depot, but not the whole time,
/// and the same one will often show up across a few different unrelated depots. It's required along with split
/// GUID to request strips from Steam's servers, so it might be some kind of specific server identifier.
/// STEAM_MINSTANCE identifies a personalized executable that contains strips (and is all 0x00 if the executable is stripped)
/// STEAM_SPLIT_GUID identifies a specific build of that particular executable.
/// Some cracked versions of games are known to 0x00 out the data in all 3, but still keep them there.
/// There's also one edge case with a manifest for depotID 246581 where they're all 0x00, but this one might not
/// be active anyways.
///
/// All* CEG executables contain a .version section (virtual size 4, physical size 512) that begins with 4 bytes
/// identifying some kind of version, and the rest is all 0x00. This is not parsed or output since it's
/// specifically the version of the static library the executable was compiled against, which doesn't mean much,
/// and since there are CEG v1/v2/v3 functions, which *do* have significance, this would likely just create confusion.
/// Unfortunately, it is not feasible to parse v1/v2/v3 functions in BOS anyways, at least not at the moment.
/// *The only exceptions to this are some (but not all) versions of Company of Heroes 2 (depot ID 231431), as
/// well as Football Manager 2013 (depot IDs 207892, 216532, 216552, 216572, 216592, 216612, 220502, likely more)
/// use StarForce (ProActive? or maybe just packing/encryption/antitamper?), and this seems to strip the .version
/// section. Currently not checked if the version just wound up somewhere else.
///
/// All CEG executables contain the strings "STEAMSTART" and "STEAM_DRM_IPC" in the .rdata section.
/// Additionally, the .rdata section will either contain the string "This file has been stripped" if the executable
/// is stripped, or contain the string "This file contains strips" if the executable contains strips.
/// </summary>
/// <inheritdoc/>
public string? CheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
string[] lookupValues = ["STEAM_GUIDD", "STEAM_MINSTANCE", "STEAM_SPLIT_GUID"];
string[] returnedGuids = ["NOTHING", "NOTHING", "NOTHING"];
int localOffset = 0;
for (int i = 0; i < lookupValues.Length; i++)
{
var resourceDataList = exe.FindResourceByNamedType(lookupValues[i]);
if (resourceDataList.Count <= 0)
{
// The .version section should always be present, besides the two aforementioned games that use
// StarForce on top of CEG. If an executable contains a .version section, it almost certainly has
// CEG.
if (exe.ContainsSection(".version"))
return "CEG - Unable to parse resources, please report to us on GitHub!";
return null;
}
var resourceData = resourceDataList[0];
if (resourceData != null)
returnedGuids[i] = resourceData.ReadGuid(ref localOffset).ToString();
localOffset = 0;
}
string steamGuid = returnedGuids[0].ToUpperInvariant();
string steamMInstance = returnedGuids[1].ToUpperInvariant();
string steamSplitGuid = returnedGuids[2].ToUpperInvariant();
uint timestamp = exe.COFFFileHeader.TimeDateStamp;
const string zeroGUID = "00000000-0000-0000-0000-000000000000";
// If steam M instance is all 0x00, either the executable is stripped or it was tampered with.
if (steamMInstance == zeroGUID)
{
// If the steam guid or the steam split guid are all 0x00, the executable is almost certainly
// tampered. Tampered executables are also desired, though, which is why it's reported.
// There's also one edge case with a manifest for depotID 246581 where they're all 0x00, but this one
// might not active anyways, and is a pretty major edge case
if (steamGuid == zeroGUID || steamSplitGuid == zeroGUID)
{
// If for whatever reason only the steam guid is zeroed out, the Steam Split Guid can still be
// used to look up the executable.
if (steamSplitGuid != zeroGUID)
{
if (CEGDictionary.TryGetValue(steamSplitGuid, out string? tamperedName))
return $"CEG - Possibly tampered - {tamperedName} {ReportHelper(steamSplitGuid)}";
return "CEG - Unknown manifest, please report to us on GitHub!";
}
// If it's tampered and the steam split guid is zeroed out, the compilation timestamp needs to be
// used as a less precise fallback.
if (CEGBackupDictionary.TryGetValue(timestamp, out string[]? lookupGuids))
{
string returnString = "CEG - Possibly tampered, potentially";
for (int i = 0; i < lookupGuids.Length; i++)
{
string lookupGuid = lookupGuids[i];
if (CEGDictionary.TryGetValue(lookupGuid, out string? lookedUpName))
{
returnString = $"{returnString} {lookedUpName} {ReportHelper(lookupGuid)}";
if (i + 1 == lookupGuids.Length)
break;
returnString = $"{returnString} or";
}
}
return returnString;
}
// If it still can't be matched to anything, it's an unknown manifest.
return "CEG - Unknown manifest, please report to us on GitHub!";
}
// If the steam M instance is all 0x00 but the other values are not, the executable is stripped.
// Some tampered executables may still wind up here, but there's only so much that can be done to
// compensate for tampering.
if (CEGDictionary.TryGetValue(steamSplitGuid, out string? strippedName))
return $"CEG - Stripped - {strippedName}";
else
return "CEG - Unknown manifest, please report to us on GitHub!";
}
// If the steam M instance is not all 0x00, it has strips.
if (CEGDictionary.TryGetValue(steamSplitGuid, out string? name))
return $"CEG - Contains Strips - {name} {ReportHelper(steamSplitGuid)}";
else
return "CEG - Unknown manifest, please report to us on GitHub!";
}
/// <summary>
/// Checks if a given steam split guid is in the dictionary already to determine if reporting is desired.
/// </summary>
/// <param name="steamSplitGuid">Steam split GUID</param>
/// <returns>A string about reporting on GitHub if not found, otherwise returns null.</returns>
private static string? ReportHelper(string steamSplitGuid)
{
if (!HaveStrips.Contains(steamSplitGuid))
return "- Please report to us on GitHub!";
return null;
}
}
}

View File

@@ -3,6 +3,7 @@ using BinaryObjectScanner.Interfaces;
using SabreTools.IO;
using SabreTools.IO.Matching;
#pragma warning disable IDE0230 // Use UTF-8 string literal
namespace BinaryObjectScanner.Protection
{
public class CactusDataShield : IContentCheck

View File

@@ -19,7 +19,7 @@ namespace BinaryObjectScanner.Protection
public string? CheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
// Get the export directory table
if (exe.ExportDirectoryTable != null)
if (exe.ExportDirectoryTable is not null)
{
// Found in "cenega.dll" in IA item "speed-pack".
bool match = exe.ExportDirectoryTable.Name.OptionalEquals("ProtectDVD.dll", StringComparison.OrdinalIgnoreCase);

View File

@@ -34,7 +34,7 @@ namespace BinaryObjectScanner.Protection
// Get the .text section strings, if they exist
var strs = exe.GetFirstSectionStrings(".text");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("CODE-LOCK.OCX")))
return "ChosenBytes Code-Lock";

View File

@@ -61,7 +61,7 @@ namespace BinaryObjectScanner.Protection
// Checks for Professional
// PEX checks intentionally only detect Professional
if (exe.OverlayStrings != null)
if (exe.OverlayStrings is not null)
{
// Checks if main executable contains reference to optgraph.dll.
// This might be better removed later, as Redump ID 82475 is a false positive, and also doesn't actually
@@ -76,7 +76,7 @@ namespace BinaryObjectScanner.Protection
}
var strs = exe.GetFirstSectionStrings(".rdata");
if (strs != null)
if (strs is not null)
{
// Samples: Redump ID 82475, German Emergency 2 Deluxe, Redump ID 48393
if (strs.Exists(s => s.Contains("optgraph.dll")))
@@ -90,7 +90,7 @@ namespace BinaryObjectScanner.Protection
public List<string> CheckDirectoryPath(string path, List<string>? files)
{
var protections = new List<string>();
if (files == null)
if (files is null)
return protections;
// Checks for Light
@@ -110,7 +110,11 @@ namespace BinaryObjectScanner.Protection
{
lightFiles = fileList.FindAll(f =>
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
f = f[path.Length..];
#else
f = f.Remove(0, path.Length);
#endif
f = f.TrimStart('/', '\\');
return f.StartsWith(dir + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase);
});
@@ -120,7 +124,7 @@ namespace BinaryObjectScanner.Protection
break;
}
if ((lightFiles != null) && (lightFiles.Count > 0))
if ((lightFiles is not null) && (lightFiles.Count > 0))
{
try
{

View File

@@ -18,7 +18,7 @@ namespace BinaryObjectScanner.Protection
{
// Get the code/CODE section strings, if they exist
var strs = exe.GetFirstSectionStrings("code") ?? exe.GetFirstSectionStrings("CODE");
if (strs != null)
if (strs is not null)
{
// Found in "NECRO95.EXE" in IA item "NBECRORV11".
// Full string:

View File

@@ -17,7 +17,7 @@ namespace BinaryObjectScanner.Protection
{
// Get the .text section, if it exists
var textData = exe.GetFirstSectionData(".text");
if (textData != null)
if (textData is not null)
{
var matchers = new List<ContentMatchSet>
{

View File

@@ -11,7 +11,7 @@ namespace BinaryObjectScanner.Protection
public List<string> CheckDirectoryPath(string path, List<string>? files)
{
var protections = new List<string>();
if (files == null)
if (files is null)
return protections;
if (Directory.Exists(Path.Combine(path, "VIDEO_TS")))
@@ -20,10 +20,14 @@ namespace BinaryObjectScanner.Protection
for (int i = 0; i < bupfiles.Count; i++)
{
var bupfile = new FileInfo(bupfiles[i]);
if (bupfile.DirectoryName == null)
if (bupfile.DirectoryName is null)
continue;
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
string ifofile = Path.Combine(bupfile.DirectoryName, bupfile.Name[..^bupfile.Extension.Length] + ".ifo");
#else
string ifofile = Path.Combine(bupfile.DirectoryName, bupfile.Name.Substring(0, bupfile.Name.Length - bupfile.Extension.Length) + ".ifo");
#endif
if (bupfile.Length != ifofile.FileSize())
{
protections.Add("DVD-Movie-PROTECT (Unconfirmed - Please report to us on Github)");

View File

@@ -7,6 +7,7 @@ using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
using OHMN = SabreTools.Data.Models.COFF.OptionalHeaderMagicNumber;
#pragma warning disable IDE0230 // Use UTF-8 string literal
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -63,7 +64,7 @@ namespace BinaryObjectScanner.Protection
// https://github.com/horsicq/Detect-It-Easy/blob/master/db/PE/_denuvoComplete.2.sg
// Denuvo Protector
if (exe.OptionalHeader?.Magic == OHMN.PE32Plus && exe.EntryPointData != null)
if (exe.OptionalHeader?.Magic == OHMN.PE32Plus && exe.EntryPointData is not null)
{
byte?[] denuvoProtector =
[
@@ -102,12 +103,11 @@ namespace BinaryObjectScanner.Protection
// Mad Max, Metal Gear Solid: TPP, Rise of the Tomb Raider
new(
new ContentMatch(
new byte?[]
{
[
0x51, 0x52, 0x41, 0x50, 0x41, 0x51, 0x4C, 0x8D,
null, null, null, null, null, 0x4C, 0x8D, null,
null, null, null, null, 0x4D, 0x29, 0xC1,
},
],
end: 0
),
"Denuvo v1.0 (x64)"),
@@ -115,11 +115,10 @@ namespace BinaryObjectScanner.Protection
// Lords of the Fallen, Batman: AK, Just Cause 3, Sherlock Holmes: TdD, Tales of Berseria etc
new(
new ContentMatch(
new byte?[]
{
[
0x48, 0x8D, 0x0D, null, null, null, null, 0xE9,
null, null, null, null,
},
],
end: 0
),
"Denuvo v2.0a (x64)"),
@@ -127,13 +126,12 @@ namespace BinaryObjectScanner.Protection
// Yesterday Origins
new(
new ContentMatch(
new byte?[]
{
[
0x48, 0x89, null, null, null, null, null, 0x48,
0x89, null, null, null, null, null, 0x4C, 0x89,
null, null, null, null, null, 0x4C, 0x89, null,
null, null, null, null, 0x48, 0x83, 0xFA, 0x01,
},
],
end: 0
),
"Denuvo v2.0b (x64)"),
@@ -141,11 +139,10 @@ namespace BinaryObjectScanner.Protection
// Sniper Ghost Warrior 3 (beta), Dead Rising 4 (SteamStub-free)
new(
new ContentMatch(
new byte?[]
{
[
null, null, null, null, null, null, null, null,
0x4C, 0x89, 0x1C, 0x24, 0x49, 0x89, 0xE3,
},
],
end: 0
),
"Denuvo v3.0a (x64)"),
@@ -153,13 +150,12 @@ namespace BinaryObjectScanner.Protection
// Train Sim World CSX Heavy Haul
new(
new ContentMatch(
new byte?[]
{
[
0x4D, 0x8D, null, null, null, null, null, null,
null, null, null, 0x48, 0x89, null, null, null,
null, null, 0x48, 0x8D, null, null, 0x48, 0x89,
null, 0x48, 0x89, null, 0x48, 0x89,
},
],
end: 0
),
"Denuvo v3.0b (x64)"),
@@ -207,13 +203,12 @@ namespace BinaryObjectScanner.Protection
// Pro Evolution Soccer 2017, Champions of Anteria
new(
new ContentMatch(
new byte?[]
{
[
0x55, 0x89, 0xE5, 0x8D, null, null, null, null,
null, null, 0xE8, null, null, null, null, 0xE8,
null, null, null, null, 0xE8, null, null, null,
null, 0xE8, null, null, null, null,
},
],
end: 0
),
"Denuvo v1.0 (x86)"),
@@ -221,11 +216,10 @@ namespace BinaryObjectScanner.Protection
// Romance of 13 Kingdoms, 2Dark
new(
new ContentMatch(
new byte?[]
{
[
0x8D, null, null, null, null, null, null, 0x89,
0x7C, 0x24, 0x04, 0x89, 0xE7,
},
],
end: 0
),
"Denuvo v2.0 (x86)"),

View File

@@ -12,6 +12,8 @@ namespace BinaryObjectScanner.Protection
// LockBlocks falls under this category, being created by and seemingly exclusively in Dinamic Multimedia products, but in every place I find it described online, it is said to very specifically have two rings on the data side of the disc.
// Due to there being seemingly no absolute defining feature to LockBlocks other than this, any protected disc from Dinamic Multimedia that doesn't specifically have two rings is considered to have "Dinamic Multimedia Protection".
// That being said, it may be entirely possible that LockBlocks is the name for all these protections as a whole, as some sources seem to consider games that don't seem to have two rings to have LockBlocks.
// FX Interactive was formed by people formerly working at Dinamic Multimedia. The ring system used by this company shares many features of the Dinamic one, including using the same Mastering SIDs. It's possible related as a result but no direct connection has been found.
// Another possibly related DRM is SonoProtec. All known instances of Dinamic Multimedia Protection (aswell as FX Interactive) have been mastered at Sonopress Ibermemory SA. And while no directly comfirmed isntances of Sonoprotec has been found. Patent for this protection was filled by Sonopress Ibermemory SA on 1998-11-06. Around the same time Dinamic Multimedia Protection started showing up and patent description is very similar to how LockBlocks was described.
// Resources:
// https://www.cdmediaworld.com/hardware/cdrom/cd_protections_lockblocks.shtml
@@ -21,6 +23,10 @@ namespace BinaryObjectScanner.Protection
// https://www.gamecopyworld.com/games/pc_pc_atletismo.shtml
// https://www.gamecopyworld.com/games/pc_pc_calcio_2000.shtml
// https://www.gamecopyworld.com/games/pc_pc_futbol_2000.shtml
// https://www.gamecopyworld.com/games/pc_space_clash.shtml
// https://www.trademarkelite.com/europe/trademark/trademark-detail/001282730/SONOPROTEC
// https://www.patents.google.com/patent/EP1107251A1
// https://www.patents.google.com/patent/ES2149719A1
public List<string> CheckDirectoryPath(string path, List<string>? files)
{
@@ -46,7 +52,7 @@ namespace BinaryObjectScanner.Protection
{
var matchers = new List<PathMatchSet>
{
// Many more checks are likely possible based on the sources, but only ones that have been personally verified are getting added.
// Many more checks are likely possible based on the sources, but only ones that have been personally verified are getting added. As it stands now file names might be randomised though and not a good source for detection.
// Uncopyable files found in at least http://redump.org/disc/70531/, and likely in multiple others.
new(new FilePathMatch("2kscore.sc0"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
@@ -58,6 +64,15 @@ namespace BinaryObjectScanner.Protection
new(new FilePathMatch("opublic.001"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("spland.sc0"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("uqprime.ipx"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
// Uncopyable files found in at least http://redump.org/disc/55245/, and likely in multiple others.
new(new FilePathMatch("ZAXARAIE.DA0"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("IEDESAEO.EAS"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("NAIOUEGO.FEK"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("PIDAEIAI.DA0"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("SIXADIRE.PAU"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("UOUOZEJE.DA0"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("BAWIVOZI.YAQ"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
new(new FilePathMatch("YAUAEARO.QAI"), "Dinamic Multimedia Protection/LockBlocks [Check disc for 2 physical rings]"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Data.Models.PortableExecutable.Resource.Entries;
using SabreTools.IO;
using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
@@ -63,7 +64,7 @@ namespace BinaryObjectScanner.Protection
return "DiscGuard";
// Found in "Alternate.exe" (Redump entry 31914) and "Alt.exe" (Redump entries 46743, 46961, 79284, and 79374).
List<Dictionary<int, string?>?> resources =
List<StringTableResource?> resources =
[
.. exe.FindStringTableByEntry("DiscGuard"),
.. exe.FindStringTableByEntry("The file Dg.vbn was not found."),
@@ -75,7 +76,7 @@ namespace BinaryObjectScanner.Protection
// Get the .vbn section, if it exists
var vbnData = exe.GetFirstSectionData(".vbn");
if (vbnData != null)
if (vbnData is not null)
{
var matchers = new List<ContentMatchSet>
{
@@ -195,7 +196,7 @@ namespace BinaryObjectScanner.Protection
private string? GetVersion(string file, byte[]? fileContent, List<int> positions)
{
// If we have no content
if (fileContent == null)
if (fileContent is null)
return null;
// Check the internal versions

View File

@@ -31,7 +31,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("EReg Config Form")))
return "EA CdKey Registration Module";
@@ -39,7 +39,7 @@ namespace BinaryObjectScanner.Protection
// Get the .rdata section strings, if they exist
strs = exe.GetFirstSectionStrings(".rdata");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("GenericEA")) && strs.Exists(s => s.Contains("Activation")))
return "EA DRM Protection";
@@ -47,7 +47,7 @@ namespace BinaryObjectScanner.Protection
// Get the .rdata section strings, if they exist
strs = exe.GetFirstSectionStrings(".text");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("GenericEA")) && strs.Exists(s => s.Contains("Activation")))
return "EA DRM Protection";

View File

@@ -20,10 +20,10 @@ namespace BinaryObjectScanner.Protection
// Most every tested sample of "engine32.dll" has a product name of "engine32", and the file description typically follows the naming pattern of "[Game Name] DLL-helper".
// Detects Engine32 within the game executables that contain it.
if (exe.ImportDirectoryTable != null && exe.ImportHintNameTable != null)
if (exe.ImportDirectoryTable is not null && exe.ImportHintNameTable is not null)
{
bool importDirectoryTableMatch = Array.Exists(exe.ImportDirectoryTable,
idte => idte?.Name != null && idte.Name.Equals("ENGINE32.DLL", StringComparison.OrdinalIgnoreCase));
idte => idte?.Name is not null && idte.Name.Equals("ENGINE32.DLL", StringComparison.OrdinalIgnoreCase));
bool hintNameTableMatch = Array.Exists(exe.ImportHintNameTable,
ihne => ihne?.Name == "InitEngine");
@@ -33,7 +33,7 @@ namespace BinaryObjectScanner.Protection
}
// Detects Engine32 within the file "engine32.dll".
if (exe.ExportNameTable?.Strings != null)
if (exe.ExportNameTable?.Strings is not null)
{
bool exportNameTableMatch1 = Array.Exists(exe.ExportNameTable.Strings, s => s == "engine32.dll");
bool exportNameTableMatch2 = Array.Exists(exe.ExportNameTable.Strings, s => s == "DeinitEngine");

View File

@@ -22,7 +22,7 @@ namespace BinaryObjectScanner.Protection
return $"Games for Windows LIVE {exe.GetInternalVersion()}";
// Get the import directory table
if (exe.ImportDirectoryTable != null)
if (exe.ImportDirectoryTable is not null)
{
if (Array.Exists(exe.ImportDirectoryTable, idte => idte?.Name == "xlive.dll"))
return "Games for Windows LIVE";

View File

@@ -22,10 +22,10 @@ namespace BinaryObjectScanner.Protection
public string? CheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
// Get the header padding strings, if it exists
if (exe.HeaderPaddingStrings != null)
if (exe.HeaderPaddingStrings is not null)
{
var match = exe.HeaderPaddingStrings.Find(s => s.Contains("Gefest Protection System"));
if (match != null)
if (match is not null)
return $"Gefest Protection System {GetVersion(match)}";
}
@@ -57,7 +57,11 @@ namespace BinaryObjectScanner.Protection
private static string GetVersion(string match)
{
match = match.Trim('*').Trim();
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
return match["Gefest Protection System ".Length..];
#else
return match.Substring("Gefest Protection System ".Length);
#endif
}
}
}

View File

@@ -54,7 +54,7 @@ namespace BinaryObjectScanner.Protection
// Get the .text section strings, if they exist
var strs = exe.GetFirstSectionStrings(".text");
if (strs != null)
if (strs is not null)
{
// Found in "The Sudoku Challenge Collection.exe" in "The Sudoku Challenge! Collection" by Play at Joe's.
if (strs.Exists(s => s.Contains("mfint.dll")))
@@ -63,7 +63,7 @@ namespace BinaryObjectScanner.Protection
// Get the code/CODE section strings, if they exist
strs = exe.GetFirstSectionStrings("code") ?? exe.GetFirstSectionStrings("CODE");
if (strs != null)
if (strs is not null)
{
// Found in "launcher.exe" in "Sea Adventure / Adventure de la Mer" by Compedia.
if (strs.Exists(s => s.Contains("mfint.dll")))
@@ -72,7 +72,7 @@ namespace BinaryObjectScanner.Protection
// Get the UPX1 section strings, if they exist
strs = exe.GetFirstSectionStrings("UPX1");
if (strs != null)
if (strs is not null)
{
// Found in "postmanpat.exe" in "Postman Pat" by Compedia.
if (strs.Exists(s => s.Contains("mfint.dll")))

View File

@@ -33,13 +33,13 @@ namespace BinaryObjectScanner.Protection
// TODO: Check for CVP* instead?
bool containsCheck = false;
if (exe.ExportNameTable?.Strings != null)
if (exe.ExportNameTable?.Strings is not null)
containsCheck = Array.Exists(exe.ExportNameTable.Strings, s => s.OptionalStartsWith("CVPInitializeClient"));
// Get the .rdata section strings, if they exist
bool containsCheck2 = false;
var strs = exe.GetFirstSectionStrings(".rdata");
if (strs != null)
if (strs is not null)
{
containsCheck2 = strs.Exists(s => s.EndsWith("ATTLIST"))
&& strs.Exists(s => s.Equals("ELEMENT"))

View File

@@ -2,6 +2,7 @@ using System.Text.RegularExpressions;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
namespace BinaryObjectScanner.Protection
{
public class InterLok : IExecutableCheck<PortableExecutable>
@@ -11,13 +12,13 @@ namespace BinaryObjectScanner.Protection
{
// Get the .rsrc section strings, if they exist
var strs = exe.GetFirstSectionStrings(".rsrc");
if (strs != null)
if (strs is not null)
{
// Found in "nfsc_link.exe" in IA item "nfscorigin".
// Full string:
// (: ) InterLok PC v2.0, PACE Anti-Piracy, Copyright (C) 1998, ALL RIGHTS RESERVED
var match = strs.Find(s => s.Contains("InterLok") && s.Contains("PACE Anti-Piracy"));
if (match != null)
if (match is not null)
return $"PACE Anti-Piracy InterLok {GetVersion(match)}";
}
@@ -26,7 +27,7 @@ namespace BinaryObjectScanner.Protection
private static string GetVersion(string match)
{
var versionMatch = Regex.Match(match, @"(?<=InterLok )(.*?)(?=,)");
var versionMatch = Regex.Match(match, @"(?<=InterLok )(.*?)(?=,)", RegexOptions.Compiled);
if (versionMatch.Success)
return versionMatch.Value;

View File

@@ -26,7 +26,7 @@ namespace BinaryObjectScanner.Protection
if (exe.ContainsSection(".dcrtext") && importTableMatches)
{
var dcrtextData = exe.GetFirstSectionData(".dcrtext");
if (dcrtextData != null)
if (dcrtextData is not null)
{
var matchers = new List<ContentMatchSet>
{
@@ -63,7 +63,7 @@ namespace BinaryObjectScanner.Protection
private static string? GetVersion(string file, byte[]? fileContent, List<int> positions)
{
// If we have no content
if (fileContent == null)
if (fileContent is null)
return null;
return Encoding.ASCII.GetString(fileContent, positions[0] + 67, 8);

View File

@@ -41,7 +41,7 @@ namespace BinaryObjectScanner.Protection
// Get the .text section strings, if they exist
var strs = exe.GetFirstSectionStrings(".rdata");
if (strs != null)
if (strs is not null)
{
// Found in "TFT.exe" in Redump entry 95617.
if (strs.Exists(s => s.Contains("@KalypsoLauncherXml")))

View File

@@ -35,7 +35,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
// Found in "START.EXE" (Redump entry 95010 and product ID SVWC-7185).
if (strs.Exists(s => s.Contains("LGCD2_LAUNCH")))

View File

@@ -33,7 +33,7 @@ namespace BinaryObjectScanner.Protection
return null;
string? finalString = reserved653Bytes.ReadNullTerminatedAnsiString(ref firstNonZero);
if (finalString == null)
if (finalString is null)
return null;
// Redump ID 113120
@@ -189,7 +189,7 @@ namespace BinaryObjectScanner.Protection
private static string GetBuild(byte[]? sectionContent, bool versionTwo)
{
if (sectionContent == null)
if (sectionContent is null)
return "(Build unknown)";
// Unkown + (char)0x00 + Unkown
@@ -214,7 +214,7 @@ namespace BinaryObjectScanner.Protection
private static string? GetVersion(byte[]? sectionContent, int position)
{
// If we have invalid data
if (sectionContent == null)
if (sectionContent is null)
return null;
return Encoding.ASCII.GetString(sectionContent, position + 76, 4);

View File

@@ -5,6 +5,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -56,7 +57,6 @@ namespace BinaryObjectScanner.Protection
// File Description "C-Dilla Windows NT RTS" in "CDILLA05.DLL"/"CDILLA10.EXE"/"CDILLA16.EXE" from C-Dilla LMS version 3.27.000 for Windows NT.
// File Description "C-Dilla Windows 16-Bit RTS Installer" in "CdaIns16.dll"/"CdSetup.exe" from C-Dilla LMS version 3.27.000.
return null;
}
@@ -115,7 +115,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
// Found in "DJMixStation\DJMixStation.exe" in IA item "ejay_nestle_trial".
if (strs.Exists(s => s.Contains("SOFTWARE\\C-Dilla\\RTS")))

View File

@@ -7,6 +7,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -35,7 +36,7 @@ namespace BinaryObjectScanner.Protection
{
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("\\*.CDS")))
return "Cactus Data Shield 200";
@@ -113,7 +114,7 @@ namespace BinaryObjectScanner.Protection
public static string GetCactusDataShieldVersion(string firstMatchedString, List<string>? files)
{
// If we have no files
if (files == null)
if (files is null)
return string.Empty;
// Find the version.txt file first
@@ -137,10 +138,14 @@ namespace BinaryObjectScanner.Protection
{
using var sr = new StreamReader(path, Encoding.Default);
var line = sr.ReadLine();
if (line == null)
if (line is null)
return null;
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
return $"{line[3..]} ({sr.ReadLine()})";
#else
return $"{line.Substring(3)} ({sr.ReadLine()})";
#endif
}
catch
{

View File

@@ -5,6 +5,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -53,7 +54,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
// Found in "FLEXLM.CPL", "INSTALLS.EXE", "LMGR326B.DLL", "LMGRD.EXE", and "TAKEFIVE.EXE" in IA item "prog-17_202403".
if (strs.Exists(s => s.Contains("FLEXlm License Manager")))

View File

@@ -6,6 +6,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>

View File

@@ -6,6 +6,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -43,17 +44,18 @@ namespace BinaryObjectScanner.Protection
internal static string? SafeCastCheckExecutable(string file, NewExecutable exe, bool includeDebug)
{
// Check for the CDAC01AA name string.
if (exe.ResidentNameTable != null)
if (exe.ResidentNameTable is not null)
{
var residentNames = Array.ConvertAll(exe.ResidentNameTable,
rnte => rnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(rnte.NameString));
rnte => rnte?.NameString is null ? string.Empty : Encoding.ASCII.GetString(rnte.NameString));
if (Array.Exists(residentNames, s => s.Contains("CDAC01AA")))
return "SafeCast";
}
// TODO: Don't read entire file
#pragma warning disable CS0618
byte[]? data = exe.ReadArbitraryRange();
if (data == null)
if (data is null)
return null;
var neMatchSets = new List<ContentMatchSet>
@@ -74,10 +76,10 @@ namespace BinaryObjectScanner.Protection
// TODO: Invesitgate if the "AdobeLM.dll" file (along with mentions of "AdobeLM" in executables) uniquely identifies SafeCast, or if it can be used with different DRM. (Found in IA item ccd0605)
// Get the import directory table, if it exists
if (exe.ImportDirectoryTable != null)
if (exe.ImportDirectoryTable is not null)
{
if (Array.Exists(exe.ImportDirectoryTable,
idte => idte?.Name != null && idte.Name.Equals("CdaC14BA.dll", StringComparison.OrdinalIgnoreCase)))
idte => idte?.Name is not null && idte.Name.Equals("CdaC14BA.dll", StringComparison.OrdinalIgnoreCase)))
{
return "SafeCast";
}
@@ -90,7 +92,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
// Found in "DJMixStation\DJMixStation.exe" in IA item "ejay_nestle_trial".
if (strs.Exists(s => s.Contains("SOFTWARE\\C-Dilla\\SafeCast")))
@@ -149,13 +151,13 @@ namespace BinaryObjectScanner.Protection
var matchers = new List<PathMatchSet>
{
// Found in IA item "britney-spears-special-edition-cd-rom".
new(new List<PathMatch>
{
new(
[
new FilePathMatch("C2C.16"),
new FilePathMatch("C2C.DLL"),
new FilePathMatch("C2CDEL.16"),
new FilePathMatch("C2CDEL.EXE"),
}, "SafeCast"),
], "SafeCast"),
// Found in IA item "ejay_nestle_trial".
new(new FilePathMatch("cdac01aa.dll"), "SafeCast"),

View File

@@ -7,6 +7,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -52,14 +53,14 @@ namespace BinaryObjectScanner.Protection
internal static string? SafeDiscCheckExecutable(string file, PortableExecutable exe, bool includeDebug)
{
// Found in Redump entry 57986.
if (exe.ImportHintNameTable != null)
if (exe.ImportHintNameTable is not null)
{
if (Array.Exists(exe.ImportHintNameTable, ihne => ihne?.Name == "LTDLL_Authenticate"))
return "SafeDisc Lite";
}
// Found in Redump entry 57986.
if (exe.ImportDirectoryTable != null)
if (exe.ImportDirectoryTable is not null)
{
if (Array.Exists(exe.ImportDirectoryTable, idte => idte?.Name == "ltdll.dll"))
return "SafeDisc Lite";
@@ -67,7 +68,7 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
// Found in Redump entries 14928, 25579, 32751.
if (strs.Exists(s => s.Contains("LTDLL_Initialise")))
@@ -313,7 +314,6 @@ namespace BinaryObjectScanner.Protection
new(new FilePathMatch("00001009.016"), GetSafeDiscSplshVersion, "SafeDisc"),
new(new FilePathMatch("00001009.256"), GetSafeDiscSplshVersion, "SafeDisc"),
new(new FilePathMatch("DPLAYERX.DLL"), GetSafeDiscDPlayerXVersion, "SafeDisc"),
new(new FilePathMatch("drvmgt.dll"), GetSafeDiscDrvmgtVersion, "SafeDisc"),
@@ -715,7 +715,6 @@ namespace BinaryObjectScanner.Protection
// "1.1.43 1999/02/25 -> SafeDisc 1.01.043 (Redump entries 34562 and 63304).
// "1.1.44 1999/03/08" -> SafeDisc 1.01.044 (Redump entries 61731 and 81619).
// The hash of every "CLOKSPL.EXE" correlates directly to a specific SafeDisc version.
var sha1 = HashTool.GetFileHash(firstMatchedString, HashType.SHA1);
return sha1?.ToUpperInvariant() switch
@@ -1043,39 +1042,32 @@ namespace BinaryObjectScanner.Protection
return string.Empty;
var sha1 = HashTool.GetFileHash(firstMatchedString, HashType.SHA1);
switch (sha1?.ToUpperInvariant())
return sha1?.ToUpperInvariant() switch
{
// Found in Redump entry 63488.
case "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709":
return "(Empty File)";
"DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" => "(Empty File)",
// First known generic SafeDisc splash-screen.
// 4-bit (16 color) version, found in Redump entries 43321, 45040, 45202, 66586, 68206, 75501, 79272, and 110603.
case "D8A8CF761DD7C04F635385E4C4589E5F26C6171E":
return "1.11.000-2.40.011";
"D8A8CF761DD7C04F635385E4C4589E5F26C6171E" => "1.11.000-2.40.011",
// 8-bit (256 color) version, found in Redump entries 43321, 45040, 45202, 66586, 68206, 75501, 79272, and 110603.
case "0C9E45BF3EBE1382A3593994328C22BCB9A55456":
return "1.11.000-2.40.011";
"0C9E45BF3EBE1382A3593994328C22BCB9A55456" => "1.11.000-2.40.011",
// Second known generic SafeDisc splash-screen.
// 4-bit (16 color), found in Redump entries 46339 and 75897.
case "9B80F524D45041ED8CE1613AD5BDE94BFDBB2814":
return "2.70.030-2.80.010";
"9B80F524D45041ED8CE1613AD5BDE94BFDBB2814" => "2.70.030-2.80.010",
// 8-bit (256 color) version, found in Redump entries 46339 and 75897.
case "827AE9A32906CBE9098C9101184E0BE74CEA2744":
return "2.70.030-2.80.010";
"827AE9A32906CBE9098C9101184E0BE74CEA2744" => "2.70.030-2.80.010",
// Third known generic SafeDisc splash-screen.
// 4-bit (16 color), found in Redump entries 74338, 75782, 84985, and 91552.
case "814ED63FD619655650E271D1B8B46BBE39C3655A":
return "3.15.010-3.20.022";
"814ED63FD619655650E271D1B8B46BBE39C3655A" => "3.15.010-3.20.022",
// 8-bit (256 color) version, found in Redump entries 31824, 74338, 75782, 84985, 91552, and 104053.
case "40C7ACEDB6C41AB067285090373E886EFB4F4AC4":
return "3.15.010-4.60.000";
"40C7ACEDB6C41AB067285090373E886EFB4F4AC4" => "3.15.010-4.60.000",
default:
return null;
}
_ => null,
};
// There appear to be a few distinct generations of file names used for SafeDisc splash-screens.
// The first are the files named "SPLSH16.BMP"/"SPLSH256.BMP", which were typically used in SafeDisc versions 1.00.025-1.01.044.

View File

@@ -1,5 +1,7 @@
using SabreTools.Serialization.Wrappers;
#pragma warning disable CA1822 // Mark members as static
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>

View File

@@ -8,6 +8,7 @@ using SabreTools.IO.Extensions;
using SabreTools.IO.Matching;
using SabreTools.Serialization.Wrappers;
#pragma warning disable IDE0060 // Remove unused parameter
namespace BinaryObjectScanner.Protection
{
/// <summary>
@@ -27,6 +28,26 @@ namespace BinaryObjectScanner.Protection
if (diskImage.VolumeDescriptorSet[0] is not PrimaryVolumeDescriptor pvd)
return null;
var applicationUse = pvd.ApplicationUse;
var reserved653Bytes = pvd.Reserved653Bytes;
#region RipGuard
// Macrovision RipGuard protected DVDs have the string "MVSNRGFP" at the start of the application use, followed
// by some other data. The meaning of the rest of the data is unknown, but ultimately not necessary to check.
// Meaning of string unknown. Best guess is "Macrovision RipGuard (File?) Protection"
// While not checked, this string plus a similar-looking block of data appear near the very end of the image
// as well. Possibly part of another standard ISO9660 field?
byte[] ripGuardBytes = [0x4D, 0x56, 0x53, 0x4E, 0x52, 0x47, 0x46, 0x50]; // MVSNRGFP
int offset = 0;
var compareBytes = applicationUse.ReadBytes(ref offset, 8);
if (compareBytes.EqualsExactly(ripGuardBytes))
return "Macrovision RipGuard";
#endregion RipGuard
#region SafeDisc
if (!FileType.ISO9660.NoteworthyApplicationUse(pvd))
return null;
@@ -36,12 +57,9 @@ namespace BinaryObjectScanner.Protection
if (!FileType.ISO9660.NoteworthyReserved653Bytes(pvd))
return null;
var applicationUse = pvd.ApplicationUse;
var reserved653Bytes = pvd.Reserved653Bytes;
#region Read Application Use
int offset = 0;
offset = 0;
var appUseZeroBytes = applicationUse.ReadBytes(ref offset, 256);
var appUseDataBytesOne = applicationUse.ReadBytes(ref offset, 128);
offset += 64; // Some extra values get added here over time. Check is good enough, easier to skip this.
@@ -83,6 +101,8 @@ namespace BinaryObjectScanner.Protection
return null;
return "SafeDisc";
#endregion
}
/// <inheritdoc/>
@@ -100,7 +120,7 @@ namespace BinaryObjectScanner.Protection
if (!string.IsNullOrEmpty(safeCast))
resultsList.Add(safeCast!);
if (resultsList != null && resultsList.Count > 0)
if (resultsList is not null && resultsList.Count > 0)
return string.Join(";", [.. resultsList]);
return null;
@@ -136,12 +156,12 @@ namespace BinaryObjectScanner.Protection
{
// Check the header padding for protected sections.
var sectionMatch = CheckSectionForProtection(file, includeDebug, exe.HeaderPaddingStrings, exe.HeaderPaddingData, true);
if (sectionMatch != null)
if (sectionMatch is not null)
resultsList.Add(sectionMatch);
// Get the .data section, if it exists, for protected sections.
sectionMatch = CheckSectionForProtection(file, includeDebug, exe.GetFirstSectionStrings(".data"), exe.GetFirstSectionData(".data"), true);
if (sectionMatch != null)
if (sectionMatch is not null)
resultsList.Add(sectionMatch!);
int entryPointIndex = exe.FindEntryPointSectionIndex();
@@ -169,12 +189,12 @@ namespace BinaryObjectScanner.Protection
{
// Check the header padding for protected sections.
var sectionMatch = CheckSectionForProtection(file, includeDebug, exe.HeaderPaddingStrings, exe.HeaderPaddingData, false);
if (sectionMatch != null)
if (sectionMatch is not null)
resultsList.Add(sectionMatch);
// Check the .data section, if it exists, for protected sections.
sectionMatch = CheckSectionForProtection(file, includeDebug, exe.GetFirstSectionStrings(".data"), exe.GetFirstSectionData(".data"), false);
if (sectionMatch != null)
if (sectionMatch is not null)
resultsList.Add(sectionMatch);
}
@@ -210,7 +230,7 @@ namespace BinaryObjectScanner.Protection
// Clean the result list
resultsList = CleanResultList(resultsList);
if (resultsList != null && resultsList.Count > 0)
if (resultsList is not null && resultsList.Count > 0)
return string.Join(";", [.. resultsList]);
return null;
@@ -223,40 +243,40 @@ namespace BinaryObjectScanner.Protection
// Run Macrovision directory checks
var macrovision = MacrovisionCheckDirectoryPath(path, files);
if (macrovision != null)
if (macrovision is not null)
results.AddRange(macrovision);
// Run Cactus Data Shield directory checks
var cactusDataShield = CactusDataShieldCheckDirectoryPath(path, files);
if (cactusDataShield != null)
if (cactusDataShield is not null)
results.AddRange(cactusDataShield);
// Run C-Dilla directory checks
var cDilla = CDillaCheckDirectoryPath(path, files);
if (cDilla != null)
if (cDilla is not null)
results.AddRange(cDilla);
// Run FLEXnet directory checks
var flexNet = FLEXNetCheckDirectoryPath(path, files);
if (flexNet != null)
if (flexNet is not null)
results.AddRange(flexNet);
// Run RipGuard directory checks
var ripGuard = RipGuardCheckDirectoryPath(path, files);
if (ripGuard != null)
if (ripGuard is not null)
results.AddRange(ripGuard);
// Run SafeCast directory checks
var safeCast = SafeCastCheckDirectoryPath(path, files);
if (safeCast != null)
if (safeCast is not null)
results.AddRange(safeCast);
// Run SafeDisc directory checks
var safeDisc = SafeDiscCheckDirectoryPath(path, files);
if (safeDisc != null)
if (safeDisc is not null)
results.AddRange(safeDisc);
if (results != null && results.Count > 0)
if (results is not null && results.Count > 0)
return results;
return [];
@@ -304,7 +324,7 @@ namespace BinaryObjectScanner.Protection
// Clean the result list
resultsList = CleanResultList(resultsList);
if (resultsList != null && resultsList.Count > 0)
if (resultsList is not null && resultsList.Count > 0)
return string.Join(";", [.. resultsList]);
return null;
@@ -498,7 +518,7 @@ namespace BinaryObjectScanner.Protection
private static string? CheckSectionForProtection(string file, bool includeDebug, List<string>? sectionStrings, byte[]? sectionRaw, bool newVersion)
{
// Get the section strings, if they exist
if (sectionStrings != null)
if (sectionStrings is not null)
{
// If we don't have the "BoG_" string, the section isn't protected.
if (!sectionStrings.Exists(s => s.Contains("BoG_")))
@@ -524,7 +544,7 @@ namespace BinaryObjectScanner.Protection
}
// Get the section data, if it exists
if (sectionRaw != null)
if (sectionRaw is not null)
{
// TODO: Add more checks to help differentiate between SafeDisc and SafeCast.
var matchers = new List<ContentMatchSet>
@@ -547,7 +567,7 @@ namespace BinaryObjectScanner.Protection
internal static string? GetMacrovisionVersion(string file, byte[]? fileContent, List<int> positions)
{
// If we have no content
if (fileContent == null)
if (fileContent is null)
return null;
// Begin reading 2 bytes after "BoG_ *90.0&!! Yy>" for older versions
@@ -831,7 +851,7 @@ namespace BinaryObjectScanner.Protection
private static List<string>? CleanResultList(List<string>? resultsList)
{
// If we have an invalid result list
if (resultsList == null || resultsList.Count == 0)
if (resultsList is null || resultsList.Count == 0)
return resultsList;
// Remove duplicates

View File

@@ -41,14 +41,14 @@ namespace BinaryObjectScanner.Protection
// Get the .data/DATA section strings, if they exist
var strs = exe.GetFirstSectionStrings(".data") ?? exe.GetFirstSectionStrings("DATA");
if (strs != null)
if (strs is not null)
{
if (strs.Exists(s => s.Contains("CD3 Launch Error")))
return "MediaMax CD-3";
}
// Get the export name table
if (exe.ExportNameTable?.Strings != null)
if (exe.ExportNameTable?.Strings is not null)
{
if (Array.Exists(exe.ExportNameTable.Strings, s => s == "DllInstallSbcp"))
return "MediaMax CD-3";
@@ -63,22 +63,22 @@ namespace BinaryObjectScanner.Protection
var matchers = new List<PathMatchSet>
{
// Found on "All That I Am" by Santana (Barcode 8 2876-59773-2 6)
new(new List<PathMatch>
{
new(
[
// TODO: Verify if these are OR or AND
// TODO: Verify that this is directly related to MediaMax CD-3.
new FilePathMatch("PlayDisc.exe"),
new FilePathMatch("PlayDisc.xml"),
}, "MediaMax CD-3"),
], "MediaMax CD-3"),
// Found on "Contraband" by Velvet Revolver (Barcode 8 28766 05242 8)
// "SCCD3X01.dll" should already be detected by the content checks, but not "SCCD3X02.dll".
new(new List<PathMatch>
{
new(
[
// TODO: Verify if these are OR or AND
new FilePathMatch("SCCD3X01.dll"),
new FilePathMatch("SCCD3X02.dll"),
}, "MediaMax CD-3"),
], "MediaMax CD-3"),
};
return MatchUtil.GetAllMatches(files, matchers, any: false);

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