
C2-агент Adaptix, использующий PIC-линкер Crystal Palace и модульную систему PICO
Агент C2 для Adaptix, построенный на Crystal Palace — кастомном линкере PIC (Position Independent Code) и модульной системе PICO. Демонстрирует, как создавать модульные агенты из шеллкода, где каждый компонент (транспорт, задачи, обфускация) представляет собой отдельный PICO-блоб, загружаемый во время выполнения.
Этот агент не содержит методов обхода средств защиты и не предназначен для использования как есть в реальных операциях. Это эталонная реализация для создания агентов с помощью Crystal Palace и модульной системы PICO.
Crystal Palace — это PIC-линкер, который принимает скомпилированные COFF-объекты и создаёт позиционно-независимые исполняемые файлы. Ключевые концепции:
make pic +gofirst) — основной исполняемый шеллкод. Содержит загрузочный код (bootstrap), DFR-резолвер и маркеры секций, в которые подключаются PICO-модули. Вызывается напрямую загрузчиком.make object) — самодостаточные блобы кода с собственными секциями кода и данных. Загружаются во время выполнения через PicoLoad() из libtcg. Каждый PICO имеет точку входа (go()), вызываемую через указатель на функцию.MODULE$Function (например, KERNEL32$VirtualAlloc) на вызовы resolve(mod_hash, func_hash) с использованием хеширования ROR13. Таблицы импорта нет. Все строковые аргументы (имена DLL, имена функций) формируются в стеке как массивы символов, чтобы избежать открытого текста в бинарнике.entry_module, transport_module и т.д.) через директиву link в файле .spec.{LoadLibraryA, GetProcAddress}, передаваемая в PicoLoad(), чтобы 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 | Описание |
|---|
x86_64-w64-mingw32-gcc (кросс-компилятор MinGW)./setup.sh --ax ../AdaptixC2
whoami, sleep, exit из консоли Adaptix
whoami | 0x30 | Возвращает COMPUTER\username |
sleep <seconds> | 0x20 | Обновляет интервал обратного вызова |
exit thread|process | 0x10 | Завершает работу агента |