59 lines
2.1 KiB
C#
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);
|
|
}
|