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,579 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
This guide covers common issues, errors, and solutions when using WhiteMagic.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Process Access Issues](#process-access-issues)
|
||||
- [Memory Operation Failures](#memory-operation-failures)
|
||||
- [Execution Errors](#execution-errors)
|
||||
- [Hooking Problems](#hooking-problems)
|
||||
- [Pattern Scanning Issues](#pattern-scanning-issues)
|
||||
- [Performance Problems](#performance-problems)
|
||||
- [Crash and Stability Issues](#crash-and-stability-issues)
|
||||
- [Build and Compilation Errors](#build-and-compilation-errors)
|
||||
|
||||
---
|
||||
|
||||
## Process Access Issues
|
||||
|
||||
### "Process is not open for read/write"
|
||||
|
||||
**Symptom:**
|
||||
```
|
||||
System.InvalidOperationException: Process is not open for read/write.
|
||||
```
|
||||
|
||||
**Causes:**
|
||||
1. Process has exited
|
||||
2. Process handle is closed
|
||||
3. Insufficient permissions
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Check process state
|
||||
if (magic.Memory.Handle.IsInvalid || magic.Memory.Handle.IsClosed)
|
||||
{
|
||||
Console.WriteLine("Process handle is invalid or closed");
|
||||
}
|
||||
|
||||
// Re-open the process
|
||||
using var magic = Magic.Open(process);
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Keep `Process` object alive
|
||||
- Don't dispose `Magic` while using it
|
||||
- Use `using` statement for automatic cleanup
|
||||
|
||||
### "OpenProcess failed: Access Denied"
|
||||
|
||||
**Symptom:**
|
||||
```
|
||||
Process.Open returns false, or Magic.Open throws Win32Exception
|
||||
```
|
||||
|
||||
**Causes:**
|
||||
1. Insufficient privileges (not running as administrator)
|
||||
2. Target process is protected (anti-cheat, system process)
|
||||
3. 32-bit/64-bit mismatch
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Run as administrator
|
||||
// Right-click → Run as Administrator
|
||||
|
||||
// Check process bitness matches host
|
||||
if (Environment.Is64BitProcess != Is64BitProcess(targetProcess))
|
||||
{
|
||||
Console.WriteLine("Bitness mismatch between host and target");
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Run with admin privileges
|
||||
- Check target process protection level
|
||||
- Ensure bitness compatibility
|
||||
|
||||
---
|
||||
|
||||
## Memory Operation Failures
|
||||
|
||||
### "Read returns default value"
|
||||
|
||||
**Symptom:**
|
||||
```csharp
|
||||
int value = magic.Memory.Read<int>(address);
|
||||
// value is 0 (or default), but expected different value
|
||||
```
|
||||
|
||||
**Causes:**
|
||||
1. Address is invalid
|
||||
2. Memory protection prevents reading
|
||||
3. Fewer bytes read than expected
|
||||
4. Process has exited
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Verify address with debugger
|
||||
// Check if address is readable
|
||||
byte[] test = magic.Memory.ReadBytes(address, 4);
|
||||
if (test.Length < 4)
|
||||
{
|
||||
Console.WriteLine("Cannot read from address");
|
||||
}
|
||||
|
||||
// Validate handle
|
||||
if (magic.Memory.Handle.IsInvalid)
|
||||
{
|
||||
Console.WriteLine("Process handle is invalid");
|
||||
}
|
||||
```
|
||||
|
||||
### "Write returns false"
|
||||
|
||||
**Symptom:**
|
||||
```csharp
|
||||
bool success = magic.Memory.Write(address, value);
|
||||
if (!success) { /* Write failed */ }
|
||||
```
|
||||
|
||||
**Causes:**
|
||||
1. Memory is read-only (e.g., `.text` section)
|
||||
2. Address is invalid
|
||||
3. Process has exited
|
||||
4. Anti-cheat blocking writes
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Retry with delay
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (magic.Memory.Write(address, value))
|
||||
break;
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
// Check if memory is protected
|
||||
// Use VirtualProtectEx if you have rights (advanced)
|
||||
```
|
||||
|
||||
**For code patches, use `PatchManager` instead:**
|
||||
```csharp
|
||||
var patch = magic.PatchManager.Create("Patch", address, bytes);
|
||||
patch.Apply(); // Handles VirtualProtect automatically
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Errors
|
||||
|
||||
### "CreateRemoteThread failed"
|
||||
|
||||
**Symptom:**
|
||||
```
|
||||
Win32Exception: CreateRemoteThread failed
|
||||
```
|
||||
|
||||
**Causes:**
|
||||
1. Target process is protected
|
||||
2. Insufficient permissions
|
||||
3. Target process has exited
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Ensure process is still running
|
||||
if (process.HasExited)
|
||||
{
|
||||
Console.WriteLine("Process has exited");
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
// Run as administrator
|
||||
```
|
||||
|
||||
### "Crash when calling target function via RemoteThreadExecutor"
|
||||
|
||||
**Symptom:** Target application crashes after `RemoteThreadExecutor.Execute<T>`
|
||||
|
||||
**Cause:** Calling single-threaded function from remote thread (thread-affinity violation)
|
||||
|
||||
**Solution:** Use `MainThreadPump` instead:
|
||||
|
||||
```csharp
|
||||
// WRONG (crashes game):
|
||||
int health = magic.RemoteThread.Execute<int>(
|
||||
readHealthFn,
|
||||
CallConvention.Cdecl
|
||||
);
|
||||
|
||||
// RIGHT (crash-safe):
|
||||
var pump = magic.CreateMainThreadPump(frameAddress);
|
||||
int health = await pump.Enqueue(() =>
|
||||
magic.Memory.Read<int>(healthAddress)
|
||||
);
|
||||
```
|
||||
|
||||
**Explanation:** Game state, scripting engines, and render contexts are often main-thread-only. Use `MainThreadPump` for these.
|
||||
|
||||
### "MainThreadPump work item wedged"
|
||||
|
||||
**Symptom:** `Enqueue<T>()` hangs or times out
|
||||
|
||||
**Cause:** Work item threw exception or entered infinite loop
|
||||
|
||||
**Solution:**
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
int result = await pump.Enqueue(() =>
|
||||
{
|
||||
// Keep work items short!
|
||||
return magic.Memory.Read<int>(address);
|
||||
}, TimeSpan.FromSeconds(1)); // Add timeout
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Console.WriteLine("Work item wedged (stuck the frame)");
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention:**
|
||||
- Keep pump work items short (< 1ms)
|
||||
- Avoid blocking calls in work items
|
||||
- Use timeout on `Enqueue<T>`
|
||||
|
||||
---
|
||||
|
||||
## Hooking Problems
|
||||
|
||||
### "Detour failed: prologue too short"
|
||||
|
||||
**Symptom:**
|
||||
```
|
||||
InvalidOperationException: Prologue is too short for detour (minimum 5 bytes required)
|
||||
```
|
||||
|
||||
**Cause:** Function prologue is shorter than minimum required for jmp instruction
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Option 1: Hook a different function
|
||||
var detour = magic.DetourManager.Detour(alternativeAddress, hook);
|
||||
|
||||
// Option 2: Patch deeper into function (after prologue)
|
||||
// (Advanced, risky)
|
||||
|
||||
// Option 3: For WinAPI, use hot-patch area
|
||||
if (IsHotPatchPadded(functionAddress))
|
||||
{
|
||||
// Can use 2-byte jmp at [address-2]
|
||||
}
|
||||
```
|
||||
|
||||
### "Patch failed: access violation"
|
||||
|
||||
**Symptom:**
|
||||
```
|
||||
AccessViolationException when applying patch
|
||||
```
|
||||
|
||||
**Cause:** Memory is protected or invalid
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Verify address is valid
|
||||
byte[] test = magic.Memory.ReadBytes(address, 1);
|
||||
|
||||
// Use PatchManager (handles VirtualProtect automatically)
|
||||
var patch = magic.PatchManager.Create("Patch", address, bytes);
|
||||
patch.Apply();
|
||||
```
|
||||
|
||||
### "Hook not called"
|
||||
|
||||
**Symptom:** Detour applied successfully, but hook delegate never executes
|
||||
|
||||
**Causes:**
|
||||
1. Wrong function address
|
||||
2. Target already called function before hook installed
|
||||
3. Target is using a different implementation (e.g., forwarded export)
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Verify address with debugger
|
||||
// Install hook early (before target uses function)
|
||||
// Check for forwarded exports
|
||||
var fn = magic["module"]["function"];
|
||||
Console.WriteLine($"Function address: {fn.Address}");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern Scanning Issues
|
||||
|
||||
### "FindPattern returns IntPtr.Zero"
|
||||
|
||||
**Symptom:** Pattern scan finds no matches
|
||||
|
||||
**Causes:**
|
||||
1. Pattern is incorrect
|
||||
2. Module not loaded
|
||||
3. Memory layout changed (ASLR, update)
|
||||
4. Pattern wildcards too broad
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Verify pattern syntax
|
||||
// Use IDA-style: "48 8B ? ? ? ? ?"
|
||||
// ? = wildcard byte
|
||||
|
||||
// Narrow search range
|
||||
// Scan only relevant module, not entire memory
|
||||
|
||||
// Check if module is loaded
|
||||
var module = magic["moduleName"];
|
||||
if (module.BaseAddress == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("Module not loaded");
|
||||
}
|
||||
|
||||
// Test with known pattern
|
||||
var test = magic.Memory.FindPattern("48 8B 05 ? ? ? ?", module.BaseAddress, 0x1000);
|
||||
```
|
||||
|
||||
### "Pattern scanner is slow"
|
||||
|
||||
**Symptom:** `FindPattern` takes several seconds
|
||||
|
||||
**Cause:** Scanning large memory regions without cache
|
||||
|
||||
**Solution:**
|
||||
```csharp
|
||||
// Use pattern cache (if available in your version)
|
||||
// Or cache results manually
|
||||
private static readonly Dictionary<string, IntPtr> PatternCache = new();
|
||||
|
||||
IntPtr FindPatternCached(string pattern, IntPtr baseAddr, int size)
|
||||
{
|
||||
string key = $"{pattern}_{baseAddr}_{size}";
|
||||
if (PatternCache.TryGetValue(key, out var cached))
|
||||
return cached;
|
||||
|
||||
IntPtr result = magic.Memory.FindPattern(pattern, baseAddr, size);
|
||||
PatternCache[key] = result;
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Problems
|
||||
|
||||
### "Memory reads are slow"
|
||||
|
||||
**Symptom:** `Read<T>` takes several milliseconds
|
||||
|
||||
**Causes:**
|
||||
1. Reading individual elements in a loop
|
||||
2. Using marshalled types extensively
|
||||
3. Target process is heavily loaded
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// WRONG: Slow loop
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
values[i] = magic.Memory.Read<int>(baseAddr + i * 4);
|
||||
}
|
||||
|
||||
// RIGHT: Batch read
|
||||
values = magic.Memory.Read<int>(baseAddr, 1000);
|
||||
|
||||
// RIGHT: Use blittable types
|
||||
// Use int instead of bool where possible
|
||||
```
|
||||
|
||||
### "High CPU usage"
|
||||
|
||||
**Symptom:** WhiteMagic causes high CPU in target process
|
||||
|
||||
**Cause:** Polling loops, tight read loops in pump
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// WRONG: Tight loop in pump
|
||||
while (true)
|
||||
{
|
||||
int health = magic.Memory.Read<int>(healthAddr);
|
||||
if (health == 0) break;
|
||||
}
|
||||
|
||||
// RIGHT: Event-driven or throttled
|
||||
// Use MainThreadPump with delays
|
||||
// Or poll at reasonable interval (e.g., 60 Hz)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Crash and Stability Issues
|
||||
|
||||
### "Target crashes when attached"
|
||||
|
||||
**Symptom:** Target application crashes shortly after `Magic.Open`
|
||||
|
||||
**Cause:** Thread-affinity violation (calling function from wrong thread)
|
||||
|
||||
**Solution:** Use `MainThreadPump` for state-sensitive calls:
|
||||
|
||||
```csharp
|
||||
// Identify the crash-safe execution model
|
||||
var pump = magic.CreateMainThreadPump(frameAddress);
|
||||
|
||||
// All state-sensitive calls go through pump
|
||||
await pump.Enqueue(() => { /* safe code */ });
|
||||
```
|
||||
|
||||
### "Random crashes during operation"
|
||||
|
||||
**Symptom:** Intermittent crashes, hard to reproduce
|
||||
|
||||
**Causes:**
|
||||
1. Race conditions (concurrent memory access)
|
||||
2. Anti-cheat interference
|
||||
3. Memory protection changes mid-operation
|
||||
|
||||
**Solutions:**
|
||||
```csharp
|
||||
// Add synchronization
|
||||
lock (syncLock)
|
||||
{
|
||||
magic.Memory.Write(address, value);
|
||||
}
|
||||
|
||||
// Handle access violations gracefully
|
||||
try
|
||||
{
|
||||
magic.Memory.Read<int>(address);
|
||||
}
|
||||
catch (AccessViolationException)
|
||||
{
|
||||
// Retry or handle gracefully
|
||||
}
|
||||
|
||||
// Check for anti-cheat
|
||||
// Some anti-cheat tools detect and block memory manipulation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build and Compilation Errors
|
||||
|
||||
### "XML documentation errors"
|
||||
|
||||
**Symptom:** Build fails with `CS1591` or `CS0419` errors
|
||||
|
||||
**Cause:** Missing XML comments or ambiguous cref references
|
||||
|
||||
**Solution:**
|
||||
```xml
|
||||
<!-- In WhiteMagic.csproj -->
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- Suppress missing warnings -->
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
Or add missing XML comments (see [Documentation Best Practices](../README.md#documentation)).
|
||||
|
||||
### "Type or namespace not found"
|
||||
|
||||
**Symptom:** `WhiteMagic` namespace not found after adding reference
|
||||
|
||||
**Cause:** Project not referencing `WhiteMagic.dll`
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
dotnet add reference ../WhiteMagic/WhiteMagic.csproj
|
||||
# OR
|
||||
dotnet add package WhiteMagic
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Enable Detailed Logging
|
||||
|
||||
```csharp
|
||||
// Add diagnostic logging
|
||||
using System.Diagnostics;
|
||||
|
||||
Debug.WriteLine($"Reading from 0x{address:X}");
|
||||
int value = magic.Memory.Read<int>(address);
|
||||
Debug.WriteLine($"Read result: {value}");
|
||||
```
|
||||
|
||||
### Verify with External Tools
|
||||
|
||||
- **Cheat Engine:** Verify memory addresses and values
|
||||
- **x64dbg/windbg:** Verify function addresses and disassembly
|
||||
- **Process Hacker:** Check process handles and permissions
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
1. ❌ **Forgetting `isRelative` for module-relative addresses**
|
||||
```csharp
|
||||
// WRONG
|
||||
int value = magic.Memory.Read<int>(0x1000);
|
||||
|
||||
// RIGHT (if 0x1000 is module-relative)
|
||||
int value = magic.Memory.Read<int>(0x1000, isRelative: true);
|
||||
```
|
||||
|
||||
2. ❌ **Using `RemoteThreadExecutor` for game state**
|
||||
```csharp
|
||||
// WRONG (crashes)
|
||||
int health = magic.RemoteThread.Execute<int>(fn, CallConvention.Cdecl);
|
||||
|
||||
// RIGHT (crash-safe)
|
||||
int health = await pump.Enqueue(() => magic.Memory.Read<int>(addr));
|
||||
```
|
||||
|
||||
3. ❌ **Not disposing `Magic`**
|
||||
```csharp
|
||||
// WRONG (leaks handles)
|
||||
var magic = Magic.Open(process);
|
||||
// ... use magic
|
||||
// Forgot to dispose!
|
||||
|
||||
// RIGHT
|
||||
using (var magic = Magic.Open(process))
|
||||
{
|
||||
// ... use magic
|
||||
} // Auto-disposes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you're still stuck:
|
||||
|
||||
1. **Check documentation:**
|
||||
- [Architecture](./architecture.md)
|
||||
- [Execution Models](./execution-models.md)
|
||||
- [Memory Access](./memory-access.md)
|
||||
- [Function Hooking](./hooking.md)
|
||||
|
||||
2. **Search issues:** Check existing GitHub issues
|
||||
|
||||
3. **Create minimal reproduction:**
|
||||
```csharp
|
||||
// Minimal code that reproduces the issue
|
||||
using var magic = Magic.Open(process);
|
||||
int value = magic.Memory.Read<int>(address);
|
||||
// What happens vs. what you expect
|
||||
```
|
||||
|
||||
4. **Include system info:**
|
||||
- WhiteMagic version
|
||||
- .NET version
|
||||
- Target process (if applicable)
|
||||
- Windows version
|
||||
- x86 or x64
|
||||
|
||||
---
|
||||
|
||||
## Common Error Codes
|
||||
|
||||
| Win32 Error | Meaning | Solution |
|
||||
|-------------|---------|----------|
|
||||
| `ERROR_ACCESS_DENIED` (5) | Insufficient permissions | Run as administrator |
|
||||
| `ERROR_INVALID_HANDLE` (6) | Handle is invalid/closed | Re-open process |
|
||||
| `ERROR_NOT_ENOUGH_MEMORY` (8) | Insufficient memory | Reduce buffer size |
|
||||
| `ERROR_NOACCESS` (998) | Invalid access | Check memory protection |
|
||||
| `ERROR_PARTIAL_COPY` (299) | Partial read/write | Retry or check address |
|
||||
|
||||
---
|
||||
|
||||
*For more information, see the main [README](../README.md) and [architecture documentation](./architecture.md).*
|
||||
Reference in New Issue
Block a user