Reconcile spec with Phase 2 review outcome; ignore *.log
Record the D1 deviation: InProcessReader reads the current process through ReadProcessMemory/WriteProcessMemory on a self-handle, not unsafe direct deref. Rationale: .NET cannot catch AccessViolationException, so a raw deref of a bad address terminates the host with no soft-failure path. Update the memory-access spec (new fail-soft scenario), design D1, and task 2.9. Log task 2.10: ReadString null-terminator scan is not code-unit aligned, so UTF-16/UTF-32 can match a misaligned multi-byte null or miss one split across a chunk boundary (harmless for ASCII/UTF-8, the WoW case). Ignore *.log (testrun.log). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,3 +9,4 @@ reference/
|
||||
|
||||
# Scratch
|
||||
*.tmp
|
||||
*.log
|
||||
|
||||
@@ -36,7 +36,7 @@ WhiteMagic is a **new, additive** .NET 8 library that unifies the four. It reuse
|
||||
|
||||
`MemoryBase` defines abstract `ReadBytes`/`WriteBytes`/`Read<T>`/`Write<T>`, relative/absolute addressing, and hosts the `PatchManager`. Two concrete readers:
|
||||
- `ExternalReader : MemoryBase` — `ReadProcessMemory`/`WriteProcessMemory` over a `SafeMemoryHandle`. Owns allocation, injection, and the remote-thread + main-thread executors.
|
||||
- `InProcessReader : MemoryBase` — `unsafe` direct pointer deref; owns the `DetourManager` and `InProcessInvoker`.
|
||||
- `InProcessReader : MemoryBase` — reads the current process via `ReadProcessMemory`/`WriteProcessMemory` on a self-handle; owns the `DetourManager` and `InProcessInvoker`. **(Revised from `unsafe` direct deref during Phase 2: .NET cannot catch `AccessViolationException`, so a bad deref kills the host with no soft-failure path; RPM-on-self fails soft. The in-process speed win moves to the delegate-call/detour paths, not the reader. See `specs/memory-access`.)**
|
||||
|
||||
**Why**: GreyMagic proved this abstraction lets the same higher-level code (pattern scan, patch, high-level API) run in either mode. External is the primary path for a bot host; in-process is the fast/crash-free path once injected.
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
### 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).
|
||||
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 `InProcessReader` as `unsafe` direct pointer dereference (the "fast/crash-free" path). Implementation revised it to `ReadProcessMemory`/`WriteProcessMemory` on a handle to the current process, because .NET (Core) cannot catch `AccessViolationException` (`HandleProcessCorruptedStateExceptions` is 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) like `ExternalReader`. 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 `InProcessReader` reads an unmapped or protected address
|
||||
- **THEN** it MUST return empty/`default` rather than crash the host process
|
||||
|
||||
#### 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)`
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
- [x] 2.6 Implement `ReadString`/`WriteString` on `MemoryBase` to pass 2.5
|
||||
- [x] 2.7 Add tests for relative/absolute addressing (`GetAbsolute`/`GetRelative`, `isRelative` flag)
|
||||
- [x] 2.8 Implement addressing helpers to pass 2.7
|
||||
- [x] 2.9 Add tests + `unsafe` implementation for `InProcessReader` (direct deref against own process); verify shared `MemoryBase` API works for both readers
|
||||
- [x] 2.9 Add tests + implementation for `InProcessReader` (RPM/WPM on a self-handle — see D1 deviation note; direct deref rejected because .NET cannot catch `AccessViolationException`); verify shared `MemoryBase` API works for both readers
|
||||
- [ ] 2.10 Follow-up (found in review): `ReadString` scans for the null terminator byte-by-byte, so for UTF-16/UTF-32 it can match a **misaligned** multi-byte null across a char boundary (e.g. `"A"`+U+4200 = `41 00 00 42` matches `{00,00}` at offset 1) and can miss a terminator split across the 64-byte chunk boundary. Harmless for ASCII/UTF-8 (the WoW case). Fix: align the scan to the encoding's code-unit width and carry the last `(nullLen-1)` bytes across chunks. Add a UTF-16 test.
|
||||
|
||||
## 3. Managed Assembler (spec: managed-assembler)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user