Files
whitemagic/WhiteMagic/Native/NativeMethods.cs
T
kbeandClaude Opus 4.8 7c5e72e0e0 Harden memory layer: fix char sizing, ref structs, count guards, ReadString advance
Second-review fixes, each covered by a regression test in MemoryHardeningTests:

- MarshalCache: special-case char (Size=2; Marshal.SizeOf reports 1/ANSI but the
  blittable path reads a 2-byte UTF-16 unit). TypeRequiresMarshal now also trips on
  RuntimeHelpers.IsReferenceOrContainsReferences<T>() so reference-carrying structs
  route to the marshal path instead of throwing in MemoryMarshal.Read. Document that
  the MarshalAs scan is top-level only.
- MemoryBase.Read<T>(count): reject negative count (ArgumentOutOfRangeException) and
  guard elementSize*count overflow. Same overflow guard on Write<T>(values).
- MemoryBase.ReadString: advance by bytes actually read, not the requested amount, so
  a partial read no longer skips the unread tail of the window.
- ExternalReader: default to a minimal access set (not AllAccess, which over-requests
  and fails on protected processes); wrap Process.MainModule in try/catch so a
  bitness-mismatched or protected target yields ImageBase=Zero instead of throwing.
- NativeMethods: WaitForSingleObject and CreateRemoteThread's threadId are DWORD (uint),
  not int — the signatures no longer sign-flip.

Deferred: hoisting the identical ExternalReader/InProcessReader byte-IO into MemoryBase
(cosmetic; skipped to avoid colliding with concurrent Phase 3 edits).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 19:47:24 +02:00

138 lines
6.0 KiB
C#

using System.Runtime.InteropServices;
namespace WhiteMagic.Native;
/// <summary>
/// P/Invoke declarations for the Win32 process, memory, thread, and module
/// APIs that WhiteMagic uses. Every declaration uses <see cref="LibraryImportAttribute"/>
/// (source-generated interop). SetLastError is enabled on all calls that the
/// Win32 API documents as setting a thread-local last-error value.
/// </summary>
internal static partial class NativeMethods
{
// ── Process ──────────────────────────────────────────────────────────────
/// <summary>Opens an existing process and returns a handle to it.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
internal static partial SafeMemoryHandle OpenProcess(
ProcessAccess desiredAccess,
[MarshalAs(UnmanagedType.Bool)] bool inheritHandle,
int processId);
/// <summary>Closes an open object handle.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool CloseHandle(IntPtr handle);
// ── Memory ───────────────────────────────────────────────────────────────
/// <summary>Reads memory from a process.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool ReadProcessMemory(
SafeMemoryHandle process,
IntPtr baseAddress,
Span<byte> buffer,
int size,
out nint bytesRead);
/// <summary>Writes memory to a process.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool WriteProcessMemory(
SafeMemoryHandle process,
IntPtr baseAddress,
ReadOnlySpan<byte> buffer,
int size,
out nint bytesWritten);
/// <summary>Reserves or commits a region of memory in a process.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
internal static partial IntPtr VirtualAllocEx(
SafeMemoryHandle process,
IntPtr address,
nint size,
MemoryAllocationType allocationType,
MemoryProtectionType protect);
/// <summary>Changes the protection on a committed region of memory.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool VirtualProtectEx(
SafeMemoryHandle process,
IntPtr address,
nint size,
MemoryProtectionType newProtect,
out MemoryProtectionType oldProtect);
/// <summary>Releases or decommits a region of memory in a process.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool VirtualFreeEx(
SafeMemoryHandle process,
IntPtr address,
nint size,
MemoryFreeType freeType);
// ── Threading ────────────────────────────────────────────────────────────
/// <summary>Creates a thread that runs in the virtual address space of a process.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
internal static partial SafeMemoryHandle CreateRemoteThread(
SafeMemoryHandle process,
IntPtr threadAttributes,
nint stackSize,
IntPtr startAddress,
IntPtr parameter,
ThreadCreationFlags creationFlags,
out uint threadId);
/// <summary>Sets a 64-bit thread context (AMD64).</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool SetThreadContext(
SafeMemoryHandle thread,
ref Context64 context);
/// <summary>Gets a 64-bit thread context (AMD64).</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool GetThreadContext(
SafeMemoryHandle thread,
ref Context64 context);
/// <summary>Sets a 32-bit (WOW64) thread context.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool Wow64SetThreadContext(
SafeMemoryHandle thread,
ref Context32 context);
/// <summary>Gets a 32-bit (WOW64) thread context.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool Wow64GetThreadContext(
SafeMemoryHandle thread,
ref Context32 context);
// ── Modules ──────────────────────────────────────────────────────────────
/// <summary>Loads a module into the calling process.</summary>
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryW")]
internal static partial IntPtr LoadLibrary(
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
/// <summary>Returns the address of a function or variable from a loaded module.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
internal static partial IntPtr GetProcAddress(
IntPtr hModule,
[MarshalAs(UnmanagedType.LPStr)] string lpProcName);
/// <summary>Waits until an object is signaled or the timeout elapses. Returns a
/// <c>WAIT_*</c> status (DWORD); <c>WAIT_FAILED</c> is <c>0xFFFFFFFF</c>.</summary>
[LibraryImport("kernel32.dll", SetLastError = true)]
internal static partial uint WaitForSingleObject(
SafeMemoryHandle handle,
uint milliseconds);
}