Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
lacuna-rs — Crate Rust para falsificação de pilha de chamadas ghost-frame, chamadas de sistema indiretas em tempo de execução e injeção APC no Windows x64. Fornece resolução SSN, emissão de stubs JIT e criptografia de parâmetros baseada em VEH para evasão de EDR. | Kitploit
Ferramentas/GitHubGitHub/karkas66/lacuna-rs
ShellcodePós-ExploraçãoRed TeamingDesenvolvimento de PayloadsExploração de Binários
GitHubkarkas66/lacuna-rs

lacuna-rs

Crate Rust para falsificação de pilha de chamadas ghost-frame, chamadas de sistema indiretas em tempo de execução e injeção APC no Windows x64. Fornece resolução SSN, emissão de stubs JIT e criptografia de parâmetros baseada em VEH para evasão de EDR.

Ver Repositório
181há 1 mêsAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

lacuna-rs

Ghost-frame spoofing de pilha de chamadas + syscalls indiretos em tempo de execução para Windows x64 — portado para Rust.

Portado de LACUNA Chain (lacuna_chain.c) por Mohamed Alzhrani (0xmaz).

lacuna-rs é uma crate Rust reutilizável que fornece as mesmas primitivas do TTP C original, estruturada para poder ser integrada em qualquer projeto Rust — como wsyscall-rs ou syscalls-rs, mas com resolução de SSN em tempo de execução, direcionamento syscall;ret por função, e spoofing de pilha ghost-frame.


Crítico: Requisito de Frame-Pointer

A funcionalidade stack-spoof falhará silenciosamente se a crate consumidora não for compilada com frame pointers.

As primitivas de stack-stomping em chain.rs localizam o slot de endereço de retorno do chamador via assembly inline mov rbp, {x}. Isso requer que a cadeia RBP esteja intacta. Rust (e a maioria dos compiladores em modo release) omite frame pointers por padrão.

build.rs define force-frame-pointers=yes para a geração de código desta crate, mas o Cargo não pode propagar flags de compilador para crates downstream. Você deve adicionar isso ao seu próprio projeto:

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

Sem isso, stomp_plant() lerá lixo do RBP e ou não fará nada (melhor caso) ou corromperá a pilha (pior caso). A crate não tem como detectar em tempo de execução se frame pointers estão habilitados — ela simplesmente não funcionará.

Se você precisa apenas das primitivas de scan, resolução de SSN ou injeção (sem stack spoofing), você pode omitir a funcionalidade stack-spoof e este requisito não se aplica.


O que faz


Flags de funcionalidade

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

Quando nenhuma funcionalidade está habilitada, apenas as camadas de scan/PE/NT estão disponíveis.


Início rápido

Scan por regiões fantasma

root@kitploit:~
cargo run --example scan

Construir + verificar a cadeia ghost-frame

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

Injetar shellcode via seção + APC

Injection proof Exemplo de injeção com implante real - C2 estava offline, mas o Shellcode foi executado

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

Adicione --verbose para habilitar a saída de diagnóstico VEH (dumps de pilha, impressões de registradores):

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

Algoritmo de pontuação de threads

Em vez de enfileirar APCs para cada thread no processo alvo (o que pode causar crash quando muitas threads são alertadas simultaneamente), inject_sapc usa um algoritmo de pontuação para selecionar os melhores MAX_APC_THREADS (5) candidatos:

  • Tempo de ciclo — NtQueryInformationThread(ThreadCycleTime) — indicador primário de atividade
  • Tempo de CPU — NtQueryInformationThread(ThreadTimes) — tempo de kernel + usuário
  • Contagem de suspensão — NtQueryInformationThread(ThreadSuspendCount) — threads não suspensas recebem bônus de +300; threads suspensas recebem -150 por contagem de suspensão
  • Prioridade — NtQueryInformationThread(ThreadBasicInformation) — threads com prioridade 8-10 recebem bônus de +150; prioridades fora do intervalo recebem penalidade de -100

Threads completamente ociosas (zero ciclos e zero tempo de CPU) são ignoradas totalmente. Os candidatos restantes são ordenados por pontuação (maior primeiro, desempate por ciclos) e truncados para os 5 melhores.


