Files

59 lines
2.1 KiB
C#

using System.Security.Cryptography;
namespace WxAgent.Core;
/// <summary>Finds a fixed byte pattern across contiguous read-only memory chunks.</summary>
public sealed class MemoryPatternScanner : IDisposable
{
private readonly byte[] _pattern;
private readonly byte[] _tail;
private int _tailLength;
private ulong? _end;
public MemoryPatternScanner(byte[] pattern)
{
if (pattern.Length is < 1 or > 1024) throw new ArgumentOutOfRangeException(nameof(pattern));
_pattern = pattern.ToArray();
_tail = new byte[pattern.Length - 1];
}
public IReadOnlyList<ulong> Feed(ReadOnlySpan<byte> bytes, ulong address)
{
if (_end != address) _tailLength = 0;
var matches = new List<ulong>();
Span<byte> boundary = stackalloc byte[_tail.Length * 2];
var headLength = Math.Min(bytes.Length, _tail.Length);
_tail.AsSpan(0, _tailLength).CopyTo(boundary);
bytes[..headLength].CopyTo(boundary[_tailLength..]);
var joined = boundary[..(_tailLength + headLength)];
for (var i = 0; i < _tailLength; i++)
if (i + _pattern.Length > _tailLength && i + _pattern.Length <= joined.Length
&& joined.Slice(i, _pattern.Length).SequenceEqual(_pattern))
matches.Add(address - (ulong)_tailLength + (ulong)i);
var offset = 0;
while (offset <= bytes.Length - _pattern.Length)
{
var found = bytes[offset..].IndexOf(_pattern);
if (found < 0) break;
offset += found;
matches.Add(address + (ulong)offset);
offset++;
}
if (bytes.Length >= _tail.Length)
{
bytes[^_tail.Length..].CopyTo(_tail);
_tailLength = _tail.Length;
}
else
{
_tailLength = Math.Min(joined.Length, _tail.Length);
joined[^_tailLength..].CopyTo(_tail);
}
CryptographicOperations.ZeroMemory(boundary);
_end = checked(address + (ulong)bytes.Length);
return matches;
}
public void Dispose() => CryptographicOperations.ZeroMemory(_tail);
}