Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
lacuna-rs — Crate Rust per ghost-frame call-stack spoofing, runtime indirect syscalls e APC injection su Windows x64. Fornisce risoluzione SSN, emissione di stub JIT e crittografia dei parametri basata su VEH per evasione di EDR. | Kitploit
Strumenti/GitHubGitHub/karkas66/lacuna-rs
ShellcodePost-ExploitRed TeamingSviluppo PayloadBinary Exploitation
GitHubkarkas66/lacuna-rs

lacuna-rs

Crate Rust per ghost-frame call-stack spoofing, runtime indirect syscalls e APC injection su Windows x64. Fornisce risoluzione SSN, emissione di stub JIT e crittografia dei parametri basata su VEH per evasione di EDR.

Vedi Repository
1817218 giorni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

lacuna-rs

Ghost-frame call-stack spoofing + runtime indirect syscalls per Windows x64 — portato in Rust.

Portato da LACUNA Chain (lacuna_chain.c) di Mohamed Alzhrani (0xmaz).

lacuna-rs è una crate Rust riutilizzabile che fornisce le stesse primitive del TTP originale in C, strutturata in modo da poter essere inserita in qualsiasi progetto Rust — come wsyscall-rs o syscalls-rs, ma con risoluzione runtime degli SSN, targeting syscall;ret per funzione e ghost-frame stack spoofing.


Critico: Requisito del Frame-Pointer

La feature stack-spoof fallirà silenziosamente se la crate che la utilizza non è compilata con i frame pointer.

Le primitive di stack-stomping in chain.rs individuano lo slot dell'indirizzo di ritorno del chiamante tramite l'asm inline mov rbp, {x}. Questo richiede che la catena RBP sia intatta. Rust (e la maggior parte dei compilatori in modalità release) omette i frame pointer per impostazione predefinita.

build.rs imposta force-frame-pointers=yes per la codegen di questa crate, ma Cargo non può propagare i flag del compilatore alle crate a valle. Devi aggiungere questo al tuo progetto:```toml

.cargo/config.toml (in YOUR crate, not in lacuna-rs)

[build] rustflags = ["-C", "force-frame-pointers=yes"]

Senza questo, `stomp_plant()` leggerà dati spazzatura da RBP e o non farà nulla (caso migliore) o corromperà lo stack (caso peggiore). Il crate non ha modo di rilevare a runtime se i frame pointer sono abilitati — semplicemente non funzionerà.

Se hai bisogno solo dello scanning, della risoluzione SSN o delle primitive di injection (senza stack spoofing), puoi omettere la feature `stack-spoof` e questo requisito non si applica.

---

## Cosa fa

| Primitive | C function | Rust module |
|---|---|---|
| PE section + export parsing | `pe_section()`, `pe_export()` | `pe` |
| `.pdata` ghost-region scanning | `scan_ghosts()`, `best_ghost()` | `scan` |
| Ghost-gadget discovery (`jmp [rbx]`) | `scan_ghost_gadgets()` | `scan` |
| `win32u` NOP-gap finder | `win32u_nop_gap()` | `scan` |
| BYOUD-MF anchor finder | `find_mf_target()` | `scan` |
| SSN resolution (Hell's Gate / Halo's Gate) | `resolve_ssn()` | `nt` |
| Per-function `syscall;ret` locator | `find_func_syscall()` | `nt` |
| JIT indirect-syscall stub emission | `alloc_stub()` | `stub` |
| Ghost-gadget stub redirect | (in `alloc_stub()`) | `stub` |
| VEH + hardware-breakpoint param encryption | `param_encrypt_veh()`, `pcrypt_arm()` | `veh` |
| Chain-guard VEH | `chain_veh()` | `veh` |
| LACUNA chain construction | `build_chain()` | `chain` |
| Stack stomp (BYOUD-RT) | `stomp_plant()`, `stomp_restore()` | `chain` |
| Chain walker (verify) | `lacuna_walk_chain()` | `chain` |
| Section-based APC injection | `do_inject_sapc()` | `inject` |

---

## Feature flags```toml
[dependencies]
lacuna-rs = { version = "0.4", features = ["inject", "stack-spoof", "veh"] }
FunzionalitàDescrizioneRichiede frame pointer?
syscalls (predefinito)Risoluzione SSN + emissione stub JITNo
inject (predefinito)Iniezione APC basata su sezione (inject::inject_sapc)No
vehVEH + cifratura parametri con breakpoint hardwareNo
stack-spoofCatena ghost-frame LACUNA + stack stompSì
no-stdModalità no_std (sperimentale)No

