Replace the two-attempt read with a single read sized to the smaller of:
- detourLength + 16 (the decoder's preferred window), and
- bytes remaining in the current page (so ReadProcessMemory does not fail whole read).
Reading only detourLength bytes could leave the instruction analyzer without enough
bytes to resolve a multi-byte instruction that crosses the splice point on
sparse prologues. Reading up to the page boundary gives the largest safe window.
Tests: 199 passing, 4 integration/interactive skipped.
Risk mitigation:
- redirect now falls back to reading the minimum required bytes (detourLength) if the full buffer (detourLength + 16) cannot be read due to page boundaries.
- First attempt: read detourLength + 16 bytes for the instruction analyzer (preferred).
- Second attempt: read only detourLength bytes if the first attempt fails (bare minimum).
- Throw only if both attempts fail.
This prevents crashes when function interception functions that sit at the very end of a committed page.
Tests: 199 passing, 4 integration/interactive skipped.
- Track whether transferred thread's original context was successfully restored.
- In the catch block, leak the remote allocation instead of freeing it if the thread was not restored; this prevents the target process from executing freed memory.
- Remove redundant 'op == 0x55' check in InstructionAnalyzer (already matched by (op & 0xF8) == 0x50).
- Simplify MemoryBase ReadString align-down expression to previousLen - (previousLen % nullLen).
- Change StubAllocator size parameter from nint to int for clarity (internal test seam).
Tests: 199 passing, 4 integration/interactive skipped.
- MainThreadDispatcher: guard DispatchHook with try/catch so exceptions never escape to native caller; drain and fault pending work on Dispose; synchronize Execute/ExecuteAsync/Dispose against race/dispose.
- InstructionAnalyzer: require ModRM 0xEC for 0x83/0x81 sub-esp/rsp forms, rejecting unsafe RIP-relative or memory forms.
- PatternScannerCache: implement value equality on CacheKey so repeated scans actually hit cache.
- BackgroundTaskExecutor: add remote allocations to the free list immediately after VirtualAllocEx, before any write that could fail and leak.
- redirect: capture and restore original page protection in Apply/Remove instead of leaving target RWX.
- Regression tests for all six fixes.
Tests: 198 passing, 4 integration/interactive skipped.