Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
lacuna-rs — Rust крейт для спуфинга стека вызовов ghost-frame, runtime indirect syscalls и APC injection на Windows x64. Предоставляет разрешение SSN, эмиссию JIT-заглушек и шифрование параметров на основе VEH для обхода EDR. | Kitploit
Инструменты/GitHubGitHub/karkas66/lacuna-rs
Шелл-кодПост-эксплуатацияRed TeamingРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHubkarkas66/lacuna-rs

lacuna-rs

Rust крейт для спуфинга стека вызовов ghost-frame, runtime indirect syscalls и APC injection на Windows x64. Предоставляет разрешение SSN, эмиссию JIT-заглушек и шифрование параметров на основе VEH для обхода EDR.

Репозиторий
1817219 дней назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

lacuna-rs

Спуфинг стека вызовов через ghost-frame + runtime indirect syscalls для Windows x64 — портировано на Rust.

Портировано из LACUNA Chain (lacuna_chain.c) авторства Mohamed Alzhrani (0xmaz).

lacuna-rs — это переиспользуемый крейт Rust, предоставляющий те же примитивы, что и оригинальный C TTP, структурированный так, чтобы его можно было подключить к любому проекту на Rust — как wsyscall-rs или syscalls-rs, но с runtime-разрешением SSN, таргетингом syscall;ret для каждой функции и спуфингом стека через ghost-frame.


Критично: требование к frame pointer

Функция stack-spoof будет молча отказывать, если потребляющий крейт не скомпилирован с frame pointers.

Примитивы затирания стека в chain.rs находят слот адреса возврата вызывающего через inline asm mov rbp, {x}. Это требует целостности цепочки RBP. Rust (и большинство компиляторов в release-режиме) по умолчанию опускают frame pointers.

build.rs устанавливает force-frame-pointers=yes для кодогенерации самого этого крейта, но Cargo не может распространить флаги компилятора на зависимые крейты. Вы должны добавить это в свой собственный проект:```toml

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

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

Без этого `stomp_plant()` будет читать мусор из RBP и либо ничего не сделает (в лучшем случае), либо повредит стек (в худшем случае). У крейта нет способа определить во время выполнения, включены ли указатели кадра — он просто не будет работать.

Если вам нужны только примитивы сканирования, разрешения SSN или инъекции (без подмены стека), вы можете опустить функцию `stack-spoof`, и это требование не применяется.

---

## Что он делает

| Примитив | Функция C | Модуль Rust |
|---|---|---|
| Разбор секций PE + экспортов | `pe_section()`, `pe_export()` | `pe` |
| Сканирование призрачных областей `.pdata` | `scan_ghosts()`, `best_ghost()` | `scan` |
| Обнаружение призрачных гаджетов (`jmp [rbx]`) | `scan_ghost_gadgets()` | `scan` |
| Поиск NOP-разрывов в `win32u` | `win32u_nop_gap()` | `scan` |
| Поиск якорей BYOUD-MF | `find_mf_target()` | `scan` |
| Разрешение SSN (Hell's Gate / Halo's Gate) | `resolve_ssn()` | `nt` |
| Поиск `syscall;ret` для каждой функции | `find_func_syscall()` | `nt` |
| Генерация JIT-заглушки косвенного системного вызова | `alloc_stub()` | `stub` |
| Перенаправление заглушки призрачного гаджета | (в `alloc_stub()`) | `stub` |
| Шифрование параметров через VEH + аппаратные точки останова | `param_encrypt_veh()`, `pcrypt_arm()` | `veh` |
| VEH защиты цепочки | `chain_veh()` | `veh` |
| Построение цепочки LACUNA | `build_chain()` | `chain` |
| Подмена стека (BYOUD-RT) | `stomp_plant()`, `stomp_restore()` | `chain` |
| Обходчик цепочки (проверка) | `lacuna_walk_chain()` | `chain` |
| Инъекция APC на основе секций | `do_inject_sapc()` | `inject` |

---

## Флаги функций```toml
[dependencies]
lacuna-rs = { version = "0.4", features = ["inject", "stack-spoof", "veh"] }
ВозможностьОписаниеТребуются frame pointers?
syscalls (по умолчанию)Разрешение SSN + генерация JIT-заглушекНет
inject (по умолчанию)Секционная APC-инъекция (inject::inject_sapc)Нет
vehVEH + шифрование параметров через аппаратные точки остановаНет
stack-spoofЦепочка призрачных кадров LACUNA + stack stompДа
no-stdРежим no_std (экспериментальный)Нет

