using System.Diagnostics; using System.Runtime.InteropServices; using WhiteMagic.Native; namespace WhiteMagic; /// /// Out-of-process memory reader that accesses the target's memory through /// and /// . /// public sealed class ExternalReader : MemoryBase { private readonly SafeMemoryHandle _handle; private readonly IntPtr _imageBase; private bool _disposed; /// /// The default access rights: enough to read, write, allocate, query, run a remote /// thread, and wait on it. This deliberately omits , /// which over-requests and makes OpenProcess fail on protected processes where /// these narrower rights would succeed. /// public const ProcessAccess DefaultAccess = ProcessAccess.VmRead | ProcessAccess.VmWrite | ProcessAccess.VmOperation | ProcessAccess.QueryInformation | ProcessAccess.CreateThread | ProcessAccess.Synchronize; /// /// Opens a process for external memory access. /// /// The target process. /// The access rights to request. Defaults to /// . public ExternalReader(Process process, ProcessAccess desiredAccess = DefaultAccess) { _handle = NativeMethods.OpenProcess(desiredAccess, false, process.Id); if (_handle.IsInvalid) { int error = Marshal.GetLastPInvokeError(); throw new InvalidOperationException( $"OpenProcess failed for PID {process.Id}: error {error}"); } // Process.MainModule throws Win32Exception for a bitness-mismatched or protected // target. A missing image base must not sink the whole reader — callers can still // use absolute addresses when ImageBase is unknown. try { _imageBase = process.MainModule?.BaseAddress ?? IntPtr.Zero; } catch (System.ComponentModel.Win32Exception) { _imageBase = IntPtr.Zero; } } /// public override IntPtr ImageBase => _imageBase; /// public override SafeMemoryHandle Handle => _handle; /// 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; _handle.Dispose(); } } }