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>
58 lines
3.0 KiB
Markdown
58 lines
3.0 KiB
Markdown
## 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)`
|