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(); } } }