Quando nessuna funzionalità è abilitata, sono disponibili solo i livelli scanning/PE/NT.


Avvio rapido

Scansione per ghost region```sh

cargo run --example scan

### Compila e verifica la catena ghost-frame```sh
cargo run --example verify --features stack-spoof

Iniezione di shellcode tramite sezione + APC

Prova di iniezione Prova di iniezione -- il payload è stato eseguito all'interno del processo target.```sh cargo run --example inject --features inject,stack-spoof,veh -- <sc.bin>

Aggiungi `--verbose` per abilitare l'output diagnostico VEH (dump dello stack, stampe dei registri):```sh
cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin> --verbose

Algoritmo di scoring dei thread

inject_sapc classifica i thread del target. Il segnale di ranking più forte è se un thread possiede una finestra:

  • Possiede una finestra (thread GUI/message-pump) — EnumWindows + GetWindowThreadProcessId. Un thread GUI si blocca in un'attesa alertable nel suo message loop, quindi un APC accodato viene eseguito lì per primo. +1000.
  • Tempo di ciclo — NtQueryInformationThread(ThreadCycleTime); ricade su ThreadTimes quando la classe non è disponibile sulla build in esecuzione (vedi examples/tprobe.rs). Normalizzato e sommato.
  • Tempo CPU — NtQueryInformationThread(ThreadTimes) — tempo kernel + user.
  • Conteggio sospensioni — NtQueryInformationThread(ThreadSuspendCount). Un thread sospeso non può eseguire un APC: −2000 per conteggio sospensione.
  • Priorità — NtQueryInformationThread(ThreadBasicInformation). Priorità 8–10 ottiene +150; priorità fuori range ottiene −300.

Filtraggio. I thread sospesi e i thread bloccati in un'attesa kernel non alertable — WrDelayExecution (Sleep/NtDelayExecution) o WrSuspended, letti da NtQuerySystemInformation(SystemProcessInformation) — vengono scartati del tutto (non possono mai ricevere un APC). Il motivo dell'attesa è altrimenti solo diagnostico: viene registrato per ogni candidato come State: / Reason: / Alert:, ma non influenza il punteggio, perché KWAIT_REASON non codifica l'alertability (un'attesa alertable e una non alertable su un oggetto riportano lo stesso valore — verificato con probing in stile examples/tprobe.rs).

I candidati vengono ordinati (punteggio più alto, spareggio GUI poi cicli) e troncati a MAX_APC_CANDIDATES (10) per enumerazione/logging.

Enumerare ≠ iniettare. Il numero di thread elencati è disaccoppiato dal numero iniettati. MAX_APC_CANDIDATES (10) controlla quanti vengono classificati e registrati; APC_INJECT_THREADS (5) controlla quanti ricevono effettivamente l'APC.

Perché iniettare su diversi, poi riprovare: lo user mode non può determinare quale thread sia in un'attesa alertable, e un singolo thread scelto spesso non ne entra mai una, quindi l'APC non verrebbe mai eseguito. inject_sapc quindi accoda l'APC a diversi thread e poi riaccoda in un breve loop (~80 ms) finché la gate word condivisa non passa da 0 a 1 — cioè finché qualche thread target non ha effettivamente eseguito il payload. Registra se ciò è avvenuto: payload claimed the gate (eseguito) vs gate NOT claimed.

Perché eseguire una sola volta: lo stesso shellcode/contesto viene accodato a ogni thread, quindi senza protezione un payload non reentrante verrebbe eseguito concorrentemente e manderebbe in crash l'host (misurato: 3 esecuzioni simultanee). Il gate di reentrancy (USE_REENTRANCY_GATE, attivo di default) fa sì che il corpo del payload venga eseguito esattamente una volta indipendentemente da quanti thread (o quanti round di retry) prendono l'APC (misurato: esattamente 1). Lascia il gate attivo a meno che il tuo payload non sia esplicitamente concurrency-safe.


Uso come libreria

Base: scan + risoluzione SSN```rust

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);

Scarica lo strumento