- Rename exported symbol go → Initialize in both crystal-loader.c and
crystal-exec.c; update extension.json entrypoint accordingly.
The go(char*, uint32_t, callback) export is the primary MeterBof.A
static signature — renaming it removes the match.
- Remove all [crystal-loader] / [crystal-exec] / PICO-identifying string
literals from both DLLs; replace with short generic error strings.
- Fix crystal-loader.c VirtualAlloc(PAGE_EXECUTE_READWRITE) → VirtualAlloc(RW)
+ VirtualProtect(RX); no RWX mapping ever held (same fix applied to
crystal-exec.c in previous commit).
- Add -s / -ffunction-sections / -fdata-sections / --gc-sections to
wrapper/Makefile; crystal-loader.x64.dll shrinks from ~232 KB to 42 KB.
- Replace VirtualAlloc(PAGE_EXECUTE_READWRITE) with VirtualAlloc(RW) +
VirtualProtect(RX) after XOR decryption — no RWX mapping ever held
- Add gen_pico_header.py: XOR-encrypts the embedded PICO with a fresh
256-byte random key each build, replacing plain xxd output. Crystal Palace
byte patterns in .data are now obfuscated; each build is unique.
- Add -s (strip symbols), -ffunction-sections/-fdata-sections, --gc-sections
to Makefile — DLL shrinks from ~328 KB to ~76 KB
- Change all apt install commands from default-jdk to openjdk-17-jdk;
default-jdk on current Kali/Ubuntu pulls Java 21 which breaks
crystalpalace.jar with NoSuchMethodError
- Fix RUNBOOK.md verify step: ">= 11" → "must be 17"
- TOOLCHAIN.md tools table: mark Java 17 as exact requirement
- TOOLCHAIN.md section 7: add Java version gotcha with fix command
- Add `make -C crystal-exec all` to README quick build, RUNBOOK Phase 1.1
build steps, and quick smoke test — without it pack-extension.sh fails
because crystal-exec.x64.dll is missing from the tarball
- Update crystal-loader.x64.dll verified size: 114 KB → ~232 KB (grew after
post-ex ExitThread and loader fixes were merged)
- Add crystal-exec.x64.dll verified size: ~328 KB (was undocumented)
- Fix stager "~60 KB" placeholder in bundle-stager.sh and stager/Makefile:
actual built size is ~17 KB
- RUNBOOK Phase 1.1: add explicit note that both DLLs must exist before
running pack-extension.sh
Three techniques from RoguePlanet research applied to the stager:
1. NtCreateSection + NtMapViewOfSection instead of VirtualAlloc
- VirtualAlloc absent from IAT entirely
- Nt* resolved at runtime via GetProcAddress (not in IAT)
- RW view written/decrypted, then unmapped
- Separate RX view mapped for execution — no mapping is ever RWX
2. XOR-encrypted PICO (gen_payload.py, 256-byte random key per build)
- Crystal Palace byte patterns invisible to static scanner
- Fresh key every build: unique .data section per compile
- Decrypted in the RW section view before RX remap
3. Poseidon I/O noise (BCryptGenRandom write to temp file)
- Writes 4KB of random data to a temp file, deletes on close
- Adds bcrypt.dll to IAT (legitimate app pattern)
- Creates file I/O activity before execution, disrupts
timing-based behavioural scanners
IAT: ADVAPI32, bcrypt, KERNEL32 — VirtualAlloc gone.
Replaces the Crystal Palace demo run.x64.exe stager which triggers
Defender Wacatac.B!ml. Root cause: run.x64.exe does a naked
ReadFile+VirtualAlloc(RWX)+execute — the pattern with highest ML
feature weight in Defender's loader heuristic.
New stager (stager/stager.c):
- PICO embedded as byte array at compile time via xxd (no ReadFile)
- VirtualAlloc(RW) → RtlMoveMemory → VirtualProtect(RX): no RWX
- WinMain entry: GUI subsystem, no console window
- RegOpenKeyExW/RegCloseKey: adds advapi32 to import table, widening
it beyond the naked kernel32-only set typical of shellcode launchers
- Version info resource (resource.rc): plausible PE identity, customize
CompanyName/FileDescription/OriginalFilename before delivery
- Single file on disk: deliver stager.exe, nothing else
Build:
./bundle-stager.sh build/sliver.crystal.bin build/csvchelper.exe
Verified: clean compile, Windows GUI subsystem, import table has
ADVAPI32 + KERNEL32, no PAGE_EXECUTE_READWRITE in any section.
pico_payload.h and crystalexec_pico.h added to .gitignore — generated
build artifacts (xxd output), never commit.
The backslash at the end of the sed pattern line was inside single
quotes. The shell treats it as a literal backslash, not a line
continuation; sed then sees \<newline> as an unterminated address
regex. Collapse both substitutions onto one sed invocation.
- crystal-exec: new Sliver extension command that runs shell commands
through Crystal Palace evasion (Draugr, AMSI bypass, XOR mask,
cleanup). The post-ex DLL (crystalexec.c) is CRT-free / kernel32-only
and embedded as a byte array inside crystal-exec.x64.dll — no PICO
file to upload.
- crystal-exec uses a single callback model: all output accumulated into
a 4MB heap buffer, exactly one callback() call at the end. Sliver only
displays the first callback invocation; multiple calls are silently
dropped.
- Output channel: anonymous pipe (no disk artifact). 30-second PICO
timeout, 10-second reader drain.
- Extension manifest updated with crystal-exec command entry.
pack-extension.sh updated to include crystal-exec.x64.dll.
- Docs: RUNBOOK Phase 3 rewritten (correct --payload syntax, | runtime
args, postex.sh workflow); Phase 4 added for crystal-exec. TOOLCHAIN
section 3d documents 4-step build pipeline and CRT-free constraint.
Both READMEs updated with Use case C, new layout entries, and verified
table.