
Crystal Palace PIC リンカーと PICO モジュールシステムを使用する Adaptix C2 エージェント
Crystal Palace(カスタムPIC(Position Independent Code)リンカーとPICOモジュールシステム)で構築されたAdaptix C2エージェントです。トランスポート、タスク、難読化の各コンポーネントが実行時に読み込まれる独立したPICOブロブとして実装された、モジュール式シェルコードエージェントの構築方法を示しています。
このエージェントには回避技術は一切含まれておらず、実際のエンゲージメントでそのまま使用することを意図していません。Crystal PalaceとPICOモジュールシステムを使用してエージェントを構築するためのリファレンス実装です。
Crystal Palace は、コンパイル済みのCOFFオブジェクトを受け取り、位置非依存の実行可能ファイルを生成する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に埋め込まれます。{LoadLibraryA, GetProcAddress}は、PICOモジュールが自身のDFRシンボルを解決できるようにPicoLoad()に渡されます。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 | エージェントを終了します |