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>
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
ExternalReaderopens a target process and writes a value withWrite<int>(addr, 0x1234)then reads it back withRead<int>(addr) - THEN the returned value MUST equal
0x1234
Scenario: in-process read of own memory
- WHEN an
InProcessReaderreads 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
MemoryBaseabstract type - THEN it MUST operate unchanged against both
ExternalReaderandInProcessReader
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 typeT - THEN
Marshal.SizeOfand type inspection forTMUST 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
countelements read contiguously fromaddr
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 thenReadString(addr, Encoding.ASCII) - THEN the result MUST equal
"hello"
Scenario: read stops at null terminator
- WHEN a null-terminated string shorter than
maxLengthis 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)