IMemoryBackend и их цепочкиIMemoryBackend в EyeAuras маленький, и это хорошо: его удобно использовать не только как "финальный reader", но и как слой-декоратор. Поэтому поверх одного backend'а легко навесить статистику, логирование, кэширование или временную трассировку.
Вместо такой схемы:
MemoryAccessor -> DriverBackend
можно собрать такую:
MemoryAccessor -> CacheBackend -> StatsBackend -> LoggingBackend -> DriverBackend
У IProcessMemory есть всё, что нужно для такой подмены:
var currentBackend = process.Memory.Backend;
process.Memory.UseMemoryBackend(new MyWrappedBackend(currentBackend));
После этого новый low-level слой начинает использоваться и для обычного чтения, и для MemoryOfModule(...), и для ReadExports(), ReadImports(), сигнатур и code cave.
internal sealed class MyTracingBackend : IMemoryBackend
{
private readonly IMemoryBackend inner;
public MyTracingBackend(IMemoryBackend inner) => this.inner = inner;
public bool IsValid => inner.IsValid;
public long BaseAddress => inner.BaseAddress;
public int MemorySize => inner.MemorySize;
public bool TryReadMemory(IntPtr address, Span<byte> target)
{
return inner.TryReadMemory(address, target);
}
public bool TryWriteMemory(IntPtr address, ReadOnlySpan<byte> source)
{
return inner.TryWriteMemory(address, source);
}
public void Dispose() => inner.Dispose();
}
Это базовый шаблон. Дальше вы добавляете свой код до или после вызова inner.
null, near-null, мусорные и подозрительные адресаИменно такие backend'ы уже были в ScriptShock.MemoryApi: StatsMemoryBackend, SuspiciousAddressLoggingBackend, PageCachingMemoryBackend, CompositeMemoryBackend.
Эти две цепочки отвечают на разные вопросы:
Stats -> Cache -> Driver
Cache -> Stats -> Driver
В первом случае статистика видит итоговую пользовательскую картину. Во втором — только реальные обращения, которые дошли до драйвера. Обычно ближе к драйверу кладут то, что должно видеть физические обращения, а ближе к MemoryAccessor то, что должно видеть финальное поведение.
TryReadMemory(...) и TryWriteMemory(...) без явной причины.Dispose() должен корректно пробрасываться вниз.