callghost 0.1.0

Direct syscall framework for Rust on Windows x86_64. Bypasses usermode API hooks (EDR/AV) via multiple methods.
# CallGhost

Direct syscall framework for Rust on Windows x86_64. Bypasses usermode API hooks (EDR/AV) by avoiding ntdll stubs entirely or restoring them from clean copies.

## Features

- `syscall!` proc macro with per-call method selection, up to 12 args, compile-time function name hashing
- 4 syscall methods: `direct`, `indirect`, `unhook`, `perunsfart`
- Halo's Gate SSN resolution even when stubs are hooked
- `no_std` runtime, no CRT dependency, usable in shellcode loaders
- SSN cache (export table walked once per function, O(1) after)
- KnownDlls cache (`\KnownDlls\ntdll.dll` mapped once, reused for all unhook ops)

## Methods

| Method | Syntax | What happens | Return address points to |
|---|---|---|---|
| `direct` | `syscall!(NtFoo, ...)` | Inline `syscall` instruction in your code | Your module |
| `indirect` | `syscall!(indirect, NtFoo, ...)` | `call` to a `syscall;ret` gadget inside ntdll | ntdll |
| `unhook` | `syscall!(unhook, NtFoo, ...)` | Restore stub from KnownDlls, then direct syscall | Your module |
| `perunsfart` | `syscall!(perunsfart, NtFoo, ...)` | JIT unhook, call through clean stub, re-hook | ntdll |

### When to use what

**`direct`** is the default. Fast, no dependencies. Use when return-address checks aren't a concern.

**`indirect`** is for when EDR checks if the `syscall` instruction originates from ntdll. The gadget address is cached after first lookup.

**`unhook`** permanently restores a hooked stub so your code or other code in the process can call through ntdll normally again.

**`perunsfart`** is for when you need the call to look 100% legitimate (return address in ntdll) but don't want to leave the stub unhooked, since EDR periodic scans would notice.

## Usage

```toml
[dependencies]
callghost = { git = "https://github.com/PatchRequest/CallGhost", branch = "master" }
```

```rust
use callghost::syscall;

// Allocate memory via direct syscall, no ntdll involved
let mut base: *mut core::ffi::c_void = core::ptr::null_mut();
let mut size: usize = 4096;
let status = syscall!(NtAllocateVirtualMemory,
    -1isize,        // NtCurrentProcess
    &mut base,
    0usize,
    &mut size,
    0x3000u32,      // MEM_COMMIT | MEM_RESERVE
    0x04u32,        // PAGE_READWRITE
);

// Same call via indirect syscall (return address points to ntdll)
let status = syscall!(indirect, NtAllocateVirtualMemory,
    -1isize, &mut base, 0usize, &mut size, 0x3000u32, 0x04u32);

// Perun's Fart: JIT unhook, call through ntdll, re-hook
let status = syscall!(perunsfart, NtCreateThreadEx,
    &mut thread, 0x1FFFFFu32, core::ptr::null_mut::<u8>(),
    process, entry_point, core::ptr::null_mut::<u8>(),
    0u32, 0usize, 0usize, 0usize, core::ptr::null_mut::<u8>());
```

### Standalone unhook

```rust
// Unhook a single function
unsafe { callghost::__private::unhook_stub(callghost::__private::fnv1a(b"NtWriteVirtualMemory")) };

// Unhook ALL of ntdll's .text section
unsafe { callghost::__private::unhook_all() };

// Release the cached KnownDlls mapping when done
unsafe { callghost::__private::release_clean_ntdll() };
```

## Architecture

```
callghost/                  Facade crate, re-exports macro + runtime
callghost-macros/           Proc macro crate, generates inline asm per call site
callghost-runtime/          no_std runtime: PEB walking, SSN resolution, unhook infra
```

### How it works

1. **Compile time**: `syscall!` hashes the function name (FNV-1a) and generates method-specific inline assembly
2. **First call**: Walks PEB, finds ntdll, walks export table, resolves SSN, caches it
3. **Subsequent calls**: SSN from cache, direct/indirect asm or function pointer call
4. **Hooked stubs**: Halo's Gate sorts all Nt*/Zw* stubs by RVA, finds clean neighbors, calculates SSN by offset
5. **Unhook**: Maps `\KnownDlls\ntdll.dll` (cached), copies clean bytes over hooked stub

## Tests

```
cargo test -- --test-threads=1
```

19 tests covering hash correctness, stub cleanliness verification across 7 functions, SSN uniqueness, gadget location verification, hook proof (calling through a hooked stub returns the sentinel value), all 4 methods bypassing hooks, Halo's Gate with hooked neighbors, permanent unhook with SSN consistency, unhook_all restoring 3 hooked stubs, Perun's Fart succeeding while re-hooking, and full interchangeability (alloc+write+free with every method).

Single-threaded execution required because hook tests temporarily modify ntdll stubs.

## Requirements

Windows x86_64, Rust stable 1.85+ (edition 2024).