mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-04-20 13:12:39 +00:00
Add preliminary copy-X protection checking (#328)
* Add preliminary copy-X protection checking
* Fixed formatting.
* Removed some unecessary lines of code.
* Added debatably sufficient documentation.
* Fixed formatting, hopefully
* Finalize formatting and PR.
* Fleshes out checks after more samples. Fixes some but not all of the change requests.
* Fix ordering.
* Fixes pex check, fixes redump id formatting.
* Added copy-X info to readme.
* Revert "Added copy-X info to readme."
This reverts commit 77349aa8de.
* Add copy-X info to readme, for real this time.
* Replaced some code in byte check with BoS helper function.
* Remove first person.
* Source is no longer just trust me (to some degree)
* Fix typo
* WIP figuring out enumerable (fails to build)
* WIP 2 figuring out getfirstmatch (compiles, but breaks detection)
* Pass 1 of suggested changes.
* Removed debug match.
* Pass 2 of suggested changes.
* Added line.
* Added line for real.
* Added todo
* Improved comments.
* Finished todo.
* Redid change.
* Fixes more comments.
* double double and make it trouble
This commit is contained in:
committed by
GitHub
parent
d8aa4d230d
commit
4e0442d526
193
BinaryObjectScanner/Protection/CopyX.cs
Normal file
193
BinaryObjectScanner/Protection/CopyX.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Matching.Paths;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
// TODO: Technically not necessary, but just check for light/pro first and only if it isn't found look for the other.
|
||||
// It should be an Or situation and not an And situation.
|
||||
// TODO: Figure out if Light and Professional are what designate rings and rings+disccheck
|
||||
public class CopyX : IPathCheck, IPortableExecutableCheck
|
||||
{
|
||||
// https://web.archive.org/web/20011016234742/http://www.optimal-online.de:80/product/copy_x.htm
|
||||
// There are four kinds of copy-X; Light, Profesisonal, audio, and Trial Maker.
|
||||
// Audio is for Audio CDs. Might be scannable, might not. Samples needed to confirm.
|
||||
// No samples of Trial are known at the moment, so it can't be checked for either.
|
||||
// There are two kinds of copy-X generally observed; those with only rings, and those with rings and a disc check.
|
||||
// These comments assume with 0 evidence that the former is Light and the latter is Professional.
|
||||
// Because there is no evidence, only copy-X is being returned for now. This check has pre-emptively separated
|
||||
// the two, just for when a designation can be applied for sure.
|
||||
|
||||
// Overall:
|
||||
// Whenever these comments state "at the end of" or "at the start of" pertaining to the filesystem, they refer
|
||||
// to alphabetical order, because this is how copy-X images seem to be mastered usually (not always, but w/e).
|
||||
// Both Light and Professional have a directory at the end of the image. The files within this directory are
|
||||
// intersected by the physical ring.
|
||||
// This file is usually called ZDAT, but not always. At least one instance of Light calls it ZDATA. At least one
|
||||
// instance of Professional calls it System.
|
||||
// Seemingly it can be anything. It doesn't help that most known samples are specifically from one company's
|
||||
// games, Tivola. Still, most use ZDAT.
|
||||
|
||||
// Professional:
|
||||
// All instances of professional contain a disc check, performed via optgraph.dll.
|
||||
// All instances of professional contain in the directory at the end of the image 3 files. gov_[something].x64,
|
||||
// iofile.x64, and sound.x64.
|
||||
// Due to gov's minor name variance, sound.x64 sometimes being intersected by a ring at the start, and
|
||||
// iofile.x64 being referenced directly in optgraph.x64, only iofile.x64 is being checked for now.
|
||||
// TODO: optgraph.dll also contains DRM to prevent kernel debugger SoftICE from being used, via a process called
|
||||
// SoftICE-Test. It is not currently known if this is specifically part of copy-X, or if it's an external
|
||||
// solution employed by both copy-X and also other companies. If it's the latter, it should have its own check.
|
||||
// It has none here since it wouldn't be necessary.
|
||||
|
||||
// Light:
|
||||
// All instances of light contain 1 or more files in the directory at the end of the image. They all consist of
|
||||
// either 0x00, or some data that matches between entries (and also is present in the 3 Professional files),
|
||||
// except for the parts with the rings running through them.
|
||||
// TODO: Check the last directory alphabetically and not just ZDAT*
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
|
||||
// Checks for Professional
|
||||
// PEX checks intentionally only detect Professional
|
||||
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
if (pex.OverlayStrings != 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
|
||||
// contain the actual optgraph.dll file.
|
||||
// TODO: Find a way to check for situations like Redump ID 48393, where the string is spaced out with
|
||||
// 0x00 between letters and does not show up on string checks.
|
||||
// TODO: This might need to check every single section. Unsure until more samples are acquired.
|
||||
// TODO: TKKG also has an NE 3.1x executable with a reference. This can be added later.
|
||||
// Samples: Redump ID 108150
|
||||
if (pex.OverlayStrings.Any(s => s.Contains("optgraph.dll")))
|
||||
return "copy-X";
|
||||
}
|
||||
|
||||
var strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
// Samples: Redump ID 82475, German Emergency 2 Deluxe, Redump ID 48393
|
||||
if (strs.Any(s => s.Contains("optgraph.dll")))
|
||||
return "copy-X";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
#if NET20 || NET35
|
||||
var protections = new Queue<string>();
|
||||
#else
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
#endif
|
||||
|
||||
// Checks for Light
|
||||
// Directory checks intentionally only detect Light
|
||||
|
||||
if (files == null)
|
||||
return protections;
|
||||
|
||||
// Excludes files with .x64 extension to avoid flagging Professional files.
|
||||
// Sorts list of files in ZDAT* so just the first file gets pulled, later ones have a chance of the ring
|
||||
// intersecting the start of the file.
|
||||
var fileList = files.Where(f => !f.EndsWith(".x64", StringComparison.OrdinalIgnoreCase))
|
||||
.Where(f =>
|
||||
{
|
||||
// TODO: Compensate for the check being run a directory or more higher
|
||||
f = f.Remove(0, path.Length);
|
||||
f = f.TrimStart('/', '\\');
|
||||
return f.StartsWith("ZDAT", StringComparison.OrdinalIgnoreCase);
|
||||
})
|
||||
.OrderBy(f => f)
|
||||
.ToList();
|
||||
|
||||
if (fileList.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var stream = File.OpenRead(fileList[0]);
|
||||
byte[] block = stream.ReadBytes(1024);
|
||||
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// Checks if the file contains 0x00
|
||||
// Samples: Redump ID 81628
|
||||
new(Enumerable.Repeat<byte?>(0x00, 1024).ToArray(), "copy-X"),
|
||||
|
||||
// Checks for whatever this data is.
|
||||
// Samples: Redump ID 84759, Redump ID 107929. Professional discs also have this data, hence the exclusion check.
|
||||
new(
|
||||
[
|
||||
0x02, 0xFE, 0x4A, 0x4F, 0x52, 0x4B, 0x1C, 0xE0,
|
||||
0x79, 0x8C, 0x7F, 0x85, 0x04, 0x00, 0x46, 0x46,
|
||||
0x49, 0x46, 0x07, 0xF9, 0x9F, 0xA0, 0xA1, 0x9D,
|
||||
0xDA, 0xB6, 0x2C, 0x2D, 0x2D, 0x2C, 0xFF, 0x00,
|
||||
0x6F, 0x6E, 0x71, 0x6A, 0xFC, 0x06, 0x64, 0x62,
|
||||
0x65, 0x5F, 0xFB, 0x06, 0x31, 0x31, 0x31, 0x31,
|
||||
0x00, 0x00, 0x1D, 0x1D, 0x1F, 0x1D, 0xFE, 0xFD,
|
||||
0x51, 0x57, 0x56, 0x51, 0xFB, 0x06, 0x33, 0x34,
|
||||
], "copy-X"),
|
||||
};
|
||||
var match = MatchUtil.GetFirstMatch(fileList[0], block, matchers, false);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
protections.Enqueue(match!);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
|
||||
// Checks for Professional
|
||||
// File Path checks intentionally only detect Professional
|
||||
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Samples: Redump ID 108150, Redump ID 48393
|
||||
|
||||
// File responsible for disc check
|
||||
new(new FilePathMatch("optgraph.dll"), "copy-X"),
|
||||
|
||||
// Seemingly comorbid file, referenced in above file
|
||||
new(new FilePathMatch("iofile.x64"), "copy-X"),
|
||||
|
||||
// Seemingly comorbid file
|
||||
new(new FilePathMatch("sound.x64"), "copy-X"),
|
||||
|
||||
// Seemingly comorbid file
|
||||
// Check commented out until implementation can be decided
|
||||
// new(new FilePathMatch("gov_*.x64"), "copy-X"),
|
||||
};
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@ Below is a list of protections detected by BinaryObjectScanner. The two columns
|
||||
| Cenga ProtectDVD | True | True | |
|
||||
| Channelware | True | True | Version finding and detection of later versions unimplemented |
|
||||
| ChosenBytes CodeLock | True | True | Partially unconfirmed² |
|
||||
| copy-X | True | True | |
|
||||
| CopyKiller | True | True | |
|
||||
| CopyLok/CodeLok | True | False | |
|
||||
| CrypKey | True | True | |
|
||||
@@ -133,7 +134,7 @@ Below is a list of protections detected by BinaryObjectScanner. The two columns
|
||||
| Sysiphus / Sysiphus DVD | True | False | |
|
||||
| TAGES | True | True | Partially unconfirmed² |
|
||||
| Themida/WinLicense/Code Virtualizer | True | False | Only certain products/versions currently detected |
|
||||
| Tivola Ring Protection | False | True | |
|
||||
| ~~Tivola Ring Protection~~ | False | True | Existing checks found to actually be indicators of copy-X, rather than some Tivola-specific ring protection. |
|
||||
| TZCopyProtection | False | True | Partially unconfirmed² |
|
||||
| Uplay | True | True | |
|
||||
| Windows Media Data Session DRM | True | True | |
|
||||
|
||||
Reference in New Issue
Block a user