- 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.
322 lines
11 KiB
C#
322 lines
11 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using WhiteMagic;
|
|
using WhiteMagic.Execution;
|
|
|
|
namespace WhiteMagic.Examples;
|
|
|
|
/// <summary>
|
|
/// Sample struct for demonstrating struct marshalling
|
|
/// </summary>
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct PlayerInfo
|
|
{
|
|
public int Health;
|
|
public float PositionX;
|
|
public float PositionY;
|
|
public int Level;
|
|
public uint Experience;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1: Basic Memory Operations
|
|
/// Demonstrates reading and writing memory of various types
|
|
/// </summary>
|
|
public class BasicMemoryOperations
|
|
{
|
|
/// <summary>
|
|
/// Target process for examples (Notepad is safe and always available)
|
|
/// </summary>
|
|
private static Process? TargetProcess()
|
|
{
|
|
// Try to find Notepad
|
|
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 1.1: Reading and writing primitive types
|
|
/// </summary>
|
|
public static void ReadWritePrimitives()
|
|
{
|
|
Console.WriteLine("=== Example 1.1: Read/Write Primitives ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
// Example: Read an integer from a hypothetical address
|
|
// (In real use, you'd find the actual address via pattern scanning or offsets)
|
|
IntPtr testAddress = magic.Memory.ImageBase + 0x1000;
|
|
|
|
// Read different primitive types
|
|
try
|
|
{
|
|
int intValue = magic.Memory.Read<int>(testAddress);
|
|
Console.WriteLine($"Read int from 0x{testAddress:X}: {intValue}");
|
|
|
|
float floatValue = magic.Memory.Read<float>(testAddress);
|
|
Console.WriteLine($"Read float from 0x{testAddress:X}: {floatValue}");
|
|
|
|
double doubleValue = magic.Memory.Read<double>(testAddress);
|
|
Console.WriteLine($"Read double from 0x{testAddress:X}: {doubleValue}");
|
|
|
|
bool boolValue = magic.Memory.Read<bool>(testAddress);
|
|
Console.WriteLine($"Read bool from 0x{testAddress:X}: {boolValue}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Note: Read failed (address may be invalid): {ex.Message}");
|
|
}
|
|
|
|
// Write example
|
|
bool writeSuccess = magic.Memory.Write(testAddress, 42);
|
|
Console.WriteLine($"Write to 0x{testAddress:X}: {(writeSuccess ? "Success" : "Failed")}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1.2: Reading and writing arrays
|
|
/// </summary>
|
|
public static void ReadWriteArrays()
|
|
{
|
|
Console.WriteLine("\n=== Example 1.2: Read/Write Arrays ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
IntPtr arrayAddress = magic.Memory.ImageBase + 0x2000;
|
|
|
|
// Read array of integers
|
|
try
|
|
{
|
|
int[] intArray = magic.Memory.Read<int>(arrayAddress, 10);
|
|
Console.WriteLine($"Read {intArray.Length} integers from 0x{arrayAddress:X}");
|
|
Console.WriteLine($"First few values: {string.Join(", ", intArray[..Math.Min(5, intArray.Length)])}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Note: Array read failed (address may be invalid): {ex.Message}");
|
|
}
|
|
|
|
// Write array of integers
|
|
int[] writeArray = { 1, 2, 3, 4, 5 };
|
|
bool writeSuccess = magic.Memory.Write(arrayAddress, writeArray);
|
|
Console.WriteLine($"Write {writeArray.Length} integers to 0x{arrayAddress:X}: {(writeSuccess ? "Success" : "Failed")}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1.3: Reading and writing strings
|
|
/// </summary>
|
|
public static void ReadWriteStrings()
|
|
{
|
|
Console.WriteLine("\n=== Example 1.3: Read/Write Strings ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
IntPtr stringAddress = magic.Memory.ImageBase + 0x3000;
|
|
|
|
// Read ANSI string
|
|
string ansiString = magic.Memory.ReadString(stringAddress, Encoding.ASCII, maxLength: 256);
|
|
Console.WriteLine($"Read ANSI string from 0x{stringAddress:X}: \"{ansiString}\"");
|
|
|
|
// Read Unicode string
|
|
string unicodeString = magic.Memory.ReadString(stringAddress, Encoding.Unicode, maxLength: 256);
|
|
Console.WriteLine($"Read Unicode string from 0x{stringAddress:X}: \"{unicodeString}\"");
|
|
|
|
// Write string
|
|
bool writeSuccess = magic.Memory.WriteString(stringAddress, "Hello, WhiteMagic!", Encoding.ASCII);
|
|
Console.WriteLine($"Write string to 0x{stringAddress:X}: {(writeSuccess ? "Success" : "Failed")}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1.4: Reading and writing raw bytes
|
|
/// </summary>
|
|
public static void ReadWriteBytes()
|
|
{
|
|
Console.WriteLine("\n=== Example 1.4: Read/Write Bytes ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
IntPtr address = magic.Memory.ImageBase + 0x4000;
|
|
|
|
// Read bytes
|
|
byte[] buffer = magic.Memory.ReadBytes(address, 16);
|
|
Console.WriteLine($"Read {buffer.Length} bytes from 0x{address:X}");
|
|
Console.WriteLine($"Hex: {BitConverter.ToString(buffer)}");
|
|
|
|
// Write bytes
|
|
byte[] patchBytes = { 0x90, 0x90, 0x90 }; // NOP x3
|
|
int bytesWritten = magic.Memory.WriteBytes(address, patchBytes);
|
|
Console.WriteLine($"Wrote {bytesWritten} bytes to 0x{address:X}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1.5: Using RemotePointer for fluent addressing
|
|
/// </summary>
|
|
public static void RemotePointerUsage()
|
|
{
|
|
Console.WriteLine("\n=== Example 1.5: RemotePointer Fluent Addressing ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
// Create a pointer to module base
|
|
var modulePtr = magic[magic.Memory.ImageBase];
|
|
|
|
// Read offsets from module base
|
|
try
|
|
{
|
|
int offset1 = modulePtr.Read<int>(0x1000);
|
|
Console.WriteLine($"Read int at module_base + 0x1000: {offset1}");
|
|
|
|
// Chained reads (pointer -> value -> offset)
|
|
IntPtr nestedPtr = modulePtr.Read<IntPtr>(0x2000);
|
|
if (nestedPtr != IntPtr.Zero)
|
|
{
|
|
var nestedPtrObj = magic[nestedPtr];
|
|
int nestedValue = nestedPtrObj.Read<int>(0x50);
|
|
Console.WriteLine($"Read nested pointer value: {nestedValue}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Note: Read failed (offset may be invalid): {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1.6: Relative addressing
|
|
/// </summary>
|
|
public static void RelativeAddressing()
|
|
{
|
|
Console.WriteLine("\n=== Example 1.6: Relative Addressing ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
IntPtr moduleBase = magic.Memory.ImageBase;
|
|
|
|
// Absolute addressing (default)
|
|
int absValue = magic.Memory.Read<int>(moduleBase + 0x1000);
|
|
Console.WriteLine($"Absolute read at 0x{moduleBase + 0x1000:X}: {absValue}");
|
|
|
|
// Relative addressing (relative to module base)
|
|
int relValue = magic.Memory.Read<int>(0x1000, isRelative: true);
|
|
Console.WriteLine($"Relative read at offset 0x1000: {relValue}");
|
|
|
|
// They should be the same
|
|
Console.WriteLine($"Values match: {absValue == relValue}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1.7: Error handling
|
|
/// </summary>
|
|
public static void ErrorHandling()
|
|
{
|
|
Console.WriteLine("\n=== Example 1.7: Error Handling ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
// Try to read from invalid address
|
|
try
|
|
{
|
|
int invalidRead = magic.Memory.Read<int>(unchecked((IntPtr)0xdeadbeef));
|
|
Console.WriteLine($"Read from invalid address (shouldn't reach here): {invalidRead}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"✓ Caught expected exception: {ex.GetType().Name}");
|
|
}
|
|
|
|
// Try to write to read-only memory (will return false)
|
|
bool writeSuccess = magic.Memory.Write(magic.Memory.ImageBase, 42);
|
|
Console.WriteLine($"Write to read-only memory: {(writeSuccess ? "Unexpected success" : "✓ Expected failure")}");
|
|
|
|
// Try to read string from invalid address
|
|
string invalidString = magic.Memory.ReadString(unchecked((IntPtr)0xdeadbeef), Encoding.ASCII);
|
|
Console.WriteLine($"Read string from invalid address: \"{invalidString}\" (empty = graceful failure)");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example 1.8: Working with custom structs
|
|
/// </summary>
|
|
public static void CustomStructs()
|
|
{
|
|
Console.WriteLine("\n=== Example 1.8: Custom Structs ===");
|
|
|
|
var process = TargetProcess();
|
|
if (process == null) return;
|
|
|
|
using var magic = Magic.Open(process);
|
|
|
|
IntPtr structAddress = magic.Memory.ImageBase + 0x5000;
|
|
|
|
try
|
|
{
|
|
// Read struct
|
|
PlayerInfo player = magic.Memory.Read<PlayerInfo>(structAddress);
|
|
Console.WriteLine($"Read PlayerInfo:");
|
|
Console.WriteLine($" Health: {player.Health}");
|
|
Console.WriteLine($" Position: ({player.PositionX}, {player.PositionY})");
|
|
Console.WriteLine($" Level: {player.Level}");
|
|
Console.WriteLine($" Experience: {player.Experience}");
|
|
|
|
// Write struct
|
|
PlayerInfo updatedPlayer = player with
|
|
{
|
|
Health = 100,
|
|
Level = player.Level + 1
|
|
};
|
|
bool writeSuccess = magic.Memory.Write(structAddress, updatedPlayer);
|
|
Console.WriteLine($"Write updated PlayerInfo: {(writeSuccess ? "Success" : "Failed")}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Note: Struct read/write failed (address may be invalid): {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Run all basic memory operation examples
|
|
/// </summary>
|
|
public static void RunAll()
|
|
{
|
|
Console.WriteLine("╔════════════════════════════════════════════════════════════╗");
|
|
Console.WriteLine("║ WhiteMagic Example 1: Basic Memory Operations ║");
|
|
Console.WriteLine("╚════════════════════════════════════════════════════════════╝");
|
|
|
|
ReadWritePrimitives();
|
|
ReadWriteArrays();
|
|
ReadWriteStrings();
|
|
ReadWriteBytes();
|
|
RemotePointerUsage();
|
|
RelativeAddressing();
|
|
ErrorHandling();
|
|
CustomStructs();
|
|
|
|
Console.WriteLine("\n✓ All basic memory operation examples completed!");
|
|
}
|
|
}
|