using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading.Tasks; using WhiteMagic; using WhiteMagic.Assembly; using WhiteMagic.Execution; namespace WhiteMagic.Examples; /// /// Example 3: Execution Models /// Demonstrates the three execution strategies: RemoteThreadExecutor, MainThreadPump, and InProcessInvoker /// public class ExecutionModels { /// /// Target process for examples /// 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; } /// /// Example 3.1: RemoteThreadExecutor (CreateRemoteThread) /// Use for: Thread-agnostic payloads (WinAPI calls, DLL injection, self-contained code) /// 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(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(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."); } /// /// Example 3.2: MainThreadPump (Crash-Safe Execution) /// Use for: State-sensitive calls (game state, scripting, UI, render operations) /// 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(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(magic.Memory.ImageBase + 0x3000); if (basePtr != IntPtr.Zero) { int health = magic.Memory.Read(basePtr + 0x10); int maxHealth = magic.Memory.Read(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}"); } } /// /// Helper: Find a per-frame function /// In real use, you'd use pattern scanning to find D3D EndScene or similar /// 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; } /// /// Example 3.3: InProcessInvoker (Direct Delegates) /// Use for: In-process calls after DLL injection (max performance) /// 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(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."); } /// /// Example 3.4: Choosing the Right Execution Model /// 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("└─────────────────────┴──────────────┴──────────┴──────────────┘"); } /// /// Example 3.5: Combining Execution Models /// 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(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(address);"); Console.WriteLine("int result = fn(arg1, arg2); // <1μs latency"); Console.WriteLine("```"); Console.WriteLine(); Console.WriteLine("✓ DLL injection → Fast in-process calls"); } /// /// Example 3.6: Error Handling in Execution Models /// 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(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(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("```"); } /// /// Run all execution model examples /// 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!"); } }