Implement RemoteModule/RemoteFunction and prove x64 ABI at runtime

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>
This commit is contained in:
kbe
2026-07-22 02:49:51 +02:00
co-authored by Claude Opus 4.8
parent eda467bc46
commit 678cb00895
7 changed files with 438 additions and 2 deletions
+62
View File
@@ -0,0 +1,62 @@
using System.Threading.Tasks;
using WhiteMagic.Assembly;
using WhiteMagic.Execution;
namespace WhiteMagic;
/// <summary>
/// An exported function resolved in the target process, obtained via
/// <c>magic["module"]["function"]</c>. Executes through one of the session's execution
/// strategies.
/// </summary>
/// <remarks>
/// The default <see cref="Execute{T}"/> path uses the always-available
/// <see cref="RemoteThreadExecutor"/> (<c>CreateRemoteThread</c>), which is safe for
/// thread-agnostic exports. For a call that touches single-threaded target state, obtain
/// the <see cref="Address"/> and route it through a <see cref="MainThreadPump"/>, or use
/// <see cref="CreateDelegate{TDelegate}"/> when running in-process.
/// </remarks>
public sealed class RemoteFunction
{
private readonly Magic _magic;
/// <summary>The export name this function was resolved from.</summary>
public string Name { get; }
/// <summary>The absolute address of the function in the target process.</summary>
public IntPtr Address { get; }
internal RemoteFunction(Magic magic, string name, IntPtr address)
{
_magic = magic;
Name = name;
Address = address;
}
/// <summary>
/// Calls the function via a remote thread and returns its result cast to
/// <typeparamref name="T"/>.
/// </summary>
/// <param name="convention">The calling convention (ignored on x64 targets).</param>
/// <param name="args">Arguments to pass; primitives, pointers, enums, strings and
/// structs are supported.</param>
public T Execute<T>(CallConvention convention, params object?[] args)
{
return _magic.RemoteThread.Execute<T>(Address, convention, args);
}
/// <summary>Asynchronous variant of <see cref="Execute{T}"/>.</summary>
public Task<T> ExecuteAsync<T>(CallConvention convention, params object?[] args)
{
return _magic.RemoteThread.ExecuteAsync<T>(Address, convention, args);
}
/// <summary>
/// Creates a managed delegate bound to this function for the in-process scenario.
/// Only valid when the session was opened in-process.
/// </summary>
public TDelegate CreateDelegate<TDelegate>() where TDelegate : Delegate
{
return new InProcessInvoker(_magic.Memory).CreateFunction<TDelegate>(Address);
}
}