Implemented: - Core: UTF-16 ReadString boundary/alignment fix, target bitness and process id on MemoryBase - function interception: PatchManager, DetourManager, InstructionAnalyzer, MainThreadDispatcher - Execution: BackgroundTaskExecutor, InProcessInvoker - High-level: Magic facade, RemotePointer, async wrappers - Discovery/external code loading/Window groundwork (PEB/TEB, pattern scanning, raw allocations, DLL external code loading, window/input) Tests: 180 passing, 4 integration/interactive tests skipped.
80 lines
2.9 KiB
C#
80 lines
2.9 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace WhiteMagic.Execution;
|
|
|
|
/// <summary>
|
|
/// Direct native-to-managed delegate calls for the in-process scenario.
|
|
/// This is the third execution tier: no remote thread is created; the call runs
|
|
/// synchronously on the current thread.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This class assumes the WhiteMagic consumer has already arranged to run inside the
|
|
/// target process. Bootstrapping the managed loader (e.g., via a CLR host or native
|
|
/// shim) that places WhiteMagic into a foreign process is a separate follow-up change
|
|
/// and is not implemented here.</para>
|
|
/// </remarks>
|
|
public sealed class InProcessInvoker
|
|
{
|
|
private readonly MemoryBase _memory;
|
|
|
|
/// <summary>Creates an invoker bound to the supplied memory reader.</summary>
|
|
public InProcessInvoker(MemoryBase memory)
|
|
{
|
|
_memory = memory ?? throw new ArgumentNullException(nameof(memory));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a managed delegate of type <typeparamref name="TDelegate"/> that calls
|
|
/// the native function at <paramref name="address"/>.
|
|
/// </summary>
|
|
/// <typeparam name="TDelegate">A delegate type whose signature matches the native function.</typeparam>
|
|
public TDelegate CreateFunction<TDelegate>(IntPtr address)
|
|
where TDelegate : Delegate
|
|
{
|
|
if (address == IntPtr.Zero)
|
|
{
|
|
throw new ArgumentException(
|
|
"Function address cannot be zero.", nameof(address));
|
|
}
|
|
|
|
return Marshal.GetDelegateForFunctionPointer<TDelegate>(address);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the vtable pointer stored at the start of an object in memory.
|
|
/// </summary>
|
|
/// <param name="objectAddress">The address of the object instance.</param>
|
|
/// <returns>The address of the vtable.</returns>
|
|
public IntPtr ReadVTable(IntPtr objectAddress)
|
|
{
|
|
return _memory.Read<IntPtr>(objectAddress);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a function pointer from a vtable by index.
|
|
/// </summary>
|
|
/// <param name="vTableAddress">The address of the vtable.</param>
|
|
/// <param name="methodIndex">The zero-based index of the method slot.</param>
|
|
/// <returns>The address in the specified vtable slot.</returns>
|
|
public IntPtr ReadVTableFunction(IntPtr vTableAddress, int methodIndex)
|
|
{
|
|
ArgumentOutOfRangeException.ThrowIfNegative(methodIndex);
|
|
|
|
int pointerSize = _memory.Is64Bit ? 8 : 4;
|
|
IntPtr slotAddress = vTableAddress + (methodIndex * pointerSize);
|
|
return _memory.Read<IntPtr>(slotAddress);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convenience helper that reads an object's vtable and returns the function
|
|
/// address at the requested method index.
|
|
/// </summary>
|
|
public IntPtr GetObjectVTableFunction(IntPtr objectAddress, int methodIndex)
|
|
{
|
|
IntPtr vTable = ReadVTable(objectAddress);
|
|
return ReadVTableFunction(vTable, methodIndex);
|
|
}
|
|
}
|