- 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>
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>
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>