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:
@@ -0,0 +1,254 @@
|
||||
using System.ComponentModel;
|
||||
using WhiteMagic;
|
||||
using WhiteMagic.Memory;
|
||||
using WhiteMagic.Native;
|
||||
|
||||
namespace WhiteMagicTest.Memory;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="AllocatedMemory"/>.
|
||||
/// </summary>
|
||||
public class AllocatedMemoryTests
|
||||
{
|
||||
private static InProcessReader CreateReader()
|
||||
{
|
||||
return new InProcessReader();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_allocates_memory_with_execute_read_write_protection()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
Assert.NotEqual(IntPtr.Zero, allocated.BaseAddress);
|
||||
Assert.Equal(4096, allocated.Size);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_with_custom_protection()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096, MemoryProtectionType.ReadOnly);
|
||||
|
||||
Assert.NotEqual(IntPtr.Zero, allocated.BaseAddress);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_throws_on_negative_size()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
new AllocatedMemory(reader, -1));
|
||||
|
||||
Assert.Equal("size", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_throws_on_zero_size()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
new AllocatedMemory(reader, 0));
|
||||
|
||||
Assert.Equal("size", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRegion_adds_named_region()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
allocated.AddRegion("test", 100);
|
||||
|
||||
Assert.Equal(100, allocated.AddressOf("test") - allocated.BaseAddress);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRegion_throws_on_duplicate_name()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
allocated.AddRegion("test", 100);
|
||||
|
||||
var ex = Assert.Throws<ArgumentException>(() =>
|
||||
allocated.AddRegion("test", 200));
|
||||
|
||||
Assert.Contains("already exists", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRegion_throws_on_negative_offset()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
allocated.AddRegion("test", -1));
|
||||
|
||||
Assert.Equal("offset", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRegion_throws_on_offset_exceeding_size()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
allocated.AddRegion("test", 4096));
|
||||
|
||||
Assert.Equal("offset", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddressOf_returns_correct_address()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
allocated.AddRegion("region1", 0);
|
||||
allocated.AddRegion("region2", 100);
|
||||
allocated.AddRegion("region3", 200);
|
||||
|
||||
Assert.Equal(allocated.BaseAddress, allocated.AddressOf("region1"));
|
||||
Assert.Equal(allocated.BaseAddress + 100, allocated.AddressOf("region2"));
|
||||
Assert.Equal(allocated.BaseAddress + 200, allocated.AddressOf("region3"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddressOf_throws_on_unknown_region()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
var ex = Assert.Throws<ArgumentException>(() =>
|
||||
allocated.AddressOf("unknown"));
|
||||
|
||||
Assert.Contains("does not exist", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_and_Read_int_roundtrip()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
allocated.AddRegion("value", 0);
|
||||
|
||||
int original = unchecked((int)0xDEADBEEF);
|
||||
Assert.True(allocated.Write("value", original));
|
||||
|
||||
int read = allocated.Read<int>("value");
|
||||
Assert.Equal(original, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_and_Read_long_roundtrip()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
allocated.AddRegion("value", 8);
|
||||
|
||||
long original = 0x123456789ABCDEF0;
|
||||
Assert.True(allocated.Write("value", original));
|
||||
|
||||
long read = allocated.Read<long>("value");
|
||||
Assert.Equal(original, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytes_and_ReadBytes_roundtrip()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
allocated.AddRegion("buffer", 0);
|
||||
|
||||
byte[] original = { 0x01, 0x02, 0x03, 0x04, 0x05 };
|
||||
int written = allocated.WriteBytes("buffer", original);
|
||||
Assert.Equal(original.Length, written);
|
||||
|
||||
byte[] read = allocated.ReadBytes("buffer", original.Length);
|
||||
Assert.Equal(original, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_frees_memory()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
var allocated = new AllocatedMemory(reader, 4096);
|
||||
IntPtr baseAddr = allocated.BaseAddress;
|
||||
|
||||
Assert.NotEqual(IntPtr.Zero, baseAddr);
|
||||
|
||||
allocated.Dispose();
|
||||
|
||||
// After dispose, accessing properties should throw ObjectDisposedException
|
||||
Assert.Throws<ObjectDisposedException>(() =>
|
||||
allocated.AddressOf("any"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Multiple_regions_independent_access()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
allocated.AddRegion("a", 0);
|
||||
allocated.AddRegion("b", 4);
|
||||
allocated.AddRegion("c", 8);
|
||||
|
||||
Assert.True(allocated.Write("a", 0x11111111));
|
||||
Assert.True(allocated.Write("b", 0x22222222));
|
||||
Assert.True(allocated.Write("c", 0x33333333));
|
||||
|
||||
Assert.Equal(0x11111111, allocated.Read<int>("a"));
|
||||
Assert.Equal(0x22222222, allocated.Read<int>("b"));
|
||||
Assert.Equal(0x33333333, allocated.Read<int>("c"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_to_unknown_region_throws()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
var ex = Assert.Throws<ArgumentException>(() =>
|
||||
allocated.Write("unknown", 42));
|
||||
|
||||
Assert.Contains("does not exist", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Read_from_unknown_region_throws()
|
||||
{
|
||||
using var reader = CreateReader();
|
||||
|
||||
using var allocated = new AllocatedMemory(reader, 4096);
|
||||
|
||||
var ex = Assert.Throws<ArgumentException>(() =>
|
||||
allocated.Read<int>("unknown"));
|
||||
|
||||
Assert.Contains("does not exist", ex.Message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user