Files
whitemagic/docs/troubleshooting.md
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

580 lines
13 KiB
Markdown

# 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).*