
Crystal Palace PIC 링커와 PICO 모듈 시스템을 사용하는 Adaptix C2 에이전트
Crystal Palace — 커스텀 PIC(Position Independent Code) 링커 및 PICO 모듈 시스템 — 기반으로 빌드된 Adaptix C2 에이전트입니다. 각 구성 요소(transport, tasks, obfuscation)가 런타임에 로드되는 별도의 PICO 블롭인 모듈형 셸코드 에이전트를 빌드하는 방법을 보여줍니다.
이 에이전트는 탐지 회피(evasion) 기법을 포함하지 않으며, 실제 작전(engagement)에 그대로 사용하기 위한 것이 아닙니다. Crystal Palace와 PICO 모듈 시스템으로 에이전트를 구축하기 위한 참조 구현입니다.
Crystal Palace는 컴파일된 COFF 개체를 입력받아 position-independent 실행 파일을 생성하는 PIC 링커입니다. 핵심 개념:
make pic +gofirst) — 메인 실행 셸코드입니다. 부트스트랩 코드, DFR 리졸버, 그리고 PICO 모듈이 링크되는 섹션 마커를 포함합니다. 로더가 직접 호출합니다.make object) — 자체 코드 및 데이터 섹션을 가진 독립형 코드 블롭입니다. libtcg의 PicoLoad()를 통해 런타임에 로드됩니다. 각 PICO에는 함수 포인터로 호출 가능한 엔트리 포인트(go())가 있습니다.MODULE$Function 구문(예: KERNEL32$VirtualAlloc)을 ROR13 해싱을 사용하는 resolve(mod_hash, func_hash) 호출로 대체합니다. 임포트 테이블이 없습니다. 모든 문자열 인자(DLL 이름, 함수 이름)는 바이너리 내 평문을 피하기 위해 char 배열로 스택에 구성됩니다..spec 파일의 link 지시문을 통해 명명된 섹션(entry_module, transport_module 등)에서 Core PIC에 임베드됩니다.PicoLoad()에 전달되는 Crystal Palace 구조체 {LoadLibraryA, GetProcAddress}로, PICO 모듈이 자체 DFR 심볼을 해석할 수 있게 합니다.C source → mingw-gcc → COFF objects → Crystal Palace link → raw PIC shellcode → loader (Exe/Dll/Svc)
.spec 파일은 Crystal Palace가 모든 것을 링크하는 방식을 정의합니다:
x64:
load "Bin/obj/main.x64.o" # Core PIC
make pic +gofirst
foreach %LIBS: mergelib %_ # Merge libtcg
load "Bin/obj/entry.x64.o" # Entry PICO
make object
load "Bin/obj/crypto.x64.o" # merge crypto into entry
merge
load "Bin/obj/packer.x64.o" # merge packer into entry
merge
export
link "entry_module" # link at section marker
load "Bin/obj/transport.x64.o" # Transport PICO
make object
mergelib "lib/LibWinHttp/..."
export
link "transport_module"
... # task_module, obfuscation_module
dfr "resolve" "ror13" # resolve all DFR symbols
export
Core PIC (main.c)
│
├── resolve() DFR bridge → libtcg hash lookup
├── AllocateAndLoadModule() PicoLoad each PICO into shared RWX region
│
└── calls entry module go() with pointers to all other modules
│
├── Entry Module (entry.c)
│ MvState, checkin, transact (RC4 wire format), task loop
│
├── Transport Module (transport.c)
│ HTTP/HTTPS POST via LibWinHttp
│
├── Task Module (tasks.c)
│ Command dispatch: whoami (0x30), sleep (0x20), exit (0x10)
│
└── Obfuscation Module (obfuscation.c)
Ekko sleep — timer-queue ROP chain that encrypts module memory
with RC4 (SystemFunction033) during sleep, decrypts on wake
┌─────────────────────────────┐
│ Shared RWX Region │ VirtualAlloc(PAGE_EXECUTE_READWRITE)
│ ├── Entry code │ PicoLoad → code here
│ ├── Transport code │
│ ├── Task code │
│ └── Obfuscation code │
├─────────────────────────────┤
│ Entry data (RW) │ PicoLoad → data here (separate alloc)
│ Transport data (RW) │
│ Task data (RW) │
│ Obfuscation data (RW) │
├─────────────────────────────┤
│ Core PIC (freed after boot) │ Original shellcode, freed by entry module
└─────────────────────────────┘
공유 RWX 영역은 Ekko가 슬립 주기 동안 암호화/복호화하는 대상입니다.
모든 트래픽은 RC4(스트림 암호, 16바이트 키)로 암호화됩니다.
Send: [36B agent_id][RC4(payload)][16B key (first checkin only)]
Receive: [36B agent_id][RC4(response)]
src_beacon/Source/
├── main.c Core PIC — DFR resolver, module loading, bootstrap
├── entry.c Entry PICO — agent state, checkin, task loop, transact
├── transport.c Transport PICO — HTTP POST via LibWinHttp
├── tasks.c Task PICO — whoami/sleep/exit dispatch
├── obfuscation.c Obfuscation PICO — Ekko sleep (timer-queue ROP + RC4)
├── crypto.c RC4 stream cipher (merged into entry PICO)
├── packer.c Binary packer BE / parser LE (merged into entry PICO)
└── includes/
├── config.h Build-time defines (UUID, sleep, callback host/port/uri/ssl)
├── crypto.h RC4 API
├── packer.h PackBuf / Parser API
├── tcg.h Crystal Palace libtcg (PicoLoad, findModuleByHash, etc.)
└── HTTP.h LibWinHttp API
| 명령 | ID | 설명 |
|---|---|---|
whoami |
x86_64-w64-mingw32-gcc (MinGW 크로스 컴파일러)./setup.sh --ax ../AdaptixC2
whoami, sleep, exit 명령을 사용합니다.
| 0x30 |
COMPUTER\username 반환 |
sleep <seconds> | 0x20 | 콜백 간격을 업데이트합니다 |
exit thread|process | 0x10 | 에이전트를 종료합니다 |