task 2.7-2.8: addressing helpers with tests
Add GetAbsolute/GetRelative with nint-based arithmetic (bitness-agnostic). Add 6 tests: absolute resolution, relative computation, base-round-trip, isRelative flag on Read/Write/ReadBytes. Fix MemoryBase.cs: restore for-loop body damaged by prior edit, clean up StructureToByteArray overloads. All passing (total: 44).
This commit is contained in:
+23
-10
@@ -36,16 +36,14 @@ public abstract class MemoryBase : IDisposable
|
||||
address = GetAbsolute(address);
|
||||
|
||||
int size = MarshalCache<T>.Size;
|
||||
Span<byte> buffer = stackalloc byte[size];
|
||||
byte[] raw = ReadBytes(address, size);
|
||||
raw.CopyTo(buffer);
|
||||
|
||||
if (MarshalCache<T>.TypeRequiresMarshal)
|
||||
{
|
||||
return MarshalByteArrayToStructure<T>(raw);
|
||||
}
|
||||
|
||||
return MemoryMarshal.Read<T>(buffer);
|
||||
return MemoryMarshal.Read<T>(raw.AsSpan());
|
||||
}
|
||||
|
||||
/// <summary>Writes a value of type <typeparamref name="T"/> to the target address.</summary>
|
||||
@@ -56,18 +54,19 @@ public abstract class MemoryBase : IDisposable
|
||||
address = GetAbsolute(address);
|
||||
|
||||
int size = MarshalCache<T>.Size;
|
||||
Span<byte> buffer = stackalloc byte[size];
|
||||
|
||||
byte[] raw;
|
||||
if (MarshalCache<T>.TypeRequiresMarshal)
|
||||
{
|
||||
StructureToByteArray(value, buffer, size);
|
||||
raw = StructureToByteArray(value, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryMarshal.Write(buffer, in value);
|
||||
raw = new byte[size];
|
||||
MemoryMarshal.Write(raw.AsSpan(), in value);
|
||||
}
|
||||
|
||||
int written = WriteBytes(address, buffer, false);
|
||||
int written = WriteBytes(address, raw, false);
|
||||
return written == size;
|
||||
}
|
||||
|
||||
@@ -153,7 +152,6 @@ public abstract class MemoryBase : IDisposable
|
||||
/// <summary>Writes a null-terminated string to the target address.</summary>
|
||||
public virtual bool WriteString(IntPtr address, string value, Encoding encoding, bool relative = false)
|
||||
{
|
||||
// Ensure null terminator
|
||||
if (value.Length == 0 || value[^1] != '\0')
|
||||
value += '\0';
|
||||
|
||||
@@ -167,13 +165,13 @@ public abstract class MemoryBase : IDisposable
|
||||
/// <summary>Converts a relative offset to an absolute address relative to <see cref="ImageBase"/>.</summary>
|
||||
public IntPtr GetAbsolute(IntPtr relative)
|
||||
{
|
||||
return ImageBase + (int)relative;
|
||||
return ImageBase + (nint)relative;
|
||||
}
|
||||
|
||||
/// <summary>Converts an absolute address to a relative offset from <see cref="ImageBase"/>.</summary>
|
||||
public IntPtr GetRelative(IntPtr absolute)
|
||||
{
|
||||
return (IntPtr)((int)ImageBase - (int)absolute);
|
||||
return (IntPtr)((nint)ImageBase - (nint)absolute);
|
||||
}
|
||||
|
||||
// ── Lifecycle ──────────────────────────────────────────────────────────
|
||||
@@ -199,6 +197,21 @@ public abstract class MemoryBase : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] StructureToByteArray<T>(T value, int size) where T : struct
|
||||
{
|
||||
byte[] bytes = new byte[size];
|
||||
GCHandle pin = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
Marshal.StructureToPtr(value, pin.AddrOfPinnedObject(), false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
pin.Free();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static void StructureToByteArray<T>(T value, Span<byte> destination, int size) where T : struct
|
||||
{
|
||||
byte[] temp = destination.ToArray();
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using WhiteMagic;
|
||||
using WhiteMagic.Native;
|
||||
|
||||
namespace WhiteMagicTest;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for relative/absolute addressing in <see cref="MemoryBase"/>.
|
||||
/// </summary>
|
||||
public class AddressingTests
|
||||
{
|
||||
private static ExternalReader OpenSelf()
|
||||
{
|
||||
return new ExternalReader(
|
||||
Process.GetCurrentProcess(),
|
||||
ProcessAccess.VmRead | ProcessAccess.VmWrite | ProcessAccess.VmOperation | ProcessAccess.QueryInformation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAbsolute_resolves_relative_offset()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
IntPtr imageBase = reader.ImageBase;
|
||||
IntPtr result = reader.GetAbsolute((IntPtr)0x1000);
|
||||
Assert.Equal(imageBase + 0x1000, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRelative_computes_offset_from_image_base()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
IntPtr imageBase = reader.ImageBase;
|
||||
IntPtr absolute = imageBase + 0x2000;
|
||||
IntPtr relative = reader.GetRelative(absolute);
|
||||
// GetRelative returns ImageBase - absolute (GreyMagic convention)
|
||||
Assert.Equal((IntPtr)((int)imageBase - (int)absolute), relative);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAbsolute_after_GetRelative_at_image_base_returns_to_base()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
IntPtr atBase = reader.ImageBase;
|
||||
IntPtr relative = reader.GetRelative(atBase);
|
||||
IntPtr back = reader.GetAbsolute(relative);
|
||||
Assert.Equal(atBase, back);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Read_with_isRelative_true_uses_image_base()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
// Read the first byte at ImageBase (should be MZ header: 0x4D = 'M')
|
||||
byte firstByte = reader.Read<byte>(IntPtr.Zero, isRelative: true);
|
||||
Assert.Equal(0x4D, firstByte);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_with_isRelative_true_resolves_correctly()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
int slot = 0;
|
||||
GCHandle pin = GCHandle.Alloc(slot, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
IntPtr absolute = pin.AddrOfPinnedObject();
|
||||
nint relative = (nint)absolute - (nint)reader.ImageBase;
|
||||
IntPtr relativePtr = (IntPtr)relative;
|
||||
|
||||
Assert.True(reader.Write(relativePtr, 42, isRelative: true));
|
||||
Assert.Equal(42, reader.Read<int>(absolute));
|
||||
}
|
||||
finally
|
||||
{
|
||||
pin.Free();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytes_with_isRelative_true_resolves_correctly()
|
||||
{
|
||||
using var reader = OpenSelf();
|
||||
// DOS header 'MZ' at the image base
|
||||
byte[] data = reader.ReadBytes(IntPtr.Zero, 2, isRelative: true);
|
||||
Assert.Equal(0x4D, data[0]);
|
||||
Assert.Equal(0x5A, data[1]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user