Files
whitemagic/WhiteMagic.Examples/Example2_PatternScanning.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

462 lines
15 KiB
C#

using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using WhiteMagic;
using WhiteMagic.Discovery;
namespace WhiteMagic.Examples;
/// <summary>
/// Example 2: Pattern Scanning
/// Demonstrates finding patterns in target process memory
/// </summary>
public class PatternScanning
{
/// <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 2.1: Simple pattern scan
/// </summary>
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");
}
}
/// <summary>
/// Example 2.2: Pattern scan with multiple results
/// </summary>
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}");
}
}
}
/// <summary>
/// Example 2.3: Pattern scanning in specific region
/// </summary>
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}");
}
}
/// <summary>
/// Example 2.4: Finding a function signature
/// </summary>
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}");
}
}
}
/// <summary>
/// Example 2.5: Pattern scanning with caching
/// </summary>
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");
}
}
/// <summary>
/// Example 2.6: Pattern scanning with wildcard flexibility
/// </summary>
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}");
}
}
}
/// <summary>
/// Example 2.7: Combining pattern scan with validation
/// </summary>
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<System.Diagnostics.ProcessModule>()
.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}");
}
}
/// <summary>
/// Example 2.8: Pattern validation and error handling
/// </summary>
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<byte>(),
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");
}
/// <summary>
/// Run all pattern scanning examples
/// </summary>
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!");
}
}