Add memory-region query, enumeration, and scoped protection
Implements VirtualQueryEx + MEMORY_BASIC_INFORMATION wrappers, the immutable MemoryRegion record, the ProtectionScope disposable helper, and MemoryBase.QueryRegion/EnumerateRegions/ChangeProtection. Closes section 1 of add-thread-region-finder.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using WhiteMagic.Hooking;
|
||||
using WhiteMagic.Memory;
|
||||
using WhiteMagic.Native;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
@@ -268,6 +270,58 @@ public abstract class MemoryBase : IDisposable
|
||||
return (IntPtr)((nint)absolute - (nint)ImageBase);
|
||||
}
|
||||
|
||||
// ── Memory region query ────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Queries the memory region that contains <paramref name="address"/> in the target
|
||||
/// process using <c>VirtualQueryEx</c>.
|
||||
/// </summary>
|
||||
/// <returns>An immutable snapshot of the region.</returns>
|
||||
/// <exception cref="InvalidOperationException">The query fails.</exception>
|
||||
public MemoryRegion QueryRegion(IntPtr address)
|
||||
{
|
||||
nuint bufferSize = (nuint)Marshal.SizeOf<MemoryBasicInformation>();
|
||||
nuint result = NativeMethods.VirtualQueryEx(Handle, address, out MemoryBasicInformation info, bufferSize);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
int error = Marshal.GetLastPInvokeError();
|
||||
throw new InvalidOperationException($"VirtualQueryEx failed for address 0x{address:X}: error {error}.");
|
||||
}
|
||||
|
||||
return new MemoryRegion(info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the memory regions of the target process from the lowest address upward.
|
||||
/// The walk is lazy; callers can stop early without walking the entire address space.
|
||||
/// </summary>
|
||||
public IEnumerable<MemoryRegion> EnumerateRegions()
|
||||
{
|
||||
IntPtr address = IntPtr.Zero;
|
||||
nuint bufferSize = (nuint)Marshal.SizeOf<MemoryBasicInformation>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
nuint result = NativeMethods.VirtualQueryEx(Handle, address, out MemoryBasicInformation info, bufferSize);
|
||||
if (result == 0)
|
||||
yield break;
|
||||
|
||||
yield return new MemoryRegion(info);
|
||||
address = info.BaseAddress + (nint)info.RegionSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the page protection on a region of memory and returns a disposable scope
|
||||
/// that restores the original protection on dispose, including when an exception escapes
|
||||
/// the guarded body.
|
||||
/// </summary>
|
||||
public ProtectionScope ChangeProtection(IntPtr address, nint size, MemoryProtectionType protection)
|
||||
{
|
||||
return new ProtectionScope(this, address, size, protection);
|
||||
}
|
||||
|
||||
// ── Lifecycle ──────────────────────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user