Files
whitemagic/openspec/changes/whitemagic-foundation/specs/remote-execution/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.3 KiB

ADDED Requirements

Requirement: Three-tier execution model

WhiteMagic SHALL provide three execution strategies selected by payload safety: RemoteThreadExecutor (via CreateRemoteThread), MainThreadPump (work marshalled onto the target's own thread), and InProcessInvoker (direct native-delegate calls when injected in-process).

Scenario: strategies are distinct and selectable

  • WHEN a caller chooses an execution strategy
  • THEN each of remote-thread, main-thread-pump, and in-process MUST be individually invokable

Scenario: main-thread pump is the documented default for game state

  • WHEN documentation or API guidance describes calling functions that touch single-thread-affinity process state
  • THEN it MUST direct callers to the main-thread pump, not CreateRemoteThread

Requirement: Remote-thread execution for thread-agnostic payloads

RemoteThreadExecutor SHALL create a remote thread at a target address using a calling-convention-aware stub, wait for completion, and return the typed exit value. Its documentation MUST state that it is safe only for thread-agnostic payloads.

Scenario: execute with parameters and convention

  • WHEN Execute<int>(addr, CallingConvention.Cdecl, arg1, arg2) is called on a safe self-contained function
  • THEN the target MUST be called with the arguments laid out per cdecl and the typed return value returned

Scenario: parameters marshalled and freed

  • WHEN a string or struct parameter is passed to Execute
  • THEN it MUST be allocated in the remote process, passed by pointer, and freed after the call completes

Scenario: no process open

  • WHEN Execute is called with no process open
  • THEN it MUST fail deterministically rather than crash

Requirement: Crash-safe main-thread pump

MainThreadPump SHALL install a hook on a per-frame function in the target and, each time that function runs, drain a thread-safe queue of work items, executing each on the target's own thread and returning its result or exception to the requesting caller.

Scenario: work runs on the hooked thread

  • WHEN a work item is queued and the hooked per-frame function next executes
  • THEN the work item MUST run in the context of the thread that calls the per-frame function

Scenario: result returned to caller

  • WHEN a caller queues a function returning a value and awaits its completion
  • THEN the caller MUST receive the returned value

Scenario: exception propagated, pump survives

  • WHEN a queued work item throws
  • THEN the exception MUST be surfaced to the requesting caller AND subsequent queued items MUST still be processed

Scenario: uninstall restores the frame function

  • WHEN the pump is disposed
  • THEN the hooked per-frame function MUST be restored to its original bytes

Requirement: In-process delegate invocation

InProcessInvoker SHALL convert a function address to a typed managed delegate and call it directly, without creating a thread or crossing a thread boundary.

Scenario: call as delegate

  • WHEN CreateFunction<TDelegate>(addr) is called in-process and the returned delegate is invoked
  • THEN the native function at addr MUST be called directly on the current thread with the delegate's marshalled arguments