using System.IO;
namespace BinaryObjectScanner.Matching
{
///
/// Content matching criteria
///
public class ContentMatch : IMatch
{
///
/// Content to match
///
public byte?[] Needle { get; set; }
///
/// Starting index for matching
///
public int Start { get; set; }
///
/// Ending index for matching
///
public int End { get; set; }
///
/// Constructor
///
/// Byte array representing the search
/// Optional starting index
/// Optional ending index
public ContentMatch(byte?[] needle, int start = -1, int end = -1)
{
Needle = needle;
Start = start;
End = end;
}
#region Array 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
/// Tuple of success and found position
public (bool success, int position) 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);
// Set the default start and end values
int start = Start;
int end = End;
// 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 we somehow have an invalid end and we haven't matched, return
if (i > stack.Length)
return (false, -1);
// Check to see if the values are equal
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
/// True if the needle matches the stack at a given index
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;
// Loop through and check the value
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 Stream Matching
///
/// Get if this match can be found in a stack
///
/// Stream to search for the given content
/// True to search from the end of the array, false from the start
/// Tuple of success and found position
public (bool success, int position) Match(Stream 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);
// Set the default start and end values
int start = Start;
int end = End;
// If start or end are not set properly, set them to defaults
if (start < 0)
start = 0;
if (end < 0)
end = (int)(stack.Length - Needle.Length);
for (int i = reverse ? end : start; reverse ? i > start : i < end; i += reverse ? -1 : 1)
{
// If we somehow have an invalid end and we haven't matched, return
if (i > stack.Length)
return (false, -1);
// Check to see if the values are equal
if (EqualAt(stack, i))
return (true, i);
}
return (false, -1);
}
///
/// Get if a stack at a certain index is equal to a needle
///
/// Stream to search for the given content
/// Starting index to check equality
/// True if the needle matches the stack at a given index
private bool EqualAt(Stream 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;
// Save the current position and move to the index
long currentPosition = stack.Position;
stack.Seek(index, SeekOrigin.Begin);
// Set the return value
bool matched = true;
// Loop through and check the value
for (int i = 0; i < Needle.Length; i++)
{
byte stackValue = (byte)stack.ReadByte();
// A null value is a wildcard
if (Needle[i] == null)
{
continue;
}
else if (stackValue != Needle[i])
{
matched = false;
break;
}
}
// Reset the position and return the value
stack.Seek(currentPosition, SeekOrigin.Begin);
return matched;
}
#endregion
}
}