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>
63 lines
2.3 KiB
C#
63 lines
2.3 KiB
C#
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);
|
|
}
|
|
}
|