From d04428b42e44cdb57c651dc6b6c585e2b11fe188 Mon Sep 17 00:00:00 2001 From: Kevin Bataille Date: Wed, 22 Jul 2026 01:36:52 +0200 Subject: [PATCH] Fix fallback read too short for sparse prologues in redirection helper 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. --- WhiteMagic/Hooking/Detour.cs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/WhiteMagic/Hooking/Detour.cs b/WhiteMagic/Hooking/Detour.cs index 541ef41..f9fcb43 100644 --- a/WhiteMagic/Hooking/Detour.cs +++ b/WhiteMagic/Hooking/Detour.cs @@ -66,18 +66,21 @@ public sealed class Detour : IDisposable int pointerSize = _memory.Is64Bit ? 8 : 4; int detourLength = pointerSize == 8 ? 14 : 5; - // Try to read detourLength + 16 bytes for the prologue decoder. - // If the target is near a page boundary, this might fail, so fall back to the minimum. - byte[] prologue = _memory.ReadBytes(Target, detourLength + 16); + // The prologue decoder may need to see bytes past the minimum detour length + // to identify the whole instruction that crosses the splice point. Prefer a + // generous read, but if the target sits near an unmapped page boundary, read + // only up to that boundary so ReadProcessMemory does not fail entirely. + int preferredBuffer = detourLength + 16; + int pageSize = Environment.SystemPageSize; + int pageOffset = (int)(Target.ToInt64() & (pageSize - 1)); + int bytesToPageBoundary = pageSize - pageOffset; + int readSize = Math.Min(preferredBuffer, bytesToPageBoundary); + + byte[] prologue = _memory.ReadBytes(Target, readSize); if (prologue.Length < detourLength) { - // Second attempt: read only the minimum required bytes - prologue = _memory.ReadBytes(Target, detourLength); - if (prologue.Length < detourLength) - { - throw new InvalidOperationException( - "Could not read enough bytes from the target function to install a detour."); - } + throw new InvalidOperationException( + "Could not read enough bytes from the target function to install a detour."); } int preserveLength = PrologueDecoder.GetWholeInstructionLength(prologue, detourLength, _memory.Is64Bit);