Implement core diagnostic memory layer, execution helpers, and high-level facade slices

Implemented:
- Core: UTF-16 ReadString boundary/alignment fix, target bitness and process id on MemoryBase
- function interception: PatchManager, DetourManager, InstructionAnalyzer, MainThreadDispatcher
- Execution: BackgroundTaskExecutor, InProcessInvoker
- High-level: Magic facade, RemotePointer, async wrappers
- Discovery/external code loading/Window groundwork (PEB/TEB, pattern scanning, raw allocations, DLL external code loading, window/input)

Tests: 180 passing, 4 integration/interactive tests skipped.
This commit is contained in:
kbe
2026-07-21 23:43:14 +02:00
parent a0ca7050a2
commit 3f0bea6bd4
44 changed files with 5595 additions and 84 deletions
+180
View File
@@ -0,0 +1,180 @@
using System.ComponentModel;
using System.Runtime.InteropServices;
using WhiteMagic.Native;
namespace WhiteMagic.Memory;
/// <summary>
/// Represents a chunk of remote memory subdivided into named regions.
/// </summary>
public sealed class AllocatedMemory : IDisposable
{
private readonly MemoryBase _memory;
private readonly IntPtr _baseAddress;
private readonly int _size;
private readonly Dictionary<string, int> _regions;
private bool _disposed;
/// <summary>
/// Creates a new allocated memory chunk.
/// </summary>
/// <param name="memory">The memory accessor.</param>
/// <param name="size">The size of the allocation in bytes.</param>
/// <param name="protection">The initial memory protection.</param>
/// <exception cref="Win32Exception">Allocation fails.</exception>
public AllocatedMemory(MemoryBase memory, int size, MemoryProtectionType protection = MemoryProtectionType.ExecuteReadWrite)
{
ArgumentNullException.ThrowIfNull(memory);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(size);
_memory = memory;
_size = size;
_regions = new Dictionary<string, int>();
// Allocate using VirtualAllocEx
_baseAddress = NativeMethods.VirtualAllocEx(
memory.Handle,
IntPtr.Zero,
size,
MemoryAllocationType.Commit | MemoryAllocationType.Reserve,
protection);
if (_baseAddress == IntPtr.Zero)
{
int error = Marshal.GetLastPInvokeError();
throw new Win32Exception(error, $"VirtualAllocEx failed (size={size}).");
}
}
/// <summary>
/// Gets the base address of the allocated memory.
/// </summary>
public IntPtr BaseAddress => _baseAddress;
/// <summary>
/// Gets the size of the allocation in bytes.
/// </summary>
public int Size => _size;
/// <summary>
/// Adds a named region at a specific offset within the allocation.
/// </summary>
/// <param name="name">The unique name for the region.</param>
/// <param name="offset">The offset from the base address.</param>
/// <exception cref="ArgumentException">A region with this name already exists.</exception>
/// <exception cref="ArgumentOutOfRangeException">Offset is outside the allocation bounds.</exception>
public void AddRegion(string name, int offset)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentNullException.ThrowIfNull(name);
ArgumentOutOfRangeException.ThrowIfNegative(offset);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _size);
if (_regions.ContainsKey(name))
throw new ArgumentException($"Region '{name}' already exists.", nameof(name));
_regions[name] = offset;
}
/// <summary>
/// Gets the absolute address of a named region.
/// </summary>
/// <param name="name">The region name.</param>
/// <returns>The absolute address of the region.</returns>
/// <exception cref="ArgumentException">No region with this name exists.</exception>
public IntPtr AddressOf(string name)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentNullException.ThrowIfNull(name);
if (!_regions.TryGetValue(name, out int offset))
throw new ArgumentException($"Region '{name}' does not exist.", nameof(name));
return _baseAddress + offset;
}
/// <summary>
/// Reads a value of type <typeparamref name="T"/> from a named region.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="name">The region name.</param>
/// <returns>The value read from memory.</returns>
/// <exception cref="ArgumentException">No region with this name exists.</exception>
public T Read<T>(string name) where T : struct
{
ObjectDisposedException.ThrowIf(_disposed, this);
IntPtr address = AddressOf(name);
return _memory.Read<T>(address);
}
/// <summary>
/// Writes a value of type <typeparamref name="T"/> to a named region.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="name">The region name.</param>
/// <param name="value">The value to write.</param>
/// <returns><see langword="true"/> if all bytes were written.</returns>
/// <exception cref="ArgumentException">No region with this name exists.</exception>
public bool Write<T>(string name, T value) where T : struct
{
ObjectDisposedException.ThrowIf(_disposed, this);
IntPtr address = AddressOf(name);
return _memory.Write(address, value);
}
/// <summary>
/// Reads bytes from a named region.
/// </summary>
/// <param name="name">The region name.</param>
/// <param name="count">The number of bytes to read.</param>
/// <returns>The bytes read from memory.</returns>
/// <exception cref="ArgumentException">No region with this name exists.</exception>
public byte[] ReadBytes(string name, int count)
{
ObjectDisposedException.ThrowIf(_disposed, this);
IntPtr address = AddressOf(name);
return _memory.ReadBytes(address, count);
}
/// <summary>
/// Writes bytes to a named region.
/// </summary>
/// <param name="name">The region name.</param>
/// <param name="bytes">The bytes to write.</param>
/// <returns>The number of bytes written.</returns>
/// <exception cref="ArgumentException">No region with this name exists.</exception>
public int WriteBytes(string name, ReadOnlySpan<byte> bytes)
{
ObjectDisposedException.ThrowIf(_disposed, this);
IntPtr address = AddressOf(name);
return _memory.WriteBytes(address, bytes);
}
/// <summary>
/// Frees the allocated memory.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
// Free using VirtualFreeEx
if (_baseAddress != IntPtr.Zero)
{
NativeMethods.VirtualFreeEx(
_memory.Handle,
_baseAddress,
0,
MemoryFreeType.Release);
}
_regions.Clear();
}
}
}