Files
whitemagic/openspec/changes/whitemagic-foundation/specs/memory-access/spec.md
T
kbeandClaude Opus 4.8 4405af15fd Add whitemagic-foundation OpenSpec design; isolate reference libs
Design-only foundation for WhiteMagic, a .NET 8 x64 library unifying the
four studied process-manipulation libs. Adds proposal, design (7 decisions),
7 capability specs, and TDD task breakdown; all validate strict.

Move Blackmagic, Blackmagic-old, GreyMagic, MemorySharp, fasm into
reference/ (gitignored) — studied, not built here; each has its own
upstream repo and nested .git. Rewrite plan doc paths to reference/.

Corrects two factual defects found in review:
- current BlackMagic has no D3D EndScene hook; MainThreadPump is net-new
  built on DetourManager, not a port
- no BlackMagic.slnx exists; task 1.3 creates a fresh solution

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

3.0 KiB

ADDED Requirements

Requirement: Abstract memory base with two readers

WhiteMagic SHALL expose an abstract MemoryBase type defining ReadBytes, WriteBytes, generic Read<T>/Write<T>, array read/write, and string read/write, with two concrete implementations: ExternalReader (out-of-process via ReadProcessMemory/WriteProcessMemory) and InProcessReader (in-process via direct pointer dereference).

Scenario: external read round-trip

  • WHEN an ExternalReader opens a target process and writes a value with Write<int>(addr, 0x1234) then reads it back with Read<int>(addr)
  • THEN the returned value MUST equal 0x1234

Scenario: in-process read of own memory

  • WHEN an InProcessReader reads a known address in its own process
  • THEN the value MUST match a direct managed read of the same address

Scenario: shared API surface

  • WHEN code is written against the MemoryBase abstract type
  • THEN it MUST operate unchanged against both ExternalReader and InProcessReader

Requirement: Typed read/write via marshal cache

MemoryBase SHALL support generic Read<T>/Write<T> for blittable and marshalled struct types, using a per-type MarshalCache<T> that caches size, type code, and marshalling requirements to avoid per-call reflection.

Scenario: blittable struct round-trip

  • WHEN a blittable [StructLayout(LayoutKind.Sequential)] struct is written and read back
  • THEN all fields MUST be preserved exactly

Scenario: marshal cache computed once

  • WHEN Read<T> is invoked repeatedly for the same type T
  • THEN Marshal.SizeOf and type inspection for T MUST be computed at most once and reused

Scenario: array read

  • WHEN Read<T>(addr, count) is called
  • THEN it MUST return an array of exactly count elements read contiguously from addr

Requirement: String read and write with encoding

MemoryBase SHALL read and write strings with a caller-specified Encoding and a maximum length, terminating reads at a null terminator or the maximum length.

Scenario: ASCII write then read

  • WHEN WriteString(addr, "hello", Encoding.ASCII) is called then ReadString(addr, Encoding.ASCII)
  • THEN the result MUST equal "hello"

Scenario: read stops at null terminator

  • WHEN a null-terminated string shorter than maxLength is read
  • THEN the returned string MUST exclude the terminator and everything after it

Requirement: Relative and absolute addressing

MemoryBase SHALL convert between addresses relative to the module image base and absolute addresses via GetAbsolute and GetRelative, and accept an isRelative flag on read/write operations.

Scenario: relative resolves against image base

  • WHEN GetAbsolute(relative) is called with the process image base known
  • THEN the result MUST equal imageBase + relative

Scenario: read with isRelative

  • WHEN Read<int>(offset, isRelative: true) is called
  • THEN the read MUST occur at GetAbsolute(offset)