Usando como biblioteca

Básico: scan + resolução de SSN

root@kitploit:~
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);

Emitir um stub de syscall indireto para qualquer API NT

root@kitploit:~
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()) };

Construir a cadeia ghost-frame + registrar VEH

root@kitploit:~
// 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);

Encapsular um syscall sensível com stack spoof + criptografia de parâmetros

root@kitploit:~
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: O que esconder atrás de ghost frames

Nem toda chamada de API precisa do tratamento LACUNA completo. O princípio chave é: esconda as chamadas que um EDR correlacionaria como atividade de injeção ou pós-exploração.

Devem estar atrás de syscalls indiretos + ghost frames + criptografia de parâmetros

Estes são os syscalls NT "joia da coroa" que EDRs hookam e correlacionam:

Para cada um destes:

  1. Resolva com nt::resolve(ntdll, b"NtXxx\0")
  2. Emita um stub com stub::make_stub(ssn, syscall_ret)
  3. Criptografe parâmetros sensíveis com XOR usando veh::pcrypt_arm(key, syscall_ret, true)
  4. Encapsule a chamada entre chain::stomp_plant() e chain::stomp_restore()

Podem ser diretas (sem ghost frames necessários)

Princípios chave

  1. Resolva qualquer syscall NT com nt::resolve(ntdll, b"NtXxx\0") -- retorna (SSN, syscall_ret_VA).
  2. Emita um stub JIT com stub::make_stub(ssn, syscall_ret) -- retorna um ponteiro de função chamável.
  3. Faça cast do stub para o tipo extern "system" fn(...) apropriado via core::mem::transmute.
  4. (Apenas para chamadas sensíveis) Construa a cadeia ghost-frame com chain::build_chain() e chame chain::stomp_plant() / chain::stomp_restore() em torno da chamada.
  5. (Apenas para chamadas sensíveis) Arme a criptografia de parâmetros com veh::pcrypt_arm(key, syscall_ret, true) antes da chamada, e veh::pcrypt_disarm() depois.
  6. Sempre desarme o VEH antes de fazer chamadas não protegidas -- um acidente DR0 em um syscall desarmado causará crash.
  7. Todas as strings de saída devem ser envolvidas em -- a crate impõe isso, e os diagnósticos VEH são controlados por que padrão é desligado.

O stub lida automaticamente com a sequência mov r10, rcx / mov eax, SSN / jmp [syscall;ret]. Se um ghost gadget foi registrado, o stub roteia através de JMP [RBX] para redirecionamento de execução de uso duplo + quadro de ponte sem artefatos.


Verificação de Cobertura do Writeup

Todas as 9 contribuições principais do writeup do LACUNA Chain estão representadas no código:

Cobertura da superfície de detecção


Por que não usar apenas syscalls-rs?

syscalls-rs e wsyscall-rs geram uma tabela de SSN em tempo de compilação a partir do ntdll de uma build específica do Windows. Se a máquina alvo executa uma build diferente, os SSNs estão errados e os syscalls falham (ou acionam heurísticas de EDR).

lacuna-rs resolve SSNs em tempo de execução lendo os stubs do ntdll diretamente, e direciona a própria instrução syscall;ret da função para que RIP esteja dentro do ntdll na entrada do kernel -- derrotando as heurísticas de "SSN mismatch" e "syscall indireto de memória não respaldada" do EDR.

Além disso, lacuna-rs fornece a cadeia de spoofing de pilha ghost-frame que syscalls-rs não tem.


Requisitos de build

  • Alvo: x86_64-pc-windows-msvc (ou x86_64-pc-windows-gnu)
  • Edição Rust: 2021
  • Dependências: litcrypt2 (ofuscação de strings em tempo de compilação)

Configuração de frame-pointer (apenas para stack-spoof)

A funcionalidade stack-spoof requer frame pointers. build.rs define force-frame-pointers=yes automaticamente para esta crate, mas crates consumidoras também devem defini-lo em seu .cargo/config.toml:

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

Ofuscação de strings litcrypt2

