Files
whitemagic/WhiteMagic.Examples/Example3_ExecutionModels.cs
T
kbe 6300bebe33 Fix API documentation and examples; add comprehensive documentation suite
- Example5: Fix format string bugs (alignment specifier placement, SafeMemoryHandle formatting)
- Example4: Fix DetourManager.Detour() → Create() in all doc strings
- Example3: Fix MemoryBase.CreateFunction() → InProcessInvoker.CreateFunction() in doc strings
- Examples 1-5: Correct all runtime errors and API mismatches vs actual WhiteMagic API
- README: Fix DetourManager.Detour() examples to use Create(); add missing code fence markers
- Add WhiteMagic.Examples project with 5 comprehensive example files (40+ sub-examples)
- Add docfx.json and toc.md for DocFX API reference generation
- Add 5 conceptual guides: architecture, memory-access, execution-models, hooking, troubleshooting
- Ensure zero errors, zero warnings across all projects (net8.0-windows)

Doc strings now teach correct APIs; runtime format bugs eliminated; build succeeds.
2026-07-22 22:26:07 +02:00

402 lines
19 KiB
C#

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using WhiteMagic;
using WhiteMagic.Assembly;
using WhiteMagic.Execution;
namespace WhiteMagic.Examples;
/// <summary>
/// Example 3: Execution Models
/// Demonstrates the three execution strategies: RemoteThreadExecutor, MainThreadPump, and InProcessInvoker
/// </summary>
public class ExecutionModels
{
/// <summary>
/// Target process for examples
/// </summary>
private static Process? TargetProcess()
{
var processes = Process.GetProcessesByName("notepad");
if (processes.Length > 0)
return processes[0];
Console.WriteLine("No Notepad process found. Please launch Notepad first.");
return null;
}
/// <summary>
/// Example 3.1: RemoteThreadExecutor (CreateRemoteThread)
/// Use for: Thread-agnostic payloads (WinAPI calls, DLL injection, self-contained code)
/// </summary>
public static void RemoteThreadExecutorExample()
{
Console.WriteLine("=== Example 3.1: RemoteThreadExecutor (CreateRemoteThread) ===");
var process = TargetProcess();
if (process == null) return;
using var magic = Magic.Open(process);
try
{
// Example: Call GetTickCount (thread-safe WinAPI function)
var getTickCount = magic["kernel32.dll"]["GetTickCount"];
if (getTickCount.Address != IntPtr.Zero)
{
Console.WriteLine($"Calling GetTickCount via RemoteThreadExecutor...");
uint ticks = getTickCount.Execute<uint>(CallConvention.Stdcall);
Console.WriteLine($"✓ Result: {ticks} ticks ({TimeSpan.FromMilliseconds(ticks):hh\\:mm\\:ss})");
}
else
{
Console.WriteLine("✗ GetTickCount not found in kernel32.dll");
}
// Example: Call GetCurrentProcessId
var getCurrentProcessId = magic["kernel32.dll"]["GetCurrentProcessId"];
if (getCurrentProcessId.Address != IntPtr.Zero)
{
Console.WriteLine($"Calling GetCurrentProcessId...");
uint processId = getCurrentProcessId.Execute<uint>(CallConvention.Stdcall);
Console.WriteLine($"✓ Result: Process ID = {processId}");
}
else
{
Console.WriteLine("✗ GetCurrentProcessId not found in kernel32.dll");
}
}
catch (Exception ex)
{
Console.WriteLine($"✗ Execution failed: {ex.Message}");
}
Console.WriteLine("\n⚠️ NOTE: RemoteThreadExecutor is ONLY for thread-agnostic functions!");
Console.WriteLine(" Do NOT use for game state, scripting engines, or render operations.");
Console.WriteLine(" Use MainThreadPump for state-sensitive calls instead.");
}
/// <summary>
/// Example 3.2: MainThreadPump (Crash-Safe Execution)
/// Use for: State-sensitive calls (game state, scripting, UI, render operations)
/// </summary>
public static async Task MainThreadPumpExample()
{
Console.WriteLine("\n=== Example 3.2: MainThreadPump (Crash-Safe Execution) ===");
var process = TargetProcess();
if (process == null) return;
using var magic = Magic.Open(process);
try
{
// Find a per-frame function (e.g., D3D EndScene, or any function called every frame)
// For this example, we'll use a hypothetical frame function address
// In real use, you'd find the actual frame function via pattern scanning
IntPtr frameFunctionAddress = FindFrameFunction(magic);
if (frameFunctionAddress == IntPtr.Zero)
{
Console.WriteLine("✗ Could not find frame function (expected in this demo environment)");
Console.WriteLine(" In a real game, you'd use pattern scanning to find the frame function.");
return;
}
Console.WriteLine($"Found frame function at: 0x{frameFunctionAddress:X}");
// Create MainThreadPump
Console.WriteLine("Creating MainThreadPump...");
var pump = magic.CreateMainThreadPump(frameFunctionAddress);
// Example 1: Safe memory read from game state
Console.WriteLine("Enqueueing safe memory read...");
try
{
int value = await pump.ExecuteAsync(() =>
{
// Safe to read game state here (running on target's main thread)
return magic.Memory.Read<int>(magic.Memory.ImageBase + 0x1000);
});
Console.WriteLine($"✓ Safe read result: {value}");
}
catch (TimeoutException)
{
Console.WriteLine("✗ Pump operation timed out (work item wedged or target not running)");
}
catch (Exception ex)
{
Console.WriteLine($"✗ Pump operation failed: {ex.Message}");
}
// Example 2: Safe memory write to game state
Console.WriteLine("Enqueueing safe memory write...");
try
{
await pump.ExecuteAsync(() =>
{
// Safe to write game state here
magic.Memory.Write(magic.Memory.ImageBase + 0x2000, 42);
return true;
});
Console.WriteLine("✓ Safe write completed");
}
catch (Exception ex)
{
Console.WriteLine($"✗ Safe write failed: {ex.Message}");
}
// Example 3: Complex state manipulation
Console.WriteLine("Enqueueing complex state manipulation...");
try
{
string result = await pump.ExecuteAsync(() =>
{
// Safe to perform complex multi-step operations
IntPtr basePtr = magic.Memory.Read<IntPtr>(magic.Memory.ImageBase + 0x3000);
if (basePtr != IntPtr.Zero)
{
int health = magic.Memory.Read<int>(basePtr + 0x10);
int maxHealth = magic.Memory.Read<int>(basePtr + 0x14);
return $"Health: {health}/{maxHealth}";
}
return "Unknown";
});
Console.WriteLine($"✓ Complex operation result: {result}");
}
catch (Exception ex)
{
Console.WriteLine($"✗ Complex operation failed: {ex.Message}");
}
Console.WriteLine("\n✓ MainThreadPump examples completed");
}
catch (Exception ex)
{
Console.WriteLine($"✗ MainThreadPump setup failed: {ex.Message}");
}
}
/// <summary>
/// Helper: Find a per-frame function
/// In real use, you'd use pattern scanning to find D3D EndScene or similar
/// </summary>
private static IntPtr FindFrameFunction(Magic magic)
{
// For demo purposes, return zero (not found)
// In real use, you'd scan for patterns like:
// - D3D9 EndScene: device + 0x44 vtable entry
// - D3D11 Present callbacks
// - Game-specific Update/Render functions
// Example pattern scan (commented out for demo):
// var module = magic["d3d9.dll"];
// if (module != null)
// {
// IntPtr endScene = magic.Memory.FindPattern("?? ?? ?? ??", module.BaseAddress, module.ModuleMemorySize);
// return endScene;
// }
return IntPtr.Zero;
}
/// <summary>
/// Example 3.3: InProcessInvoker (Direct Delegates)
/// Use for: In-process calls after DLL injection (max performance)
/// </summary>
public static void InProcessInvokerExample()
{
Console.WriteLine("\n=== Example 3.3: InProcessInvoker (Direct Delegates) ===");
Console.WriteLine("⚠️ NOTE: This example only works when injected in-process!");
Console.WriteLine(" For demo purposes, we'll show the syntax but it won't execute.");
// Example syntax (would work if injected):
Console.WriteLine("\nExample code (requires in-process injection):");
Console.WriteLine("```csharp");
Console.WriteLine("// Only works when injected in-process");
Console.WriteLine("using var magic = Magic.OpenInProcess();");
Console.WriteLine();
Console.WriteLine("// Define delegate signature");
Console.WriteLine("[UnmanagedFunctionPointer(CallingConvention.Cdecl)]");
Console.WriteLine("public delegate int AddNumbersDelegate(int a, int b);");
Console.WriteLine();
Console.WriteLine("// Create delegate from function address");
Console.WriteLine("var addNumbers = inProcess.CreateFunction<AddNumbersDelegate>(functionAddress);");
Console.WriteLine();
Console.WriteLine("// Call directly as a delegate (max performance, <1μs)");
Console.WriteLine("int result = addNumbers(10, 20);");
Console.WriteLine("Console.WriteLine($\"Result: {result}\");");
Console.WriteLine("```");
Console.WriteLine("\n✓ InProcessInvoker is the fastest but requires injection.");
Console.WriteLine(" Use RemoteThreadExecutor for initial injection, then switch to InProcessInvoker.");
}
/// <summary>
/// Example 3.4: Choosing the Right Execution Model
/// </summary>
public static void ChooseExecutionModel()
{
Console.WriteLine("\n=== Example 3.4: Choosing the Right Execution Model ===");
Console.WriteLine("Decision Flowchart:");
Console.WriteLine("─────────────────────────────────────────────────────────────────");
Console.WriteLine();
Console.WriteLine("Are you injected in-process?");
Console.WriteLine("├─ Yes → Use InProcessInvoker (direct delegates, <1μs)");
Console.WriteLine("└─ No → Does the call touch single-threaded state?");
Console.WriteLine(" ├─ Yes → Use MainThreadPump (crash-safe, ~1 frame latency)");
Console.WriteLine(" └─ No → Use RemoteThreadExecutor (CreateRemoteThread, ~1-2ms)");
Console.WriteLine();
Console.WriteLine("Examples by Use Case:");
Console.WriteLine("─────────────────────────────────────────────────────────────────");
Console.WriteLine();
Console.WriteLine("1. DLL Injection: RemoteThreadExecutor");
Console.WriteLine(" → LoadLibrary is thread-safe");
Console.WriteLine();
Console.WriteLine("2. Read Health Bar: MainThreadPump");
Console.WriteLine(" → Game state is main-thread-affine");
Console.WriteLine();
Console.WriteLine("3. Call GetTickCount: RemoteThreadExecutor");
Console.WriteLine(" → WinAPI, no thread affinity");
Console.WriteLine();
Console.WriteLine("4. Script Engine Call: MainThreadPump");
Console.WriteLine(" → Script VM is main-thread-only");
Console.WriteLine();
Console.WriteLine("5. Injected Profiling: InProcessInvoker");
Console.WriteLine(" → Already in-process, max performance");
Console.WriteLine();
Console.WriteLine("6. Window Mutation: RemoteThreadExecutor");
Console.WriteLine(" → WinAPI SetWindowPos is thread-safe");
Console.WriteLine();
Console.WriteLine("Performance Comparison:");
Console.WriteLine("─────────────────────────────────────────────────────────────────");
Console.WriteLine();
Console.WriteLine("┌─────────────────────┬──────────────┬──────────┬──────────────┐");
Console.WriteLine("│ Model │ Latency │ Safety │ Use Case │");
Console.WriteLine("├─────────────────────┼──────────────┼──────────┼──────────────┤");
Console.WriteLine("│ RemoteThreadExec │ ~1-2ms │ Thread- │ One-shot, │");
Console.WriteLine("│ │ │ agnostic │ DLL inject │");
Console.WriteLine("├─────────────────────┼──────────────┼──────────┼──────────────┤");
Console.WriteLine("│ MainThreadPump │ ~1 frame │ Crash- │ State- │");
Console.WriteLine("│ │ (16-33ms) │ safe │ sensitive │");
Console.WriteLine("├─────────────────────┼──────────────┼──────────┼──────────────┤");
Console.WriteLine("│ InProcessInvoker │ <1μs │ In- │ In-process │");
Console.WriteLine("│ │ │ process │ tools │");
Console.WriteLine("└─────────────────────┴──────────────┴──────────┴──────────────┘");
}
/// <summary>
/// Example 3.5: Combining Execution Models
/// </summary>
public static void CombinedExecutionModels()
{
Console.WriteLine("\n=== Example 3.5: Combining Execution Models ===");
var process = TargetProcess();
if (process == null) return;
using var magic = Magic.Open(process);
Console.WriteLine("Strategy: Inject DLL, then switch to InProcessInvoker");
Console.WriteLine();
Console.WriteLine("Step 1: Use RemoteThreadExecutor to inject DLL");
Console.WriteLine("```csharp");
Console.WriteLine("var loadLibrary = magic[\"kernel32.dll\"][\"LoadLibraryA\"];");
Console.WriteLine("IntPtr dllHandle = loadLibrary.Execute<IntPtr>(CallConvention.Stdcall, dllPathPtr);");
Console.WriteLine("```");
Console.WriteLine();
Console.WriteLine("Step 2: Injected DLL initializes, now in-process");
Console.WriteLine("```csharp");
Console.WriteLine("// Inside injected DLL");
Console.WriteLine("using var inProcess = Magic.OpenInProcess();");
Console.WriteLine();
Console.WriteLine("// Use InProcessInvoker for max performance");
Console.WriteLine("var fn = inProcess.CreateFunction<MyDelegate>(address);");
Console.WriteLine("int result = fn(arg1, arg2); // <1μs latency");
Console.WriteLine("```");
Console.WriteLine();
Console.WriteLine("✓ DLL injection → Fast in-process calls");
}
/// <summary>
/// Example 3.6: Error Handling in Execution Models
/// </summary>
public static void ExecutionModelErrorHandling()
{
Console.WriteLine("\n=== Example 3.6: Error Handling ===");
Console.WriteLine("RemoteThreadExecutor:");
Console.WriteLine("```csharp");
Console.WriteLine("try");
Console.WriteLine("{");
Console.WriteLine(" uint result = fn.Execute<uint>(CallConvention.Stdcall, args...);");
Console.WriteLine("}");
Console.WriteLine("catch (Win32Exception ex)");
Console.WriteLine("{");
Console.WriteLine(" // CreateRemoteThread failed (access denied, process exited, etc.)");
Console.WriteLine(" Console.WriteLine($\"Execution failed: {ex.Message}\");");
Console.WriteLine("}");
Console.WriteLine("```");
Console.WriteLine();
Console.WriteLine("MainThreadPump:");
Console.WriteLine("```csharp");
Console.WriteLine("try");
Console.WriteLine("{");
Console.WriteLine(" int result = await pump.ExecuteAsync(() => magic.Memory.Read<int>(addr));");
Console.WriteLine("}");
Console.WriteLine("catch (TimeoutException)");
Console.WriteLine("{");
Console.WriteLine(" // Work item wedged (stuck the frame) or target not running");
Console.WriteLine(" Console.WriteLine(\"Operation timed out\");");
Console.WriteLine("}");
Console.WriteLine("catch (Exception ex)");
Console.WriteLine("{");
Console.WriteLine(" // Other exception from work item");
Console.WriteLine(" Console.WriteLine($\"Operation failed: {ex.Message}\");");
Console.WriteLine("}");
Console.WriteLine("```");
Console.WriteLine();
Console.WriteLine("InProcessInvoker:");
Console.WriteLine("```csharp");
Console.WriteLine("try");
Console.WriteLine("{");
Console.WriteLine(" int result = delegate(arg1, arg2);");
Console.WriteLine("}");
Console.WriteLine("catch (AccessViolationException)");
Console.WriteLine("{");
Console.WriteLine(" // Invalid function address or bad call convention");
Console.WriteLine("}");
Console.WriteLine("```");
}
/// <summary>
/// Run all execution model examples
/// </summary>
public static async Task RunAll()
{
Console.WriteLine("╔════════════════════════════════════════════════════════════╗");
Console.WriteLine("║ WhiteMagic Example 3: Execution Models ║");
Console.WriteLine("╚════════════════════════════════════════════════════════════╝");
RemoteThreadExecutorExample();
await MainThreadPumpExample();
InProcessInvokerExample();
ChooseExecutionModel();
CombinedExecutionModels();
ExecutionModelErrorHandling();
Console.WriteLine("\n✓ All execution model examples completed!");
Console.WriteLine();
Console.WriteLine("⚠️ CRITICAL REMINDER:");
Console.WriteLine(" - RemoteThreadExecutor: ONLY for thread-agnostic functions");
Console.WriteLine(" - MainThreadPump: For state-sensitive calls (crash-safe)");
Console.WriteLine(" - InProcessInvoker: Only after DLL injection");
Console.WriteLine();
Console.WriteLine(" Using the wrong model crashes the target application!");
}
}