Когда ни одна возможность не включена, доступны только слои сканирования/PE/NT.


Быстрый старт

Сканирование призрачных регионов```sh

cargo run --example scan

### Сборка и проверка цепочки ghost-frame```sh
cargo run --example verify --features stack-spoof

Внедрение shellcode через секцию + APC

Доказательство внедрения Доказательство внедрения -- полезная нагрузка выполнилась внутри целевого процесса.```sh cargo run --example inject --features inject,stack-spoof,veh -- <sc.bin>

Добавьте `--verbose`, чтобы включить диагностический вывод VEH (дампы стека, вывод регистров):```sh
cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin> --verbose

Алгоритм оценки потоков

inject_sapc ранжирует потоки цели. Самый сильный сигнал ранжирования — владеет ли поток окном:

  • Владеет окном (GUI/поток обработки сообщений) — EnumWindows + GetWindowThreadProcessId. GUI-поток находится в alertable ожидании в своём цикле обработки сообщений, поэтому поставленный в очередь APC выполняется там первым. +1000.
  • Время цикла — NtQueryInformationThread(ThreadCycleTime); при недоступности класса на текущей сборке используется ThreadTimes (см. examples/tprobe.rs). Нормализуется и добавляется.
  • Время ЦП — NtQueryInformationThread(ThreadTimes) — время ядра + пользователя.
  • Счётчик приостановок — NtQueryInformationThread(ThreadSuspendCount). Приостановленный поток не может выполнить APC: −2000 за каждую приостановку.
  • Приоритет — NtQueryInformationThread(ThreadBasicInformation). Приоритет 8–10 даёт +150; приоритет вне диапазона даёт −300.

Фильтрация. Приостановленные потоки и потоки, находящиеся в не-alertable ожидании ядра — WrDelayExecution (Sleep/NtDelayExecution) или WrSuspended, считываемые из NtQuerySystemInformation(SystemProcessInformation) — отбрасываются сразу (они никогда не смогут принять APC). В остальном причина ожидания носит исключительно диагностический характер: она логируется для каждого кандидата как State: / Reason: / Alert:, но не влияет на оценку, поскольку KWAIT_REASON не кодирует alertability (alertable и не-alertable ожидание объекта сообщают одно и то же значение — проверено зондированием в стиле examples/tprobe.rs).

Кандидаты сортируются (наибольшая оценка, при равенстве — сначала GUI, затем циклы) и усекаются до MAX_APC_CANDIDATES (10) для перечисления/логирования.

Перечисление ≠ инъекция. Количество перечисленных потоков отделено от количества инъецированных. MAX_APC_CANDIDATES (10) управляет тем, сколько потоков ранжируется и логируется; APC_INJECT_THREADS (5) управляет тем, сколько фактически получают APC.

Почему инъекция в несколько потоков, а затем повтор: пользовательский режим не может определить, какой поток находится в alertable-ожидании, а единственный выбранный поток часто так и не входит в него, поэтому APC никогда бы не сработал. Поэтому inject_sapc ставит APC в очередь для нескольких потоков, а затем повторно ставит в очередь в коротком цикле (~80 мс), пока общее слово-шлюз не переключится с 0 на 1 — то есть пока какой-либо целевой поток действительно не выполнит полезную нагрузку. Он логирует, произошло ли это: payload claimed the gate (выполнено) против gate NOT claimed.

Почему выполняется один раз: один и тот же шелл-код/контекст ставится в очередь для каждого потока, поэтому без защиты нереентерабельная полезная нагрузка выполнялась бы параллельно и обрушила бы хост (измерено: 3 одновременных выполнения). Шлюз реентерабельности (USE_REENTRANCY_GATE, по умолчанию включён) заставляет тело полезной нагрузки выполняться ровно один раз независимо от того, сколько потоков (или сколько раундов повторов) принимают APC (измерено: ровно 1). Оставляйте шлюз включённым, если ваша полезная нагрузка явно не безопасна для параллельного выполнения.


Использование в качестве библиотеки

Базовое: сканирование + разрешение 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);

Скачать инструмент