HIGH: rename CallingConvention to CallConvention to avoid BCL collision with System.Runtime.InteropServices.CallingConvention. FIXES: - checked(uint) casts for x86 pointer truncation (ArgumentOverflow) - checked distance for E8 rel32 range (>2 GiB → throw) - add esp, imm32 (81 /0 id) when cleanup > 127 bytes - pointerSize validation (throw on != 4 and != 8) - switch default: throw on unknown convention - track argIndex instead of args[1..] slicing - EmitMovRegImm32 helper (avoids manual ip tracking bugs) - seal StubAssembler - IAssembler doc: note BuildCallStub is StubAssembler-specific - thiscall 0-args throws test; fastcall 0-args is valid - update remote-execution spec example to CallConvention.Cdecl All passing (total: 93).
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, CallConvention.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
stringor struct parameter is passed toExecute - THEN it MUST be allocated in the remote process, passed by pointer, and freed after the call completes
Scenario: no process open
- WHEN
Executeis 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
addrMUST be called directly on the current thread with the delegate's marshalled arguments