using System; using System.Diagnostics; using System.Linq; using System.Text; using WhiteMagic; using WhiteMagic.Discovery; namespace WhiteMagic.Examples; /// /// Example 2: Pattern Scanning /// Demonstrates finding patterns in target process memory /// public class PatternScanning { /// /// 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 2.1: Simple pattern scan /// public static void SimplePatternScan() { Console.WriteLine("=== Example 2.1: Simple Pattern Scan ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Define a pattern to search for // mov rax, [rip+disp] (common in x64) byte[] pattern = { 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 }; string mask = "xxx????"; // 'x' = exact match, '?' = wildcard Console.WriteLine($"Scanning for pattern in module: {process.MainModule.ModuleName}"); Console.WriteLine($"Pattern bytes: {BitConverter.ToString(pattern)}"); Console.WriteLine($"Mask: {mask}"); IntPtr result = PatternScanner.FindInModule( magic.Memory, pattern, mask, process.MainModule ); if (result != IntPtr.Zero) { Console.WriteLine($"✓ Pattern found at: 0x{result:X}"); } else { Console.WriteLine("✗ Pattern not found"); } } /// /// Example 2.2: Pattern scan with multiple results /// public static void MultiplePatternScans() { Console.WriteLine("\n=== Example 2.2: Multiple Pattern Scans ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Multiple patterns to scan byte[][] patterns = { new byte[] { 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 }, // mov rax, [rip+disp] new byte[] { 0xE8, 0x00, 0x00, 0x00, 0x00 }, // call rel32 new byte[] { 0xB8, 0x00, 0x00, 0x00, 0x00 } // mov eax, imm32 }; string[] masks = { "xxx????", "x????", "x????" }; string[] descriptions = { "mov rax, [rip+disp]", "call rel32", "mov eax, imm32" }; Console.WriteLine($"Scanning for {patterns.Length} patterns in {process.MainModule.ModuleName}..."); Console.WriteLine("─────────────────────────────────────────────────────────────────"); for (int i = 0; i < patterns.Length; i++) { try { IntPtr result = PatternScanner.FindInModule( magic.Memory, patterns[i], masks[i], process.MainModule ); Console.WriteLine($"{descriptions[i],-25} {(result != IntPtr.Zero ? $"✓ 0x{result:X}" : "✗ Not found")}"); } catch (Exception ex) { Console.WriteLine($"{descriptions[i],-25} ✗ Error: {ex.Message}"); } } } /// /// Example 2.3: Pattern scanning in specific region /// public static void RegionSpecificScan() { Console.WriteLine("\n=== Example 2.3: Region-Specific Scan ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Scan specific region: .text section (first 64KB of main module) IntPtr startAddress = process.MainModule.BaseAddress; IntPtr endAddress = startAddress + 0x10000; // 64KB byte[] pattern = { 0x48, 0x8B }; string mask = "xx"; // Exact match for first 2 bytes Console.WriteLine($"Scanning region: 0x{startAddress:X} - 0x{endAddress:X}"); try { IntPtr result = PatternScanner.Find( magic.Memory, pattern, mask, startAddress, endAddress ); if (result != IntPtr.Zero) { Console.WriteLine($"✓ Pattern found at: 0x{result:X}"); Console.WriteLine($" Offset from module base: 0x{(result - startAddress):X}"); } else { Console.WriteLine("✗ Pattern not found in region"); } } catch (Exception ex) { Console.WriteLine($"✗ Scan failed: {ex.Message}"); } } /// /// Example 2.4: Finding a function signature /// public static void FindFunctionSignature() { Console.WriteLine("\n=== Example 2.4: Finding Function Signature ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Common function prologue patterns byte[][] prologues = { // x64 prologue: push rbp; mov rbp, rsp new byte[] { 0x55, 0x48, 0x89, 0xE5 }, // x64 prologue: push rbp new byte[] { 0x55 }, // x64 prologue: sub rsp, XX (stack allocation) new byte[] { 0x48, 0x83, 0xEC, 0x00 } // last byte varies }; string[] prologueMasks = { "xxxx", // Exact match "x", // Exact match "xxx?" // Last byte wildcard }; Console.WriteLine($"Scanning for function prologues in {process.MainModule.ModuleName}..."); Console.WriteLine("─────────────────────────────────────────────────────────────────"); for (int i = 0; i < prologues.Length; i++) { try { IntPtr result = PatternScanner.FindInModule( magic.Memory, prologues[i], prologueMasks[i], process.MainModule ); Console.WriteLine($"Prologue {i + 1}: {(result != IntPtr.Zero ? $"✓ Found at 0x{result:X}" : "✗ Not found")}"); } catch (Exception ex) { Console.WriteLine($"Prologue {i + 1}: ✗ Error: {ex.Message}"); } } } /// /// Example 2.5: Pattern scanning with caching /// public static void CachedPatternScan() { Console.WriteLine("\n=== Example 2.5: Cached Pattern Scanning ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Create cache var cache = new PatternScannerCache(magic.Memory); byte[] pattern = { 0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00 }; string mask = "xxx????"; Console.WriteLine("Demonstrating cache performance..."); // First scan (uncached - reads memory) Console.Write(" First scan (uncached): "); var watch = System.Diagnostics.Stopwatch.StartNew(); IntPtr result1 = cache.FindInModuleCached(pattern, mask, process.MainModule); watch.Stop(); Console.WriteLine($"{(result1 != IntPtr.Zero ? $"✓ 0x{result1:X}" : "✗ Not found")} ({watch.ElapsedMilliseconds}ms)"); // Second scan (cached - no memory read) Console.Write(" Second scan (cached): "); watch.Restart(); IntPtr result2 = cache.FindInModuleCached(pattern, mask, process.MainModule); watch.Stop(); Console.WriteLine($"{(result2 != IntPtr.Zero ? $"✓ 0x{result2:X}" : "✗ Not found")} ({watch.ElapsedMilliseconds}ms)"); if (result1 == result2) { Console.WriteLine(" ✓ Results match and cache is working"); } } /// /// Example 2.6: Pattern scanning with wildcard flexibility /// public static void FlexibleWildcardPatterns() { Console.WriteLine("\n=== Example 2.6: Flexible Wildcard Patterns ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Same pattern, different wildcard masks byte[] pattern = { 0x48, 0x8B, 0x05, 0x12, 0x34, 0x56, 0x78 }; string[] masks = { "xxx????", // Last 4 bytes wildcard "xxxx???", // Last 3 bytes wildcard "xxxxxxx", // Exact match "x?x?x?x" // Alternating wildcard }; Console.WriteLine($"Testing same pattern with different masks..."); Console.WriteLine($"Pattern: {BitConverter.ToString(pattern)}"); Console.WriteLine("─────────────────────────────────────────────────────────────────"); for (int i = 0; i < masks.Length; i++) { try { IntPtr result = PatternScanner.FindInModule( magic.Memory, pattern, masks[i], process.MainModule ); Console.WriteLine($"Mask \"{masks[i],-10}\" {(result != IntPtr.Zero ? $"✓ Found at 0x{result:X}" : "✗ Not found")}"); } catch (Exception ex) { Console.WriteLine($"Mask \"{masks[i],-10}\" ✗ Error: {ex.Message}"); } } } /// /// Example 2.7: Combining pattern scan with validation /// public static void SignatureBasedScanning() { Console.WriteLine("\n=== Example 2.7: Pattern-Based Function Scanning ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Find MessageBoxA pattern in user32.dll (if loaded) var user32Module = process.Modules.Cast() .FirstOrDefault(m => m.ModuleName.Equals("user32.dll", StringComparison.OrdinalIgnoreCase)); if (user32Module == null) { Console.WriteLine("✗ user32.dll not loaded in target process"); return; } Console.WriteLine($"Scanning {user32Module.ModuleName} for function signatures..."); // Try to find common export patterns byte[] testPattern = { 0x48, 0x8B }; string mask = "xx"; try { IntPtr result = PatternScanner.FindInModule( magic.Memory, testPattern, mask, user32Module ); if (result != IntPtr.Zero) { Console.WriteLine($"✓ Found pattern at 0x{result:X}"); Console.WriteLine($" Module base: 0x{user32Module.BaseAddress:X}"); Console.WriteLine($" Offset: 0x{(result - user32Module.BaseAddress):X}"); } else { Console.WriteLine("✗ Pattern not found"); } } catch (Exception ex) { Console.WriteLine($"✗ Scan failed: {ex.Message}"); } } /// /// Example 2.8: Pattern validation and error handling /// public static void PatternValidation() { Console.WriteLine("\n=== Example 2.8: Pattern Validation ==="); var process = TargetProcess(); if (process == null) return; if (process.MainModule == null) return; using var magic = Magic.Open(process); // Test various invalid/edge case patterns Console.WriteLine("Testing edge cases and validation..."); // Empty pattern try { IntPtr result = PatternScanner.FindInModule( magic.Memory, Array.Empty(), null, process.MainModule ); Console.WriteLine("✗ Empty pattern should throw exception"); } catch (ArgumentException) { Console.WriteLine("✓ Empty pattern correctly rejected"); } // Mismatched pattern and mask length try { IntPtr result = PatternScanner.FindInModule( magic.Memory, new byte[] { 0x48, 0x8B }, "x", // Mask too short process.MainModule ); Console.WriteLine("✗ Mismatched mask length should throw exception"); } catch (ArgumentException) { Console.WriteLine("✓ Mismatched mask length correctly rejected"); } // Invalid mask characters try { IntPtr result = PatternScanner.FindInModule( magic.Memory, new byte[] { 0x48, 0x8B }, "ab", // Invalid mask characters process.MainModule ); Console.WriteLine("✗ Invalid mask characters should throw exception"); } catch (ArgumentException) { Console.WriteLine("✓ Invalid mask characters correctly rejected"); } Console.WriteLine("✓ All validation tests passed"); } /// /// Run all pattern scanning examples /// public static void RunAll() { Console.WriteLine("╔════════════════════════════════════════════════════════════╗"); Console.WriteLine("║ WhiteMagic Example 2: Pattern Scanning ║"); Console.WriteLine("╚════════════════════════════════════════════════════════════╝"); SimplePatternScan(); MultiplePatternScans(); RegionSpecificScan(); FindFunctionSignature(); CachedPatternScan(); FlexibleWildcardPatterns(); SignatureBasedScanning(); PatternValidation(); Console.WriteLine("\n✓ All pattern scanning examples completed!"); } }