Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preliminary copy-X protection checking #328

Merged
merged 31 commits into from
Oct 27, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
948da43
Add preliminary copy-X protection checking
HeroponRikiBestest Oct 25, 2024
17a1663
Fixed formatting.
HeroponRikiBestest Oct 25, 2024
3254cfe
Removed some unecessary lines of code.
HeroponRikiBestest Oct 25, 2024
591d378
Added debatably sufficient documentation.
HeroponRikiBestest Oct 25, 2024
3855e46
Fixed formatting, hopefully
HeroponRikiBestest Oct 25, 2024
ee839ca
Finalize formatting and PR.
HeroponRikiBestest Oct 25, 2024
72b860b
Fleshes out checks after more samples. Fixes some but not all of the …
HeroponRikiBestest Oct 25, 2024
52074e9
Fix ordering.
HeroponRikiBestest Oct 26, 2024
06aee94
Fixes pex check, fixes redump id formatting.
HeroponRikiBestest Oct 26, 2024
77349aa
Added copy-X info to readme.
HeroponRikiBestest Oct 26, 2024
308f240
Revert "Added copy-X info to readme."
HeroponRikiBestest Oct 26, 2024
7080e36
Add copy-X info to readme, for real this time.
HeroponRikiBestest Oct 26, 2024
34ace7c
Replaced some code in byte check with BoS helper function.
HeroponRikiBestest Oct 26, 2024
f4d75c5
Remove first person.
HeroponRikiBestest Oct 26, 2024
bb1e3a3
Source is no longer just trust me (to some degree)
HeroponRikiBestest Oct 26, 2024
279fb88
Fix typo
HeroponRikiBestest Oct 26, 2024
3ff2b3a
WIP figuring out enumerable (fails to build)
HeroponRikiBestest Oct 26, 2024
15c8f9e
Merge remote-tracking branch 'origin/master'
HeroponRikiBestest Oct 26, 2024
9c69ffa
WIP 2 figuring out getfirstmatch (compiles, but breaks detection)
HeroponRikiBestest Oct 26, 2024
1fde6ea
Pass 1 of suggested changes.
HeroponRikiBestest Oct 27, 2024
0c0019e
Merge branch 'SabreTools:master' into master
HeroponRikiBestest Oct 27, 2024
722c0ea
Removed debug match.
HeroponRikiBestest Oct 27, 2024
75d9933
Pass 2 of suggested changes.
HeroponRikiBestest Oct 27, 2024
f84bd31
Added line.
HeroponRikiBestest Oct 27, 2024
9adc026
Added line for real.
HeroponRikiBestest Oct 27, 2024
cf1204a
Added todo
HeroponRikiBestest Oct 27, 2024
9451736
Improved comments.
HeroponRikiBestest Oct 27, 2024
ff7b27e
Finished todo.
HeroponRikiBestest Oct 27, 2024
d789dd2
Redid change.
HeroponRikiBestest Oct 27, 2024
1e84079
Fixes more comments.
HeroponRikiBestest Oct 27, 2024
350c12e
double double and make it trouble
HeroponRikiBestest Oct 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions BinaryObjectScanner/Protection/CopyX.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
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.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
// TODO: add documentation/comments to methods
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.
// 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
{
if (pex.OverlayStrings != null)
{
// Checks if main executable contains reference to optgraph.dll. Emergency 4's is missing this for some reason.
// 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.
if (pex.OverlayStrings.Any(s => s.Contains("optgraph.dll")))
{
// TODO: TKKG also has an NE 3.1x executable with a reference. This can be added later.
// Samples: Redump ID 108150
return "copy-X";
}
}

var strs = pex.GetFirstSectionStrings(".rdata");
if (strs != null)
{
if (strs.Any(s => s.Contains("optgraph.dll")))
{
// Samples: Redump ID 82475 (False positive, but probably catches original Emergency 2), would catch Redump ID 48393
return "copy-X";
}
}

return null;
}

/// <inheritdoc/>
#if NET20 || NET35
public Queue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)// Checks for Light
#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
if (files == null)
return protections;
var zdatFiles = files.Where(f => f.Remove(0, path.Length + 1).StartsWith("ZDAT", StringComparison.OrdinalIgnoreCase));//Gets files in ZDAT*
var fileList = zdatFiles.ToList();
// Sorts list of files in ZDAT* so I can just pull the first one, later ones have a chance of the ring intersecting the start of the file.
fileList.Sort();
if (fileList.Count > 0)
{
try
{
FileStream stream = new FileStream(fileList[0], FileMode.Open, FileAccess.Read);
byte[] block = new byte[1024];
stream.Read(block, 0, 1024);
// Checks if the file contains 0x00
// Samples: Redump ID 81628
if (block.All(thisByte => thisByte.Equals(0x00)))
{
protections.Enqueue("copy-X");
}
else
{
// Checks for whatever this data is.
// Samples: Redump ID 84759, Redump ID 107929. Professional discs also have this data, hence the exclusion check.
var matchers = new List<ContentMatchSet>
{
new(new byte?[]
{
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, true);
// Excludes files with .x64 extension to avoid flagging Professional files.
if (!string.IsNullOrEmpty(match) && !fileList[0].EndsWith(".x64", StringComparison.OrdinalIgnoreCase))
{
protections.Enqueue("copy-X");
}
}
}
catch (Exception)
{
}
}

return protections;
}

/// <inheritdoc/>
public string? CheckFilePath(string path)//Checks for Professional
{
// Samples: Redump ID 108150, Redump ID 48393
if (Path.GetFileName(path).Equals("optgraph.dll", StringComparison.OrdinalIgnoreCase))//Filename check for optgraph.dll disc check
{
return "copy-X";
}

if (Path.GetFileName(path).StartsWith("gov_", StringComparison.OrdinalIgnoreCase) && Path.GetFileName(path).EndsWith(".x64", StringComparison.OrdinalIgnoreCase))
{
return "copy-X";
}

if (Path.GetFileName(path).Equals("iofile.x64", StringComparison.OrdinalIgnoreCase))//Filename check for seemingly comorbid file.
{
return "copy-X";
}

if (Path.GetFileName(path).Equals("sound.x64", StringComparison.OrdinalIgnoreCase))
{
return "copy-X";
}

return null;
}
}
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand Down Expand Up @@ -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 | |
Expand Down
Loading