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,321 @@
|
||||
# WhiteMagic Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
WhiteMagic is built as a layered architecture that provides multiple levels of abstraction over Windows process-introspection APIs. This design allows consumers to choose the right level of control for their use case, from low-level memory operations to high-level ergonomic APIs.
|
||||
|
||||
## Layer Structure
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Magic (Facade) │
|
||||
│ High-level entry point: Magic.Open(), Magic.OpenInProcess() │
|
||||
└───────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
│ │
|
||||
┌───────▼─────────┐ ┌────────▼────────┐
|
||||
│ MemoryBase │ │ High-Level API │
|
||||
│ (Abstract) │ │ │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ ExternalReader │ │ RemotePointer │
|
||||
│ InProcessReader │ │ RemoteModule │
|
||||
│ │ │ RemoteFunction │
|
||||
│ ┌──────────────┐│ │ ManagedPeb/TEB │
|
||||
│ │ Hooking ││ │ Window/Input │
|
||||
│ │ DetourMgr ││ └─────────────────┘
|
||||
│ │ PatchMgr ││
|
||||
│ └──────────────┘│
|
||||
└─────────┬───────┘
|
||||
│
|
||||
┌─────▼─────┬───────────┬───────────┐
|
||||
│ │ │ │
|
||||
┌───▼────┐ ┌────▼──┐ ┌──────▼──┐ ┌──────▼───┐
|
||||
│Native │ │Discov.│ │Execution│ │Assembly │
|
||||
│P/Invoke│ │Scan/PE│ │3-tier │ │IAssembler│
|
||||
└────────┘ └───────┘ └─────────┘ └──────────┘
|
||||
```
|
||||
|
||||
## Core Layer
|
||||
|
||||
### MemoryBase (Abstract)
|
||||
|
||||
The foundation of WhiteMagic is the `MemoryBase` abstract class, which defines the contract for all memory operations:
|
||||
|
||||
**Key responsibilities:**
|
||||
- Abstract memory read/write operations
|
||||
- Host for `MarshalCache<T>` optimization
|
||||
- Owner of `PatchManager` and `DetourManager`
|
||||
- Relative/absolute addressing support
|
||||
|
||||
**Design decision (D1):** Dual-mode readers share the same abstract interface, allowing high-level code (pattern scanning, patching, etc.) to work in both external and in-process modes without changes.
|
||||
|
||||
### ExternalReader
|
||||
|
||||
Implements `MemoryBase` for out-of-process operations:
|
||||
|
||||
**Mechanism:** Uses `ReadProcessMemory`/`WriteProcessMemory` via P/Invoke
|
||||
**Handle:** `SafeMemoryHandle` to target process
|
||||
**Use case:** Primary mode for automation hosts
|
||||
|
||||
### InProcessReader
|
||||
|
||||
Implements `MemoryBase` for injected code:
|
||||
|
||||
**Mechanism:** Uses `ReadProcessMemory`/`WriteProcessMemory` on self-handle
|
||||
**Handle:** `SafeMemoryHandle` to current process
|
||||
**Use case:** Once a managed DLL is injected, enables delegate calls and detours
|
||||
|
||||
**Design decision (D1 revision):** InProcessReader uses RPM-on-self instead of unsafe direct pointer deref because .NET cannot catch `AccessViolationException`, so a bad deref kills the host with no soft-failure path. The in-process speed win moves to the delegate-call and detour paths, not the reader.
|
||||
|
||||
## Discovery Layer
|
||||
|
||||
### PatternScanner
|
||||
|
||||
Scans target memory for byte patterns with wildcard support:
|
||||
|
||||
**Features:**
|
||||
- IDA-style hex patterns (`48 8B ? ? ? ? ?`)
|
||||
- Optional caching to avoid repeated scans
|
||||
- Module-relative or absolute addressing
|
||||
|
||||
### PeHeaderParser
|
||||
|
||||
Parses PE headers to extract export information:
|
||||
|
||||
**Features:**
|
||||
- Export table parsing
|
||||
- Forwarder resolution (e.g., `kernel32!HeapAlloc` → `NTDLL.RtlAllocateHeap`)
|
||||
- Ordinal export support
|
||||
|
||||
## Execution Layer (Three-Tier Model)
|
||||
|
||||
### RemoteThreadExecutor
|
||||
|
||||
**Mechanism:** `CreateRemoteThread` + hand-assembled convention stubs
|
||||
**Use for:** Thread-agnostic payloads (pure WinAPI, self-contained code, DLL injection)
|
||||
**Risk:** NOT crash-safe for single-threaded target state
|
||||
|
||||
### MainThreadPump
|
||||
|
||||
**Mechanism:** Detour on per-frame function + work queue
|
||||
**Use for:** State-sensitive calls (default for target-state access)
|
||||
**Safety:** Crash-safe — runs on target's own thread
|
||||
|
||||
**How it works:**
|
||||
1. Installs a detour on a per-frame function (e.g., D3D `EndScene`)
|
||||
2. Each frame, the hook drains a thread-safe queue
|
||||
3. Work items run synchronously in target's context
|
||||
4. Results/exceptions returned via `TaskCompletionSource`
|
||||
|
||||
### InProcessInvoker
|
||||
|
||||
**Mechanism:** `Marshal.GetDelegateForFunctionPointer`
|
||||
**Use for:** In-process delegates after injection
|
||||
**Benefit:** Zero thread-crossing overhead
|
||||
|
||||
## Hooking Layer
|
||||
|
||||
### DetourManager
|
||||
|
||||
**Scope:** In-process only
|
||||
**Features:**
|
||||
- Inline `E9 jmp` detours over function prologues
|
||||
- `CallOriginal` support via trampoline
|
||||
- `Apply`/`Remove` operations
|
||||
- Auto-restore on `Dispose`
|
||||
|
||||
**Safety:** Prologue validation via `IAssembler.GetPrologueLength` (optional Iced backend)
|
||||
|
||||
### PatchManager
|
||||
|
||||
**Scope:** Both external and in-process
|
||||
**Features:**
|
||||
- Named byte patches
|
||||
- `Apply`/`Remove`/`IsApplied`
|
||||
- Auto-restore on `Dispose`
|
||||
|
||||
## Assembly Layer
|
||||
|
||||
### IAssembler Seam
|
||||
|
||||
Abstracts text → machine code generation:
|
||||
|
||||
**Implementations:**
|
||||
- `StubAssembler` (default): Hand-emits calling-convention stubs, no dependency
|
||||
- `IcedAssembler` (optional): Wraps Iced for arbitrary assembly
|
||||
|
||||
**Design decision (D3):** Keeps default configuration dependency-free; only callers needing arbitrary asm opt into Iced.
|
||||
|
||||
### Convention Stubs
|
||||
|
||||
`StubAssembler` emits x86/x64 calling-convention trampolines:
|
||||
|
||||
**Conventions supported:** cdecl, stdcall, thiscall, fastcall, x64 (Microsoft)
|
||||
**Encoding:** Deterministic byte emission, no parsing required
|
||||
|
||||
## High-Level Layer
|
||||
|
||||
### RemotePointer
|
||||
|
||||
Indexer-based pointer arithmetic:
|
||||
|
||||
```csharp
|
||||
var ptr = magic[baseAddress];
|
||||
int value = ptr.Read<int>(offset);
|
||||
ptr.Write(999, offset);
|
||||
```
|
||||
|
||||
### RemoteModule/RemoteFunction
|
||||
|
||||
Module and export resolution:
|
||||
|
||||
```csharp
|
||||
var module = magic["user32"];
|
||||
var fn = module["MessageBoxA"];
|
||||
int result = fn.Execute<int>(CallConvention.Stdcall, args...);
|
||||
```
|
||||
|
||||
### ManagedPeb/ManagedTeb
|
||||
|
||||
Managed views of Process Environment Block and Thread Environment Block:
|
||||
|
||||
**Features:**
|
||||
- Typed field access
|
||||
- No manual structure marshalling
|
||||
|
||||
### Window/Input
|
||||
|
||||
Window mutation and input simulation:
|
||||
|
||||
**Features:**
|
||||
- Move/resize/title/activate/flash windows
|
||||
- Keyboard/mouse input without focus (where supported)
|
||||
|
||||
## Optimization Layer
|
||||
|
||||
### MarshalCache<T>
|
||||
|
||||
Per-type metadata caching:
|
||||
|
||||
**Cached data:**
|
||||
- `Size` (managed blittable width)
|
||||
- `MarshalSize` (unmanaged interop width)
|
||||
- `TypeRequiresMarshal` (needs `Marshal.PtrToStructure`)
|
||||
- `IsIntPtr` (for special handling)
|
||||
|
||||
**Performance:** Avoids per-call reflection and `Marshal.SizeOf` overhead
|
||||
|
||||
## Thread Safety
|
||||
|
||||
### ExternalReader
|
||||
|
||||
**Thread-safe:** Yes (RPM/WPM are thread-safe)
|
||||
**Synchronization:** None required
|
||||
|
||||
### InProcessReader
|
||||
|
||||
**Thread-safe:** Yes (RPM-on-self is thread-safe)
|
||||
**Synchronization:** None required
|
||||
|
||||
### MainThreadPump
|
||||
|
||||
**Thread-safe:** Yes (uses `ConcurrentQueue`)
|
||||
**Synchronization:** Lock-free queue + `TaskCompletionSource`
|
||||
|
||||
### DetourManager/PatchManager
|
||||
|
||||
**Thread-safe:** No
|
||||
**Synchronization:** Caller must synchronize
|
||||
|
||||
## Lifetime Management
|
||||
|
||||
All disposable resources follow RAII:
|
||||
|
||||
```csharp
|
||||
using var magic = Magic.Open(process);
|
||||
// All handles, patches, and detours auto-restore on disposal
|
||||
```
|
||||
|
||||
**Resources cleaned up:**
|
||||
- Process handles (`SafeMemoryHandle`)
|
||||
- Applied patches (`PatchManager`)
|
||||
- Active detours (`DetourManager`)
|
||||
- Allocated memory (`AllocatedMemory`)
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Strategy:** Explicit failures, silent success
|
||||
|
||||
- **Read operations:** Throw on failure (Win32Exception)
|
||||
- **Write operations:** Return `false` on failure
|
||||
- **String reads:** Return `string.Empty` on failure
|
||||
- **Invalid handles:** Throw `InvalidOperationException`
|
||||
|
||||
**Rationale:** Writes to another process can legitimately fail (protection changed, process exited); silent retry is often the right strategy. Reads are typically expected to succeed, so exceptions surface the problem immediately.
|
||||
|
||||
## Extensibility Points
|
||||
|
||||
### IAssembler
|
||||
|
||||
Plug in custom assemblers:
|
||||
|
||||
```csharp
|
||||
public interface IAssembler
|
||||
{
|
||||
byte[] Assemble(string assemblyText, ulong origin = 0);
|
||||
}
|
||||
```
|
||||
|
||||
### DetourManager.PrologueLengthResolver
|
||||
|
||||
Custom prologue validation:
|
||||
|
||||
```csharp
|
||||
magic.DetourManager.PrologueLengthResolver = (address, minBytes) =>
|
||||
// Custom validation logic
|
||||
return requiredLength;
|
||||
```
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Memory Access
|
||||
|
||||
| Operation | Cost | Notes |
|
||||
|-----------|------|-------|
|
||||
| `Read<T>` (blittable) | Low | `MemoryMarshal.Read`, no alloc |
|
||||
| `Read<T>` (marshalled) | Medium | `Marshal.PtrToStructure` |
|
||||
| `Read<T>` (array) | Medium-High | Per-element marshalling |
|
||||
| `ReadBytes` | Low | Direct buffer copy |
|
||||
| `ReadString` | Medium | Encoding + allocation |
|
||||
|
||||
### Execution
|
||||
|
||||
| Method | Latency | Safety |
|
||||
|--------|---------|--------|
|
||||
| `RemoteThreadExecutor` | ~1-2ms | Thread-agnostic only |
|
||||
| `MainThreadPump` | ~1 frame (16-33ms) | Crash-safe |
|
||||
| `InProcessInvoker` | <1μs | In-process only |
|
||||
|
||||
### Pattern Scanning
|
||||
|
||||
| Mode | Cost | Notes |
|
||||
|------|------|-------|
|
||||
| Uncached | High | Scans entire module |
|
||||
| Cached | Low | O(1) after first scan |
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. **Safety by default:** MainThreadPump prevents crashes from thread-affinity violations
|
||||
2. **Bitness-agnostic:** Works on x86 and x64 without code changes
|
||||
3. **No native dependencies:** Default configuration is pure managed
|
||||
4. **Explicit operations:** Clear failure modes, no hidden retries
|
||||
5. **Resource safety:** RAII-based cleanup prevents leaks
|
||||
6. **Extensibility:** Seam points for assemblers, validators
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Execution Models](./execution-models.md) — Deep dive on the three-tier execution strategy
|
||||
- [Memory Access](./memory-access.md) — MemoryBase readers and MarshalCache optimization
|
||||
- [Function Hooking](./hooking.md) — DetourManager and PatchManager internals
|
||||
- [Assembly Seam](./assembly-seam.md) — IAssembler abstraction and Iced integration
|
||||
Reference in New Issue
Block a user