3.9 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, reading the current process through ReadProcessMemory/WriteProcessMemory on a self-handle).
Deviation from design D1. D1 originally specified
InProcessReaderasunsafedirect pointer dereference (the "fast/crash-free" path). Implementation revised it toReadProcessMemory/WriteProcessMemoryon a handle to the current process, because .NET (Core) cannot catchAccessViolationException(HandleProcessCorruptedStateExceptionsis removed), so a raw deref of a bad address terminates the host process with no soft-failure path. RPM on a self-handle fails soft (returns empty) likeExternalReader. The in-process performance win therefore moves to the delegate-call and detour paths (InProcessInvoker,DetourManager), not the reader.
Scenario: in-process read fails soft on an invalid address
- WHEN an
InProcessReaderreads an unmapped or protected address - THEN it MUST return empty/
defaultrather than crash the host process
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)