diff --git a/BurnOutSharp/Matching/ContentMatch.cs b/BurnOutSharp/Matching/ContentMatch.cs index 9fef7cf9..d4d86fe8 100644 --- a/BurnOutSharp/Matching/ContentMatch.cs +++ b/BurnOutSharp/Matching/ContentMatch.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.Eventing.Reader; + namespace BurnOutSharp.Matching { /// @@ -26,5 +28,66 @@ namespace BurnOutSharp.Matching Start = start; End = end; } + + #region Matching + + /// + /// Get if this match can be found in a stack + /// + /// Array to search for the given content + /// True to search from the end of the array, false from the start + public (bool, int) Match(byte[] stack, bool reverse = false) + { + // If either array is null or empty, we can't do anything + if (stack == null || stack.Length == 0 || Needle == null || Needle.Length == 0) + return (false, -1); + + // If the needle array is larger than the stack array, it can't be contained within + if (Needle.Length > stack.Length) + return (false, -1); + + // If start or end are not set properly, set them to defaults + if (Start < 0) + Start = 0; + if (End < 0) + End = stack.Length - Needle.Length; + + for (int i = reverse ? End : Start; reverse ? i > Start : i < End; i += reverse ? -1 : 1) + { + if (EqualAt(stack, i)) + return (true, i); + } + + return (false, -1); + } + + /// + /// Get if a stack at a certain index is equal to a needle + /// + /// Array to search for the given content + /// Starting index to check equality + private bool EqualAt(byte[] stack, int index) + { + // If the index is invalid, we can't do anything + if (index < 0) + return false; + + // If we're too close to the end of the stack, return false + if (Needle.Length >= stack.Length - index) + return false; + + for (int i = 0; i < Needle.Length; i++) + { + // A null value is a wildcard + if (Needle[i] == null) + continue; + else if (stack[i + index] != Needle[i]) + return false; + } + + return true; + } + + #endregion } } \ No newline at end of file diff --git a/BurnOutSharp/Matching/MatchUtil.cs b/BurnOutSharp/Matching/MatchUtil.cs new file mode 100644 index 00000000..d60847f4 --- /dev/null +++ b/BurnOutSharp/Matching/MatchUtil.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Linq; + +namespace BurnOutSharp.Matching +{ + /// + /// Helper class for content matching + /// + internal static class MatchUtil + { + /// + /// Get all content matches for a given list of matchers + /// + /// File to check for matches + /// Byte array representing the file contents + /// Enumerable of matchers to be run on the file + /// True to include positional data, false otherwise + /// List of strings representing the matched protections, null or empty otherwise + public static List GetAllContentMatches( + string file, + byte[] fileContent, + IEnumerable matchers, + bool includePosition = false) + { + return FindAllContentMatches(file, fileContent, matchers, includePosition, false); + } + + /// + /// Get first content match for a given list of matchers + /// + /// File to check for matches + /// Byte array representing the file contents + /// Enumerable of matchers to be run on the file + /// True to include positional data, false otherwise + /// String representing the matched protection, null otherwise + public static string GetFirstContentMatch( + string file, + byte[] fileContent, + IEnumerable matchers, + bool includePosition = false) + { + var contentMatches = FindAllContentMatches(file, fileContent, matchers, includePosition, true); + if (contentMatches == null || !contentMatches.Any()) + return null; + + return contentMatches.First(); + } + + /// + /// Get the required set of content matches on a per Matcher basis + /// + /// File to check for matches + /// Byte array representing the file contents + /// Enumerable of matchers to be run on the file + /// True to include positional data, false otherwise + /// True to stop after the first match, false otherwise + /// List of strings representing the matched protections, null or empty otherwise + private static List FindAllContentMatches( + string file, + byte[] fileContent, + IEnumerable matchers, + bool includePosition, + bool stopAfterFirst) + { + // If there's no mappings, we can't match + if (matchers == null || !matchers.Any()) + return null; + + // Initialize the list of matched protections + List matchedProtections = new List(); + + // Loop through and try everything otherwise + foreach (var matcher in matchers) + { + // Determine if the matcher passes + (bool passes, List positions) = matcher.MatchesAll(fileContent); + if (!passes) + continue; + + // Format the list of all positions found + string positionsString = string.Join(", ", positions); + + // If we there is no version method, just return the protection name + if (matcher.GetVersion == null) + { + matchedProtections.Add((matcher.ProtectionName ?? "Unknown Protection") + (includePosition ? $" (Index {positionsString})" : string.Empty)); + } + + // Otherwise, invoke the version method + // TODO: Pass all positions to the version finding method + else + { + string version = matcher.GetVersion(file, fileContent, positions[0]) ?? "Unknown Version"; + matchedProtections.Add($"{matcher.ProtectionName ?? "Unknown Protection"} {version}" + (includePosition ? $" (Index {positionsString})" : string.Empty)); + } + + // If we're stopping after the first protection, bail out here + if (stopAfterFirst) + return matchedProtections; + } + + return matchedProtections; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/Matching/Matcher.cs b/BurnOutSharp/Matching/Matcher.cs index 327f7aca..7e1fbb81 100644 --- a/BurnOutSharp/Matching/Matcher.cs +++ b/BurnOutSharp/Matching/Matcher.cs @@ -55,5 +55,36 @@ namespace BurnOutSharp.Matching } #endregion + + #region Matching + + /// + /// Determine wheter all content matches pass + /// + /// Byte array representing the file contents + /// Tuole of passing status and matching positions + public (bool, List) MatchesAll(byte[] fileContent) + { + // If no content matches are defined, we fail out + if (ContentMatches == null || !ContentMatches.Any()) + return (false, null); + + // Initialize the position list + List positions = new List(); + + // Loop through all content matches and make sure all pass + foreach (var contentMatch in ContentMatches) + { + (bool match, int position) = contentMatch.Match(fileContent); + if (!match) + return (false, null); + else + positions.Add(position); + } + + return (true, positions); + } + + #endregion } } \ No newline at end of file diff --git a/BurnOutSharp/PackerType/AdvancedInstaller.cs b/BurnOutSharp/PackerType/AdvancedInstaller.cs index c990c392..00430e62 100644 --- a/BurnOutSharp/PackerType/AdvancedInstaller.cs +++ b/BurnOutSharp/PackerType/AdvancedInstaller.cs @@ -22,7 +22,7 @@ namespace BurnOutSharp.PackerType }, "Caphyon Advanced Installer"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/PackerType/Armadillo.cs b/BurnOutSharp/PackerType/Armadillo.cs index 1af61985..11d1310b 100644 --- a/BurnOutSharp/PackerType/Armadillo.cs +++ b/BurnOutSharp/PackerType/Armadillo.cs @@ -17,7 +17,7 @@ namespace BurnOutSharp.PackerType new Matcher(new byte?[] { 0x41, 0x52, 0x4D, 0x44, 0x45, 0x42, 0x55, 0x47 }, "Armadillo"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/PackerType/CExe.cs b/BurnOutSharp/PackerType/CExe.cs index 94c2686d..ced25a9a 100644 --- a/BurnOutSharp/PackerType/CExe.cs +++ b/BurnOutSharp/PackerType/CExe.cs @@ -29,7 +29,7 @@ namespace BurnOutSharp.PackerType }, end: 200), "CExe"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/PackerType/EXEStealth.cs b/BurnOutSharp/PackerType/EXEStealth.cs index 145cfbfa..331f9d1e 100644 --- a/BurnOutSharp/PackerType/EXEStealth.cs +++ b/BurnOutSharp/PackerType/EXEStealth.cs @@ -21,7 +21,7 @@ namespace BurnOutSharp.PackerType }, "EXE Stealth"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/PackerType/InnoSetup.cs b/BurnOutSharp/PackerType/InnoSetup.cs index 056108de..f57ba1f3 100644 --- a/BurnOutSharp/PackerType/InnoSetup.cs +++ b/BurnOutSharp/PackerType/InnoSetup.cs @@ -23,7 +23,7 @@ namespace BurnOutSharp.PackerType "Inno Setup"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/PackerType/NSIS.cs b/BurnOutSharp/PackerType/NSIS.cs index 9af6f50e..00e997fd 100644 --- a/BurnOutSharp/PackerType/NSIS.cs +++ b/BurnOutSharp/PackerType/NSIS.cs @@ -29,7 +29,7 @@ namespace BurnOutSharp.PackerType }, "NSIS"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } public static string GetVersion(string file, byte[] fileContent, int index) diff --git a/BurnOutSharp/PackerType/PECompact.cs b/BurnOutSharp/PackerType/PECompact.cs index b294eb32..7d21e37a 100644 --- a/BurnOutSharp/PackerType/PECompact.cs +++ b/BurnOutSharp/PackerType/PECompact.cs @@ -27,7 +27,7 @@ namespace BurnOutSharp.PackerType }, "PE Compact 2"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } public static string GetVersion(string file, byte[] fileContent, int position) diff --git a/BurnOutSharp/PackerType/SetupFactory.cs b/BurnOutSharp/PackerType/SetupFactory.cs index 5317a45b..c663d127 100644 --- a/BurnOutSharp/PackerType/SetupFactory.cs +++ b/BurnOutSharp/PackerType/SetupFactory.cs @@ -44,7 +44,7 @@ namespace BurnOutSharp.PackerType // }, GetVersion, "Setup Factory"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/PackerType/UPX.cs b/BurnOutSharp/PackerType/UPX.cs index 827fd36d..6d86abde 100644 --- a/BurnOutSharp/PackerType/UPX.cs +++ b/BurnOutSharp/PackerType/UPX.cs @@ -12,7 +12,7 @@ namespace BurnOutSharp.PackerType var matchers = new List { // UPX! - new Matcher(new byte?[] { 0x55, 0x50, 0x58, 0x21 }, GetVersion, "Inno Setup"), + new Matcher(new byte?[] { 0x55, 0x50, 0x58, 0x21 }, GetVersion, "UPX"), // NOS new Matcher(new byte?[] { 0x4E, 0x4F, 0x53, 0x20 }, GetVersion, "UPX (NOS Variant)"), @@ -42,7 +42,7 @@ namespace BurnOutSharp.PackerType ), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } public static string GetVersion(string file, byte[] fileContent, int index) diff --git a/BurnOutSharp/PackerType/WinRARSFX.cs b/BurnOutSharp/PackerType/WinRARSFX.cs index 339b214a..0f518834 100644 --- a/BurnOutSharp/PackerType/WinRARSFX.cs +++ b/BurnOutSharp/PackerType/WinRARSFX.cs @@ -26,7 +26,7 @@ namespace BurnOutSharp.PackerType }, "WinRAR SFX"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } public Dictionary> Scan(Scanner scanner, string file) diff --git a/BurnOutSharp/PackerType/WinZipSFX.cs b/BurnOutSharp/PackerType/WinZipSFX.cs index 39fde7a9..c44f1583 100644 --- a/BurnOutSharp/PackerType/WinZipSFX.cs +++ b/BurnOutSharp/PackerType/WinZipSFX.cs @@ -29,7 +29,7 @@ namespace BurnOutSharp.PackerType new Matcher(new byte?[] { 0x5F, 0x77, 0x69, 0x6E, 0x7A, 0x69, 0x70, 0x5F }, GetVersion, "WinZip SFX"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/PackerType/WiseInstaller.cs b/BurnOutSharp/PackerType/WiseInstaller.cs index 7df7d59b..ee221894 100644 --- a/BurnOutSharp/PackerType/WiseInstaller.cs +++ b/BurnOutSharp/PackerType/WiseInstaller.cs @@ -20,7 +20,7 @@ namespace BurnOutSharp.PackerType new Matcher(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/PackerType/dotFuscator.cs b/BurnOutSharp/PackerType/dotFuscator.cs index 43ff6fe8..931a1e07 100644 --- a/BurnOutSharp/PackerType/dotFuscator.cs +++ b/BurnOutSharp/PackerType/dotFuscator.cs @@ -19,7 +19,7 @@ namespace BurnOutSharp.PackerType }, "dotFuscator"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/ActiveMARK.cs b/BurnOutSharp/ProtectionType/ActiveMARK.cs index 52712d6e..d7b6be96 100644 --- a/BurnOutSharp/ProtectionType/ActiveMARK.cs +++ b/BurnOutSharp/ProtectionType/ActiveMARK.cs @@ -22,7 +22,7 @@ namespace BurnOutSharp.ProtectionType }, "ActiveMARK 5"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/AlphaROM.cs b/BurnOutSharp/ProtectionType/AlphaROM.cs index 0cb4448b..01c06a4c 100644 --- a/BurnOutSharp/ProtectionType/AlphaROM.cs +++ b/BurnOutSharp/ProtectionType/AlphaROM.cs @@ -14,7 +14,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x53, 0x45, 0x54, 0x54, 0x45, 0x43 }, "Alpha-ROM"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/CDCheck.cs b/BurnOutSharp/ProtectionType/CDCheck.cs index e8e069d4..6408866d 100644 --- a/BurnOutSharp/ProtectionType/CDCheck.cs +++ b/BurnOutSharp/ProtectionType/CDCheck.cs @@ -21,7 +21,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x43, 0x44, 0x43, 0x68, 0x65, 0x63, 0x6B }, "Executable-Based CD Check"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } // These content checks are too broad to be useful @@ -45,7 +45,7 @@ namespace BurnOutSharp.ProtectionType }, "Executable-Based CD Check"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/CDCops.cs b/BurnOutSharp/ProtectionType/CDCops.cs index 8ebabe53..356f7b3e 100644 --- a/BurnOutSharp/ProtectionType/CDCops.cs +++ b/BurnOutSharp/ProtectionType/CDCops.cs @@ -24,7 +24,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x2E, 0x67, 0x72, 0x61, 0x6E, 0x64, 0x00 }, "CD-Cops"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/CDLock.cs b/BurnOutSharp/ProtectionType/CDLock.cs index 60a52933..9ad413db 100644 --- a/BurnOutSharp/ProtectionType/CDLock.cs +++ b/BurnOutSharp/ProtectionType/CDLock.cs @@ -23,7 +23,7 @@ namespace BurnOutSharp.ProtectionType }, "CD-Lock"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/CDSHiELDSE.cs b/BurnOutSharp/ProtectionType/CDSHiELDSE.cs index 90cf7d76..7fd5b644 100644 --- a/BurnOutSharp/ProtectionType/CDSHiELDSE.cs +++ b/BurnOutSharp/ProtectionType/CDSHiELDSE.cs @@ -14,7 +14,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x7E, 0x30, 0x30, 0x31, 0x37, 0x2E, 0x74, 0x6D, 0x70 }, "CDSHiELD SE"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/CactusDataShield.cs b/BurnOutSharp/ProtectionType/CactusDataShield.cs index 60c04902..55ce78f8 100644 --- a/BurnOutSharp/ProtectionType/CactusDataShield.cs +++ b/BurnOutSharp/ProtectionType/CactusDataShield.cs @@ -24,7 +24,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x43, 0x44, 0x53, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72 }, "Cactus Data Shield 200"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/CengaProtectDVD.cs b/BurnOutSharp/ProtectionType/CengaProtectDVD.cs index 70e331e9..de121a8e 100644 --- a/BurnOutSharp/ProtectionType/CengaProtectDVD.cs +++ b/BurnOutSharp/ProtectionType/CengaProtectDVD.cs @@ -14,7 +14,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x2E, 0x63, 0x65, 0x6E, 0x65, 0x67, 0x61 }, "Cenega ProtectDVD"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/CodeLock.cs b/BurnOutSharp/ProtectionType/CodeLock.cs index a03c8d15..81ed4d3e 100644 --- a/BurnOutSharp/ProtectionType/CodeLock.cs +++ b/BurnOutSharp/ProtectionType/CodeLock.cs @@ -25,7 +25,7 @@ namespace BurnOutSharp.ProtectionType }, "Code Lock"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/CopyKiller.cs b/BurnOutSharp/ProtectionType/CopyKiller.cs index 6fb404e1..b96c9fbd 100644 --- a/BurnOutSharp/ProtectionType/CopyKiller.cs +++ b/BurnOutSharp/ProtectionType/CopyKiller.cs @@ -21,7 +21,7 @@ namespace BurnOutSharp.ProtectionType }, "CopyKiller"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/DVDCops.cs b/BurnOutSharp/ProtectionType/DVDCops.cs index ad7b014b..7118cbe8 100644 --- a/BurnOutSharp/ProtectionType/DVDCops.cs +++ b/BurnOutSharp/ProtectionType/DVDCops.cs @@ -20,7 +20,7 @@ namespace BurnOutSharp.ProtectionType }, GetVersion, "DVD-Cops"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } public static string GetVersion(string file, byte[] fileContent, int position) diff --git a/BurnOutSharp/ProtectionType/ElectronicArts.cs b/BurnOutSharp/ProtectionType/ElectronicArts.cs index 7874658e..1af77592 100644 --- a/BurnOutSharp/ProtectionType/ElectronicArts.cs +++ b/BurnOutSharp/ProtectionType/ElectronicArts.cs @@ -106,7 +106,7 @@ namespace BurnOutSharp.ProtectionType }, "EA DRM Protection"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/GFWL.cs b/BurnOutSharp/ProtectionType/GFWL.cs index 579a8908..151a710f 100644 --- a/BurnOutSharp/ProtectionType/GFWL.cs +++ b/BurnOutSharp/ProtectionType/GFWL.cs @@ -17,7 +17,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x78, 0x6C, 0x69, 0x76, 0x65, 0x2E, 0x64, 0x6C, 0x6C }, "Games for Windows - Live"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/Intenium.cs b/BurnOutSharp/ProtectionType/Intenium.cs index 21e1c399..ca8c74e8 100644 --- a/BurnOutSharp/ProtectionType/Intenium.cs +++ b/BurnOutSharp/ProtectionType/Intenium.cs @@ -30,7 +30,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x54, 0x72, 0x69, 0x61, 0x6C, 0x00, 0x50 }, "INTENIUM Trial & Buy Protection"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/KeyLock.cs b/BurnOutSharp/ProtectionType/KeyLock.cs index cf8015db..9f20eb2f 100644 --- a/BurnOutSharp/ProtectionType/KeyLock.cs +++ b/BurnOutSharp/ProtectionType/KeyLock.cs @@ -18,7 +18,7 @@ namespace BurnOutSharp.ProtectionType }, "Key-Lock (Dongle)"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/MediaMaxCD3.cs b/BurnOutSharp/ProtectionType/MediaMaxCD3.cs index 906f9584..0e363aea 100644 --- a/BurnOutSharp/ProtectionType/MediaMaxCD3.cs +++ b/BurnOutSharp/ProtectionType/MediaMaxCD3.cs @@ -24,7 +24,7 @@ namespace BurnOutSharp.ProtectionType }, "MediaMax CD-3"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/OnlineRegistration.cs b/BurnOutSharp/ProtectionType/OnlineRegistration.cs index 33a52789..8aeb9f1c 100644 --- a/BurnOutSharp/ProtectionType/OnlineRegistration.cs +++ b/BurnOutSharp/ProtectionType/OnlineRegistration.cs @@ -21,7 +21,7 @@ namespace BurnOutSharp.ProtectionType }, Utilities.GetFileVersion, "Executable-Based Online Registration"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/Origin.cs b/BurnOutSharp/ProtectionType/Origin.cs index ce3120ce..dd2914f2 100644 --- a/BurnOutSharp/ProtectionType/Origin.cs +++ b/BurnOutSharp/ProtectionType/Origin.cs @@ -17,7 +17,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x4F, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x2E, 0x00, 0x65, 0x00, 0x78, 0x00, 0x65, 0x00 }, "Origin"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/PSXAntiModchip.cs b/BurnOutSharp/ProtectionType/PSXAntiModchip.cs index 23a3a8e9..26c0034b 100644 --- a/BurnOutSharp/ProtectionType/PSXAntiModchip.cs +++ b/BurnOutSharp/ProtectionType/PSXAntiModchip.cs @@ -42,7 +42,7 @@ namespace BurnOutSharp.ProtectionType }, "PlayStation Anti-modchip (Japanese)"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/RingPROTECH.cs b/BurnOutSharp/ProtectionType/RingPROTECH.cs index c7396b4f..1e2be7d0 100644 --- a/BurnOutSharp/ProtectionType/RingPROTECH.cs +++ b/BurnOutSharp/ProtectionType/RingPROTECH.cs @@ -19,7 +19,7 @@ namespace BurnOutSharp.ProtectionType }, "Ring PROTECH [Check disc for physical ring]"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/SVKProtector.cs b/BurnOutSharp/ProtectionType/SVKProtector.cs index 4635443a..64f4b3e2 100644 --- a/BurnOutSharp/ProtectionType/SVKProtector.cs +++ b/BurnOutSharp/ProtectionType/SVKProtector.cs @@ -14,7 +14,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x3F, 0x53, 0x56, 0x4B, 0x50, 0x00, 0x00 }, "SVK Protector"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/SafeLock.cs b/BurnOutSharp/ProtectionType/SafeLock.cs index d5bbf222..3077ab31 100644 --- a/BurnOutSharp/ProtectionType/SafeLock.cs +++ b/BurnOutSharp/ProtectionType/SafeLock.cs @@ -17,7 +17,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x53, 0x61, 0x66, 0x65, 0x4C, 0x6F, 0x63, 0x6B }, "SafeLock"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/SecuROM.cs b/BurnOutSharp/ProtectionType/SecuROM.cs index 64288112..9b290ded 100644 --- a/BurnOutSharp/ProtectionType/SecuROM.cs +++ b/BurnOutSharp/ProtectionType/SecuROM.cs @@ -46,7 +46,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x2E, 0x63, 0x6D, 0x73, 0x5F, 0x64, 0x00 }, "SecuROM 1-3"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/SmartE.cs b/BurnOutSharp/ProtectionType/SmartE.cs index 1e6f5def..71c9022b 100644 --- a/BurnOutSharp/ProtectionType/SmartE.cs +++ b/BurnOutSharp/ProtectionType/SmartE.cs @@ -17,7 +17,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x42, 0x49, 0x54, 0x41, 0x52, 0x54, 0x53 }, "SmartE"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/Sysiphus.cs b/BurnOutSharp/ProtectionType/Sysiphus.cs index c9597c9b..1b7418aa 100644 --- a/BurnOutSharp/ProtectionType/Sysiphus.cs +++ b/BurnOutSharp/ProtectionType/Sysiphus.cs @@ -25,7 +25,7 @@ namespace BurnOutSharp.ProtectionType }, GetVersion, "Sysiphus"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } public static string GetVersion(string file, byte[] fileContent, int position) diff --git a/BurnOutSharp/ProtectionType/ThreePLock.cs b/BurnOutSharp/ProtectionType/ThreePLock.cs index f61856c1..3ef1b077 100644 --- a/BurnOutSharp/ProtectionType/ThreePLock.cs +++ b/BurnOutSharp/ProtectionType/ThreePLock.cs @@ -28,7 +28,7 @@ namespace BurnOutSharp.ProtectionType // }, "3PLock"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/ThreeTwoOneStudios.cs b/BurnOutSharp/ProtectionType/ThreeTwoOneStudios.cs index 0f226324..e4d3a115 100644 --- a/BurnOutSharp/ProtectionType/ThreeTwoOneStudios.cs +++ b/BurnOutSharp/ProtectionType/ThreeTwoOneStudios.cs @@ -22,7 +22,7 @@ namespace BurnOutSharp.ProtectionType }, "321Studios Online Activation"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/ProtectionType/WTMCDProtect.cs b/BurnOutSharp/ProtectionType/WTMCDProtect.cs index 08014d22..c9b9ff2e 100644 --- a/BurnOutSharp/ProtectionType/WTMCDProtect.cs +++ b/BurnOutSharp/ProtectionType/WTMCDProtect.cs @@ -17,7 +17,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x57, 0x54, 0x4D, 0x37, 0x36, 0x35, 0x34, 0x35 }, "WTM CD Protect"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/XCP.cs b/BurnOutSharp/ProtectionType/XCP.cs index 5e8ab71c..936484e5 100644 --- a/BurnOutSharp/ProtectionType/XCP.cs +++ b/BurnOutSharp/ProtectionType/XCP.cs @@ -32,7 +32,7 @@ namespace BurnOutSharp.ProtectionType }, "XCP"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/ProtectionType/XtremeProtector.cs b/BurnOutSharp/ProtectionType/XtremeProtector.cs index e45e14e7..2592ae6c 100644 --- a/BurnOutSharp/ProtectionType/XtremeProtector.cs +++ b/BurnOutSharp/ProtectionType/XtremeProtector.cs @@ -14,7 +14,7 @@ namespace BurnOutSharp.ProtectionType new Matcher(new byte?[] { 0x58, 0x50, 0x52, 0x4F, 0x54, 0x20, 0x20, 0x20 }, "Xtreme-Protector"), }; - return Utilities.GetFirstContentMatch(file, fileContent, matchers, includePosition); + return MatchUtil.GetFirstContentMatch(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/Utilities.cs b/BurnOutSharp/Utilities.cs index fd8bcd07..36a5ea63 100644 --- a/BurnOutSharp/Utilities.cs +++ b/BurnOutSharp/Utilities.cs @@ -168,11 +168,13 @@ namespace BurnOutSharp // Initialize the loop variables bool found = true; int lastPosition = start; + var matcher = new ContentMatch(needle, end: end); // Loop over and get all positions while (found) { - (found, lastPosition) = FindPosition(stack, needle, lastPosition, end, false); + matcher.Start = lastPosition; + (found, lastPosition) = matcher.Match(stack, false); if (found) positions.Add(lastPosition); } @@ -185,7 +187,8 @@ namespace BurnOutSharp /// public static bool FirstPosition(this byte[] stack, byte?[] needle, out int position, int start = 0, int end = -1) { - (bool found, int foundPosition) = FindPosition(stack, needle, start, end, false); + var matcher = new ContentMatch(needle, start, end); + (bool found, int foundPosition) = matcher.Match(stack, false); position = foundPosition; return found; } @@ -195,7 +198,8 @@ namespace BurnOutSharp /// public static bool LastPosition(this byte[] stack, byte?[] needle, out int position, int start = 0, int end = -1) { - (bool found, int foundPosition) = FindPosition(stack, needle, start, end, true); + var matcher = new ContentMatch(needle, start, end); + (bool found, int foundPosition) = matcher.Match(stack, true); position = foundPosition; return found; } @@ -216,89 +220,10 @@ namespace BurnOutSharp return stack.FirstPosition(needle, out int _, start: stack.Length - needle.Length); } - /// - /// Find the position of one array in another, if possible - /// - private static (bool, int) FindPosition(byte[] stack, byte?[] needle, int start, int end, bool reverse) - { - // If either array is null or empty, we can't do anything - if (stack == null || stack.Length == 0 || needle == null || needle.Length == 0) - return (false, -1); - - // If the needle array is larger than the stack array, it can't be contained within - if (needle.Length > stack.Length) - return (false, -1); - - // If start or end are not set properly, set them to defaults - if (start < 0) - start = 0; - if (end < 0) - end = stack.Length - needle.Length; - - for (int i = reverse ? end : start; reverse ? i > start : i < end; i += reverse ? -1 : 1) - { - if (stack.EqualAt(needle, i)) - return (true, i); - } - - return (false, -1); - } - - /// - /// Get if a stack at a certain index is equal to a needle - /// - private static bool EqualAt(this byte[] stack, byte?[] needle, int index) - { - // If we're too close to the end of the stack, return false - if (needle.Length >= stack.Length - index) - return false; - - for (int i = 0; i < needle.Length; i++) - { - // A null value is a wildcard - if (needle[i] == null) - continue; - else if (stack[i + index] != needle[i]) - return false; - } - - return true; - } - #endregion #region Protection - /// - /// Get all content matches for a given list of matchers - /// - /// File to check for matches - /// Byte array representing the file contents - /// Enumerable of matchers to be run on the file - /// True to include positional data, false otherwise - /// List of strings representing the matched protections, null or empty otherwise - public static List GetAllContentMatches(string file, byte[] fileContent, IEnumerable matchers, bool includePosition = false) - { - return FindAllContentMatches(file, fileContent, matchers, includePosition, false); - } - - /// - /// Get first content match for a given list of matchers - /// - /// File to check for matches - /// Byte array representing the file contents - /// Enumerable of matchers to be run on the file - /// True to include positional data, false otherwise - /// String representing the matched protection, null otherwise - public static string GetFirstContentMatch(string file, byte[] fileContent, IEnumerable matchers, bool includePosition = false) - { - var contentMatches = FindAllContentMatches(file, fileContent, matchers, includePosition, false); - if (contentMatches == null || !contentMatches.Any()) - return null; - - return contentMatches.First(); - } - /// /// Get the file version as reported by the filesystem /// @@ -382,79 +307,6 @@ namespace BurnOutSharp } } - /// - /// Get the required set of content matches on a per Matcher basis - /// - /// File to check for matches - /// Byte array representing the file contents - /// Enumerable of matchers to be run on the file - /// True to include positional data, false otherwise - /// True to stop after the first match, false otherwise - /// List of strings representing the matched protections, null or empty otherwise - private static List FindAllContentMatches( - string file, - byte[] fileContent, - IEnumerable matchers, - bool includePosition, - bool stopAfterFirst) - { - // If there's no mappings, we can't match - if (matchers == null || !matchers.Any()) - return null; - - // Initialize the list of matched protections - List matchedProtections = new List(); - - // Loop through and try everything otherwise - foreach (var matcher in matchers) - { - // Setup for a single matcher - bool allMatches = true; - List positions = new List(); - - // Loop through all content matches and make sure all pass - foreach (var contentMatch in matcher.ContentMatches) - { - if (!fileContent.FirstPosition(contentMatch.Needle, out int position, contentMatch.Start, contentMatch.End)) - { - allMatches = false; - break; - } - else - { - positions.Add(position); - } - } - - // If not all matches pass, then we continue - if (!allMatches) - continue; - - // Format the list of all positions found - string positionsString = string.Join(", ", positions); - - // If we there is no version method, just return the protection name - if (matcher.GetVersion == null) - { - matchedProtections.Add((matcher.ProtectionName ?? "Unknown Protection") + (includePosition ? $" (Index {positionsString})" : string.Empty)); - } - - // Otherwise, invoke the version method - // TODO: Pass all positions to the version finding method - else - { - string version = matcher.GetVersion(file, fileContent, positions[0]) ?? "Unknown Version"; - matchedProtections.Add($"{matcher.ProtectionName} {version}" + (includePosition ? $" (Index {positionsString})" : string.Empty)); - } - - // If we're stopping after the first protection, bail out here - if (stopAfterFirst) - return matchedProtections; - } - - return matchedProtections; - } - #endregion } }