Todos os literais de string na crate e exemplos são envolvidos em macros lc!(), que os criptografam em tempo de compilação e descriptografam em tempo de execução. A chave de criptografia é lida da variável de ambiente LITCRYPT_ENCRYPT_KEY; se não definida, litcrypt2 gera automaticamente uma chave aleatória.

Para fixar uma chave para builds reproduzíveis:

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

Arquitetura

root@kitploit:~
lacuna-rs/
├── Cargo.toml
├── build.rs              # Sets force-frame-pointers for stack-spoof
├── src/
│   ├── lib.rs            # Crate root + re-exports + scan_all() + litcrypt2 setup
│   ├── win.rs            # Win32/NT FFI bindings (no_std-compatible)
│   ├── pe.rs             # PE section + export parsing
│   ├── scan.rs           # .pdata ghost-region + gadget scanning
│   ├── nt.rs             # SSN resolution + syscall;ret targeting
│   ├── stub.rs           # JIT indirect-syscall stub emission
│   ├── veh.rs            # VEH + hardware-breakpoint param encryption
│   ├── chain.rs          # LACUNA ghost-frame chain + stomp
│   └── inject.rs         # Section-based APC injection
├── 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

Créditos

  • Pesquisa original e implementação em C: Mohamed Alzhrani (0xmaz) -- LACUNA Chain
  • Técnica: Ghost Frames: Forjando Pilhas de Chamadas Plausíveis a partir de Lacunas .pdata
  • Lógica de Encontrar Melhor Thread APC: Adaptado de FrankensteinAPCInjection por S12cybersecurity
  • Ofuscação de strings: litcrypt2 v0.1.3

Divulgação Completa

Este projeto foi ativamente assistido por um Chatbot de IA. Tive a chance de testar um modelo Open Source mais novo e muito capaz (GLM-5.2 por Z.ai (Zhipu AI)) e queria dar a ele uma tarefa desafiadora. Lições aprendidas: Funcionou bem se o humano sabe como os resultados devem ser. Se não... apenas lixo.

Licença

MIT

