using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace BinaryObjectScanner.Matching
{
///
/// Helper class for matching
///
public static class MatchUtil
{
#region Array Content Matching
///
/// Get all content matches for a given list of matchers
///
/// File to check for matches
/// Array to search
/// Enumerable of ContentMatchSets 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 ConcurrentQueue GetAllMatches(
string file,
byte[] stack,
IEnumerable matchers,
bool includeDebug = false)
{
return FindAllMatches(file, stack, matchers, includeDebug, false);
}
///
/// Get first content match for a given list of matchers
///
/// File to check for matches
/// Array to search
/// Enumerable of ContentMatchSets to be run on the file
/// True to include positional data, false otherwise
/// String representing the matched protection, null otherwise
public static string GetFirstMatch(
string file,
byte[] stack,
IEnumerable matchers,
bool includeDebug = false)
{
var contentMatches = FindAllMatches(file, stack, matchers, includeDebug, 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
/// Array to search
/// Enumerable of ContentMatchSets 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 ConcurrentQueue FindAllMatches(
string file,
byte[] stack,
IEnumerable matchers,
bool includeDebug,
bool stopAfterFirst)
{
// If there's no mappings, we can't match
if (matchers == null || !matchers.Any())
return null;
// Initialize the queue of matched protections
var matchedProtections = new ConcurrentQueue();
// Loop through and try everything otherwise
foreach (var matcher in matchers)
{
// Determine if the matcher passes
(bool passes, List positions) = matcher.MatchesAll(stack);
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.GetArrayVersion == null)
{
matchedProtections.Enqueue((matcher.ProtectionName ?? "Unknown Protection") + (includeDebug ? $" (Index {positionsString})" : string.Empty));
}
// Otherwise, invoke the version method
else
{
// A null version returned means the check didn't pass at the version step
string version = matcher.GetArrayVersion(file, stack, positions);
if (version == null)
continue;
matchedProtections.Enqueue($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".Trim() + (includeDebug ? $" (Index {positionsString})" : string.Empty));
}
// If we're stopping after the first protection, bail out here
if (stopAfterFirst)
return matchedProtections;
}
return matchedProtections;
}
#endregion
#region Stream Content Matching
///
/// Get all content matches for a given list of matchers
///
/// File to check for matches
/// Stream to search
/// Enumerable of ContentMatchSets 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 ConcurrentQueue GetAllMatches(
string file,
Stream stack,
IEnumerable matchers,
bool includeDebug = false)
{
return FindAllMatches(file, stack, matchers, includeDebug, false);
}
///
/// Get first content match for a given list of matchers
///
/// File to check for matches
/// Stream to search
/// Enumerable of ContentMatchSets to be run on the file
/// True to include positional data, false otherwise
/// String representing the matched protection, null otherwise
public static string GetFirstMatch(
string file,
Stream stack,
IEnumerable matchers,
bool includeDebug = false)
{
var contentMatches = FindAllMatches(file, stack, matchers, includeDebug, 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
/// Stream to search
/// Enumerable of ContentMatchSets 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 ConcurrentQueue FindAllMatches(
string file,
Stream stack,
IEnumerable matchers,
bool includeDebug,
bool stopAfterFirst)
{
// If there's no mappings, we can't match
if (matchers == null || !matchers.Any())
return null;
// Initialize the queue of matched protections
var matchedProtections = new ConcurrentQueue();
// Loop through and try everything otherwise
foreach (var matcher in matchers)
{
// Determine if the matcher passes
(bool passes, List positions) = matcher.MatchesAll(stack);
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.GetStreamVersion == null)
{
matchedProtections.Enqueue((matcher.ProtectionName ?? "Unknown Protection") + (includeDebug ? $" (Index {positionsString})" : string.Empty));
}
// Otherwise, invoke the version method
else
{
// A null version returned means the check didn't pass at the version step
string version = matcher.GetStreamVersion(file, stack, positions);
if (version == null)
continue;
matchedProtections.Enqueue($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".Trim() + (includeDebug ? $" (Index {positionsString})" : string.Empty));
}
// If we're stopping after the first protection, bail out here
if (stopAfterFirst)
return matchedProtections;
}
return matchedProtections;
}
#endregion
#region Path Matching
///
/// Get all path matches for a given list of matchers
///
/// File path to check for matches
/// Enumerable of PathMatchSets to be run on the file
/// True if any path match is a success, false if all have to match
/// List of strings representing the matched protections, null or empty otherwise
public static ConcurrentQueue GetAllMatches(string file, IEnumerable matchers, bool any = false)
{
return FindAllMatches(new List { file }, matchers, any, false);
}
//
/// Get all path matches for a given list of matchers
///
/// File paths to check for matches
/// Enumerable of PathMatchSets to be run on the file
/// True if any path match is a success, false if all have to match
/// List of strings representing the matched protections, null or empty otherwise
public static ConcurrentQueue GetAllMatches(IEnumerable files, IEnumerable matchers, bool any = false)
{
return FindAllMatches(files, matchers, any, false);
}
///
/// Get first path match for a given list of matchers
///
/// File path to check for matches
/// Enumerable of PathMatchSets to be run on the file
/// True if any path match is a success, false if all have to match
/// String representing the matched protection, null otherwise
public static string GetFirstMatch(string file, IEnumerable matchers, bool any = false)
{
var contentMatches = FindAllMatches(new List { file }, matchers, any, true);
if (contentMatches == null || !contentMatches.Any())
return null;
return contentMatches.First();
}
///
/// Get first path match for a given list of matchers
///
/// File paths to check for matches
/// Enumerable of PathMatchSets to be run on the file
/// True if any path match is a success, false if all have to match
/// String representing the matched protection, null otherwise
public static string GetFirstMatch(IEnumerable files, IEnumerable matchers, bool any = false)
{
var contentMatches = FindAllMatches(files, matchers, any, true);
if (contentMatches == null || !contentMatches.Any())
return null;
return contentMatches.First();
}
///
/// Get the required set of path matches on a per Matcher basis
///
/// File paths to check for matches
/// Enumerable of PathMatchSets to be run on the file
/// True if any path match is a success, false if all have to match
/// True to stop after the first match, false otherwise
/// List of strings representing the matched protections, null or empty otherwise
private static ConcurrentQueue FindAllMatches(IEnumerable files, IEnumerable matchers, bool any, bool stopAfterFirst)
{
// If there's no mappings, we can't match
if (matchers == null || !matchers.Any())
return new ConcurrentQueue();
// Initialize the list of matched protections
var matchedProtections = new ConcurrentQueue();
// Loop through and try everything otherwise
foreach (var matcher in matchers)
{
// Determine if the matcher passes
bool passes;
string firstMatchedString;
if (any)
{
(bool anyPasses, string matchedString) = matcher.MatchesAny(files);
passes = anyPasses;
firstMatchedString = matchedString;
}
else
{
(bool allPasses, List matchedStrings) = matcher.MatchesAll(files);
passes = allPasses;
firstMatchedString = matchedStrings.FirstOrDefault();
}
// If we don't have a pass, just continue
if (!passes)
continue;
// If we there is no version method, just return the protection name
if (matcher.GetVersion == null)
{
matchedProtections.Enqueue(matcher.ProtectionName ?? "Unknown Protection");
}
// Otherwise, invoke the version method
else
{
// A null version returned means the check didn't pass at the version step
string version = matcher.GetVersion(firstMatchedString, files);
if (version == null)
continue;
matchedProtections.Enqueue($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".Trim());
}
// If we're stopping after the first protection, bail out here
if (stopAfterFirst)
return matchedProtections;
}
return matchedProtections;
}
#endregion
}
}