- TryBind wraps Convert.ChangeType in TryChangeType so an immediate that
overflows a candidate parameter type returns false (letting a wider overload
be tried) instead of throwing OverflowException out of assembly. Verified:
"mov eax, 4294967295" and "mov eax, -2147483649" no longer crash.
- Immediate now carries a boxed long OR ulong; TryParseImmediate parses decimal
values above long.MaxValue via a ulong fallback, and hex via ulong. Previously
such literals were rejected at parse time.
- Unwrap TargetInvocationException from method.Invoke so callers see the real
Iced failure, not the reflection wrapper.
Tests: 231 passing, 4 skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PeHeaderParser: split export forwarders on the FIRST dot (IndexOf), not the
last. A forwarder is "Module.Function" and the module name has no extension, so
the last-dot split misparsed export names that themselves contain a dot.
- PeHeaderParser: document that API-set (api-ms-win-*/ext-ms-*) and ordinal
forwarders are unsupported and should be resolved via the OS loader.
- RemoteFunction.CreateDelegate now throws InvalidOperationException unless the
session is in-process; an external target's address is not host-mapped and a
delegate to it would access-violate on invocation. Tests cover both paths.
- Reword the SSE-payload comment: the 16-byte scratch sits below the saved
return address, which the aligned store leaves intact (it never overwrote it).
Tests: 223 passing, 4 skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Section 8 of the whitemagic-foundation change.
- 8.1: reference Iced 1.21.0 behind IcedAssembler:IAssembler. The default
StubAssembler path never touches Iced; only constructing IcedAssembler pulls
it into a behavioral path.
- 8.2: IcedAssembler.Assemble bridges Intel-syntax text onto Iced's fluent
Assembler by reflection (Iced ships no text parser). Registers, immediates and
labels are supported with origin-relative encoding; memory operands throw
NotSupportedException.
- 8.3: IcedAssembler.GetPrologueLength decodes arbitrary instructions via Iced's
Decoder. DetourManager.PrologueLengthResolver (new delegate) defaults to the
built-in PrologueDecoder and is swappable to the Iced resolver, threaded into
each Detour. This lifts the "partial boundary safety" caveat on the hooking
slice when Iced is opted in.
Also gitignore test-run TestResults artifacts.
Tests: 221 passing, 4 skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 7.2: export resolution + module/function facade.
- PeHeaderParser.GetExportAddress walks the PE32/PE32+ export directory and
follows export forwarders (e.g. kernel32!HeapAlloc -> NTDLL.RtlAllocateHeap)
into other loaded modules; ordinal and unresolvable API-set forwarders throw
NotSupportedException.
- RemoteModule resolves a module base via Process.Modules (name match tolerant
of .dll/case); RemoteFunction executes via RemoteThreadExecutor by default,
exposes Address for pump routing and CreateDelegate<T> for in-process.
- Magic gains a string indexer: magic["user32"]["MessageBoxA"].
Task 3.8: add the missing live-execution ABI test - an SSE callee whose aligned
movaps #GPs unless the stub delivers a 16-byte-aligned stack, combined with a
5th stack argument. Runtime-proves shadow space, alignment, and arg placement.
Tests: 214 passing, 4 skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add GetThreadContext/SetThreadContext overloads accepting Context32 so a
32-bit process on a native 32-bit OS can capture x86 thread context.
- DllInjector.InjectWithThreadHijack now selects the context API based on
both process bitness and OS bitness:
* 64-bit process -> native 64-bit context
* 32-bit process on 64-bit OS -> WOW64 context
* 32-bit process on 32-bit OS -> native x86 context
- ExternalReader now validates that the caller supplied
ProcessAccess.QueryInformation, and surfaces any IsWow64Process failure
instead of silently falling back to host bitness.
Tests: 207 passing, 4 skipped.
- AllocatedMemory.Read<T>/Write<T>/ReadBytes/WriteBytes now validate that
the requested byte range stays within the allocated block before calling
into the memory accessor.
- Patch.Apply/Remove temporarily changes the target page to read-write and
restores the original protection, mirroring the Detour behavior.
- MainThreadPump.WorkItem uses TrySetResult/TrySetException and swallows the
InvalidOperationException raised when a completion source is already
completed, preventing Dispose from failing during concurrent pump drainage.
Regression tests added for all three fixes.
Tests: 206 passing, 4 skipped.
A non-faulting probe payload returns (rsp+8)&15 from the callee, which is 0
only when the stub delivers callee entry rsp ≡ 8 (mod 16) per the Microsoft
x64 ABI. Proves the stub frame alignment end-to-end through CreateRemoteThread
without risking a #GP that would crash the in-process test host. Verified to
fail against the old fixed-0x20 frame.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add RemoteAllocator / RemoteReleaser internal test seams for string/struct scratch memory.
- Route all scratch allocation/freeing through the seams so tests can observe leaks.
- Fix inverted StubAllocator ownership: a caller-provided stub is now never freed by the executor.
- Rewrite Execute_releases_allocated_remote_memory_on_write_failure to fail pre-fix by tracking fake allocations through the seams.
- Update WriteFailingMemoryBase to carry a valid self-handle so the executor reaches the marshal/write path.
Tests: 198 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.
The x64 frame math (0x20 + 8*stackArgs) and x86 arg buffer size grow with the
argument count. An absurdly large count could overflow int and produce a bogus
or negative frame. Add a MaxArguments (256) bound checked at the public entry —
far above any real calling convention — so the arithmetic stays in range. Add a
test asserting the cap is inclusive and count+1 throws.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
x64 call stub was ABI-broken: fixed 0x20 frame left rsp misaligned at the
inner call (callee entry rsp ≡ 0, ABI requires ≡ 8) and, for 5+ args, wrote
stack args over the return address. Compute frame K ≡ 8 (mod 16), K ≥
0x20 + 8*stackArgs, so the callee sees a 16-aligned stack and stack args land
above the shadow window. Load register args as full 64-bit imm64 (was imm32,
which truncated pointers > 4 GiB). BuildCallStub now takes nuint[]; x86 range-
checks each arg against uint.MaxValue instead of silently truncating.
MarshalCache conflated managed and unmanaged width in one Size field: the
blittable path needs Unsafe.SizeOf<T> (bool = 1) while the marshal path needs
Marshal.SizeOf<T> (inline ByValTStr/ByValArray expand past the managed
pointer). Add MarshalSize; MemoryBase picks per TypeRequiresMarshal at all four
IO sites. Prevents PtrToStructure/StructureToPtr from over-reading/overwriting
the pinned scratch buffer (heap corruption on write).
Extract shared RPM/WPM into RpmHelper: honor partial reads (dead Array.Resize
removed), consistent write-return semantics; InProcessReader now guards
MainModule like ExternalReader.
Tests: x64 frame-alignment property + inline-marshal round-trip added (both
fail against the pre-fix code); existing x64 byte-expectation tests updated to
the new frame. Build clean, 100/100 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>