Baixar ferramenta
PrimitivaFunção CMódulo Rust
Análise de seção PE + exportaçãope_section(), pe_export()pe
Scan de região fantasma .pdatascan_ghosts(), best_ghost()scan
Descoberta de ghost-gadget (jmp [rbx])scan_ghost_gadgets()scan
Localizador de gap NOP win32uwin32u_nop_gap()scan
Localizador de âncora BYOUD-MFfind_mf_target()scan
Resolução de SSN (Hell's Gate / Halo's Gate)resolve_ssn()nt
Localizador de syscall;ret por funçãofind_func_syscall()nt
Emissão de stub JIT de syscall indiretoalloc_stub()stub
Redirecionamento de stub ghost-gadget(em alloc_stub())stub
VEH + criptografia de parâmetros via hardware breakpointparam_encrypt_veh(), pcrypt_arm()veh
VEH Chain-guardchain_veh()veh
Construção da cadeia LACUNAbuild_chain()chain
Stack stomp (BYOUD-RT)stomp_plant(), stomp_restore()chain
Percursor da cadeia (verificação)lacuna_walk_chain()chain
Injeção APC baseada em seçãodo_inject_sapc()inject
FuncionalidadeDescriçãoRequer frame pointers?
syscalls (padrão)Resolução de SSN + emissão de stub JITNão
inject (padrão)Injeção APC baseada em seção (inject::inject_sapc)Não
vehVEH + criptografia de parâmetros via hardware breakpointNão
stack-spoofCadeia ghost-frame LACUNA + stack stompSim
no-stdModo no_std (experimental)Não
ChamadaPor que é sensível
NtOpenProcessAbre um handle para outro processo -- primeiro passo da injeção
NtCreateSection + NtMapViewOfSection (remoto)Assinatura de injeção baseada em seção
NtWriteVirtualMemoryEscrita entre processos -- indicador clássico de injeção
NtCreateThreadExCriação de thread remota -- primitiva de injeção de maior sinal
NtQueueApcThreadInjeção APC -- alto sinal
NtProtectVirtualMemoryMudanças de permissão RWX -- indicador de staging de shellcode
NtAllocateVirtualMemory (remoto)Alocação remota -- prelúdio de injeção
NtSetInformationThreadOcultação de thread (HideFromDebugger) -- indicador de evasão
ChamadaPor que é seguro
NtQueryInformationThreadApenas consulta, raramente hookada, sem escrita entre processos
NtDelayExecutionSleep -- benigno, usado por todas as aplicações
NtCloseFechamento de handle -- benigno, extremamente comum
GetModuleHandleA / GetProcAddressResolução de módulo -- não é um syscall, não hookável por hooks de userland do EDR
CreateToolhelp32Snapshot / Thread32First / Thread32NextEnumeração de threads -- kernel32, não syscall ntdll
OpenThread / CloseHandleOperações de handle padrão -- kernel32
GetThreadContext / SetThreadContextNecessário para DR0 -- kernel32, apenas processo próprio
lc!()
set_verbose(true)
#Conceito do WriteupLocalização no CódigoDescrição
1BYOUD-Gap (modificação zero de .pdata)chain.rs -- construção da cadeia ghost frameExplora lacunas entre entradas RUNTIME_FUNCTION; o unwinder as trata como frames folha (RSP += 8)
2Ataque de Janela APC ETW-Tiinject.rs -- dreno alertável NtDelayExecutionControla quando o snapshot de pilha APC ETW-Ti dispara manipulando o estado alertável da thread
3Criptografia de Parâmetros no Contexto BYOUDveh.rs -- pcrypt_arm(), param_encrypt_veh()Criptografa parâmetros de syscall com XOR no staging; descriptografa dentro de um VEH de hardware breakpoint na instrução syscall
4Cadeia de Gap NOP Win32u + Ghost Gadgetscan.rs -- win32u_nop_gap(), scan_ghost_gadgets()1.242 gaps NOP em win32u.dll fornecem frames folha na lista branca; ghost gadget JMP [RBX] em ntdll+0xFC47B
5Proximidade Semântica de Ghost do kernelbasechain.rs -- camada L2_kbase perto de VirtualProtectGhost de 238 bytes terminando no ponto de entrada de VirtualProtect -- indistinguível de um site de retorno VP real
6BYOUD-MF (Teletransporte de RSP de Frame de Máquina)chain.rs -- struct MachFrame, scan.rs -- find_mf_target()Explora UWOP_PUSH_MACHFRAME (opcode 10) em dispatchers KiUser* para teletransporte arbitrário de RSP em um único frame
7BYOUD-RT (Cálculo de RSP em Tempo de Execução)chain.rs -- teb_stack_base(), teb_stack_limit()Lê TEB.StackBase (GS:[0x08]) no momento da chamada para calcular a distância exata do frame -- sem necessidade de pré-calibração
8Proximidade de Ghost wow64.dllchain.rs -- camada L1_wow64, scan.rs -- alvos Wow64PrepareForExceptionGhost de 91 bytes terminando na entrada de Wow64PrepareForException -- quarta camada semântica da cadeia
9Cadeia LACUNA de Seis Camadaschain.rs -- struct LacunaStack (L1-L5 + MachFrame)Cadeia completa: KiUserExceptionDispatcher -> wow64 -> kernelbase -> ntdll -> win32u -> RtlUserThreadStart
Camada de DetecçãoStatusImplementação
Verificação de módulo de origemEVADIDOTodos os frames em ntdll / kernelbase / wow64 / win32u
Correção da caminhada de unwindEVADIDOTodos os frames lacuna são folha -> RSP+8 válido
Scan forense de .pdataEVADIDOModificação zero; lacunas são pré-existentes
Pilha sombra CETEVADIDOCadeia puramente folha; pilha sombra não consultada
Análise semântica de frameEVADIDOExceção WoW64 + adjacência VirtualProtect
Isenção de regra Win32uEVADIDOCamada 4 explicitamente excluída por todas as regras
STACKWALK ETW-TiEVADIDOAtaque de janela APC controla o tempo do snapshot
Inspeção de parâmetrosEVADIDODescriptografia VEH via HW breakpoint
Callbacks de kernelPARCIALOperações de handle ainda disparam ObRegisterCallbacks