Add optional Iced backend: arbitrary text assembly and full prologue validation
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>
This commit is contained in:
@@ -16,6 +16,7 @@ namespace WhiteMagic.Hooking;
|
||||
public sealed class Detour : IDisposable
|
||||
{
|
||||
private readonly MemoryBase _memory;
|
||||
private readonly PrologueLengthResolver _prologueLength;
|
||||
|
||||
/// <summary>The unique name of this detour.</summary>
|
||||
public string Name { get; }
|
||||
@@ -43,14 +44,21 @@ public sealed class Detour : IDisposable
|
||||
/// <summary><see langword="true"/> while the detour bytes are live at <see cref="Target"/>.</summary>
|
||||
public bool IsApplied { get; private set; }
|
||||
|
||||
internal Detour(MemoryBase memory, string name, IntPtr target, Delegate hook)
|
||||
internal Detour(
|
||||
MemoryBase memory,
|
||||
string name,
|
||||
IntPtr target,
|
||||
Delegate hook,
|
||||
PrologueLengthResolver prologueLength)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(hook);
|
||||
ArgumentNullException.ThrowIfNull(prologueLength);
|
||||
|
||||
_memory = memory;
|
||||
Name = name;
|
||||
Target = target;
|
||||
Hook = hook;
|
||||
_prologueLength = prologueLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,7 +91,7 @@ public sealed class Detour : IDisposable
|
||||
"Could not read enough bytes from the target function to install a detour.");
|
||||
}
|
||||
|
||||
int preserveLength = PrologueDecoder.GetWholeInstructionLength(prologue, detourLength, _memory.Is64Bit);
|
||||
int preserveLength = _prologueLength(prologue, detourLength, _memory.Is64Bit);
|
||||
OverwrittenBytes = new byte[preserveLength];
|
||||
Buffer.BlockCopy(prologue, 0, OverwrittenBytes, 0, preserveLength);
|
||||
|
||||
|
||||
@@ -19,6 +19,15 @@ public sealed class DetourManager
|
||||
_memory = memory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves how many whole prologue-instruction bytes a splice must preserve. Defaults
|
||||
/// to the built-in <see cref="PrologueDecoder"/>, which covers only the common prologue
|
||||
/// shapes and rejects anything else. Assign <c>new IcedAssembler().GetPrologueLength</c>
|
||||
/// to validate arbitrary prologues via the optional Iced disassembler.
|
||||
/// </summary>
|
||||
public PrologueLengthResolver PrologueLengthResolver { get; set; } =
|
||||
PrologueDecoder.GetWholeInstructionLength;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new detour and registers it with the manager.
|
||||
/// The <paramref name="hook"/> delegate's type must match the native signature of
|
||||
@@ -26,7 +35,7 @@ public sealed class DetourManager
|
||||
/// </summary>
|
||||
public Detour Create(string name, IntPtr target, Delegate hook)
|
||||
{
|
||||
var detour = new Detour(_memory, name, target, hook);
|
||||
var detour = new Detour(_memory, name, target, hook, PrologueLengthResolver);
|
||||
_detours[name] = detour;
|
||||
return detour;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,14 @@ using System;
|
||||
|
||||
namespace WhiteMagic.Hooking;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves how many whole prologue-instruction bytes must be preserved to splice
|
||||
/// <paramref name="requiredBytes"/> bytes at a target. The built-in
|
||||
/// <see cref="PrologueDecoder.GetWholeInstructionLength"/> satisfies this delegate, as
|
||||
/// does <c>IcedAssembler.GetPrologueLength</c> for full instruction coverage.
|
||||
/// </summary>
|
||||
public delegate int PrologueLengthResolver(byte[] prologue, int requiredBytes, bool is64Bit);
|
||||
|
||||
/// <summary>
|
||||
/// Minimal instruction-length decoder for common x86/x64 prologue shapes.
|
||||
/// The set is intentionally small: any opcode outside the covered set is rejected
|
||||
|
||||
Reference in New Issue
Block a user