using System.Diagnostics;
using System.Runtime.InteropServices;
using WhiteMagic.Native;
namespace WhiteMagic;
///
/// In-process memory reader that accesses the owning process's memory through
/// and
/// on a handle to the current
/// process. Unlike the unsafe-deref approach, this fails softly (returns
/// empty / zero bytes) on invalid or protected addresses instead of crashing
/// the host process with an .
///
///
/// This is functionally equivalent to opened on the current
/// process. It exists as a distinct type because the design (see
/// openspec/changes/whitemagic-foundation/design.md D1) treats "injected in-process"
/// as a separate mode from "external". The two modes will diverge further once the
/// InProcessInvoker delegate-call path lands.
///
public sealed class InProcessReader : MemoryBase
{
private readonly SafeMemoryHandle _handle;
private readonly IntPtr _imageBase;
private readonly int _processId;
private bool _disposed;
///
/// Creates an in-process reader for the current process.
///
public InProcessReader()
{
Process current = Process.GetCurrentProcess();
_processId = current.Id;
_handle = NativeMethods.OpenProcess(
ProcessAccess.VmRead | ProcessAccess.VmWrite | ProcessAccess.VmOperation
| ProcessAccess.QueryInformation | ProcessAccess.CreateThread | ProcessAccess.Synchronize,
false,
current.Id);
if (_handle.IsInvalid)
{
int error = Marshal.GetLastPInvokeError();
throw new InvalidOperationException(
$"OpenProcess failed for PID {current.Id}: error {error}");
}
// Process.MainModule rarely throws on the current process, but guard it
// nonetheless for parity with ExternalReader.
try
{
_imageBase = current.MainModule?.BaseAddress ?? IntPtr.Zero;
}
catch (System.ComponentModel.Win32Exception)
{
_imageBase = IntPtr.Zero;
}
}
///
public override IntPtr ImageBase => _imageBase;
///
public override SafeMemoryHandle Handle => _handle;
///
public override bool Is64Bit => Environment.Is64BitProcess;
///
public override int ProcessId => _processId;
///
public override byte[] ReadBytes(IntPtr address, int count, bool isRelative = false)
{
if (isRelative)
address = GetAbsolute(address);
return RpmHelper.ReadBytes(_handle, address, count);
}
///
public override int WriteBytes(IntPtr address, ReadOnlySpan bytes, bool isRelative = false)
{
if (isRelative)
address = GetAbsolute(address);
return RpmHelper.WriteBytes(_handle, address, bytes);
}
///
public override void Dispose()
{
if (!_disposed)
{
_disposed = true;
base.Dispose();
}
}
}