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.
This commit is contained in:
@@ -0,0 +1,461 @@
|
||||
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!");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user