LSH delish

Wooden sign promoting Australian desert dessert with strawberries, honeycomb, and Pavlova illustration

Another Crystal Palace and Tradecraft Garden release is now available. This release fills some gaps from recent releases and rounds out the feature set (aka, stuff I wanted to ship, but didn’t get to in time).

Language-specific Handlers

Two releases ago, I added stack unwinding meta-info generation to Crystal Palace. My original goal was to provide a tool to help restore OS-compliant walkable call stacks to programs. But, I left one opportunity on the table.

The stack unwinding meta-information is also our opportunity to, function by function, specific a language-specific exception handler (LSH). The purpose of the LSH is to give a language runtime the opportunity to act on an exception and delegate it to something specified by that program.

LSHs get invoked when there’s an exception. VEH gets first dibs. If no VEH swallows the exception, the operating system then unwinds the stack of the thread where the exception originated. Each frame’s stack unwinding meta-information is consulted, looking for a LSH. Where there’s a LSH, it’s invoked and it has the opportunity to act on the exception or let the process continue.

To setup a LSH, use the catch command:

catch "function" "lsh_handler"

The LSH prototype looks a lot like a vectored exception handler and it seems they can do a lot of the same things:

EXCEPTION_DISPOSITION handler(
IN PEXCEPTION_RECORD ExceptionRecord,
IN ULONG64 EstablisherFrame,
IN OUT PCONTEXT ContextRecord,
IN OUT PDISPATCHER_CONTEXT DispatcherContext);

I updated verify.spec and verify.c from Simple Unwind to use a LSH instead of a VEH.

Note: VEH and LSH do not have compatible return values. LSHs return an EXCEPTION_DISPOSITION enum value.

User-defined Intrinsics

This release introduces user-defined intrinsics to Crystal Palace too. An intrinsic is a function call that gets resolved into something else during a program rewriting pass.

The syntax:

intrinsic "__foo" $VAR

The above defines intrinsic foo and replaces CALL foo with the contents of $VAR.

One way I would use this is to pass configuration information to a program, something like:

pack $VAR "bi" 0xB8 %value

The above creates a MOV eax, %value instruction.

In the program, declare __foo() with whatever prototype and call it to get %value:

DWORD __foo();

Easier .spec file hooking

One of the handy features in Crystal Palace is the ability to hook parts of a .spec file from another .spec file. I use this with @config.spec files. A configuration .spec file is a .spec that evaluates, before the main project, to set global variables and other meta-information.

Crystal Palace has had a before command for awhile. before hooks a specific command and executes something else before it runs:

before "command1" : command2

The above was extended a few releases ago and we have before and after. And, the ability to get more specific with the hook. That is, we can match on the command, export type, and file name too.

One of the problems with before and after is executing a block of commands from a config.spec file. There was no easy way to do this. The main solution was to create a before or after command for each individual command.

Now, we have callnear. This command is like call. But, it canonicalizes the target file immediately when the command is parsed. So, when callnear “config.spec” is passed to before or after, it’ll call config.spec relative to our config.spec and not the file we hooked.

Simple Unwind’s verify.spec is an example of callnear:

processpic.x64:
    # let's merge in verify.x64.o in our PICO loading PIC
    load "bin/verify.x64.o"
        merge
 
    # let's designate MyHandler as the "language-specific exception" handler for go. This will
    # allow us to "catch" the PICO's debug break when it happens. This requires that our PIC's
    # stack unwinding meta-information is registered with the OS (e.g., RtlAddFunctionTable)
    catch "go" "MyHandler"
 
x64:
    # let's drop an int3 (debug break) before a call to MessageBoxA in our PICO
    pack $INT3 "b" 0xcc
    before "export" "object" : ised insert "mov rax, qword ptr [__imp_USER32$MessageBoxA]" $INT3 +before
 
    # call the local (to this file) label processpic before we export PIC
    before "export" "pic" : callnear "verify.spec" "processpic"

Migration Notes

None.

Feature Tour Video!

This project has come a long ways in the past year. And, I appreciate there’s a lot in here and it may not always immediately jump out what that stuff is, why it’s here, and how it works together. To help make some sense of it, I put together a feature tour video too:

To see a full list of what’s new, check out the release notes.

Cruising Forward with the Tradecraft Garden

A new Tradecraft Garden and Crystal Palace release is available. This release introduces a proper install script and consolidates its commands behind a cpl [verb] CLI interface. I’ve also added an x64 tail call intrinsic (__transfer) and expanded API hashing beyond ror13.

The new CLI Interface

Crystal Palace’s loose link, piclink and other commands are now gone. In their place, we have cpl—the single CLI entry point to all things Crystal Palace:

Use cpl [verb] [args] to execute a Crystal Palace command. Here’s a table showing how the old commands map to the new ones:

Old CommandNew CommandWhat does it do?
coffparsecpl coffparsePrint parsed COFF
disassemblecpl disassemblePrint disassembled object code
linkcpl linkLink DLL/object to loader
linkservecpl serverStart JSON-over-HTTP sidecar service
piclinkcpl buildBuild program from .spec

Crystal Palace now has an install procedure too! Run ./install to create a cpl script in ~/.local/bin (or, edit it to another place of your choosing). I’ve also created an optional bash tab completion script too. It’ll tab-complete the cpl verbs and @config.spec files too. The install script walks you through how to set that up.

__transfer()

One of the problems in Tradecraft Garden is we often have a loader, executed by something in backed memory, that sets up an ideal situation and passes execution to it. While we can presume what ran our loader is OK and what we setup is OK, it’s not ideal to have evidence of our loader in the callstack. __transfer is a tiny tool to help with that.

__transfer is an x64-only linker intrinsic that expands to a tail call at link time. A tail call is a function call that doesn’t return to the parent. Instead, it tears down the stack frame of the caller, jumps to the callee, and when complete—the callee returns to the caller’s caller. The effect is the caller isn’t in the stack.

C compilers often use tail calls as an optimization. And, some compilers have decorations to explicitly enable a tail call. The version of MinGW I’m working with (12/13) doesn’t. In general, I’d much prefer this feature come from the compiler vs. my bin2bin linker. But, I added this feature to fill a gap.

The contract for __transfer is pretty straight forward. The target function is a void function that takes no arguments:

void gohere();

And, calling __transfer looks like:

__transfer(gohere);

That’s it. The prototype for __transfer is defined in tcg.h. The Module Stomp example demonstrates __transfer in action.

More API Hashing

When I added Dynamic Function Resolution to Crystal Palace, I designed the feature to allow different Win32 API resolution resolver contracts. For a long time, we’ve had two contracts. ror13 calls a resolver with ror13 hashes of a desired module and function. And, strings calls a resolver with pointers to stack strings with the desired module and function.

This release adds a few more API hashing contracts. We now have: djb2, fnv1a, and sdbm.

The new Simple Loader (Alt. API Hashing) demonstrates using these other algorithms with a simple PIC DLL loader.

Migration Notes

1. Update any scripts or documents that reference link, piclink, etc. to use their cpl equivalents.

2. The Simple BOF bofprep.spec no longer explicitly imports BeaconOutput for you. Add this after run bofprep.spec to any .specs using this script:

import "LoadLibraryA, GetProcAddress, BeaconOutput"

3. LibTCG’s findFunctionByHash now calls GetProcAddress when it detects a forwarded function. This may cause accurate, but surprising “don’t call dprintf from a dfr context” type error messages when __resolve_hook, dprintf calls, and hooked GetProcAddress mix together.

If you use __resolve_hook and attach to GetProcAddress, consider evicting this hook from findFunctionByHash in LibTCG:

preserve "KERNEL32$GetProcAddress" "findFunctionByHash

The benefit is you can dprintf in your GetProcAddress hook without this well-meaning check popping up.

Explanation: OutputDebugStringA uses SEH under the hood, which in a dynamic stack PIC context (with no unwinding data) can lead to issues. CPL does a call graph walk to look for dprintf in dangerous situations.

Closing Thoughts

The above touches on the new features in this release, but it fails to get at the depth of maintenance present here too. Because of __transfer, I did a lot of work to overhaul and consolidate function prologue and epilogue walks. This invited a closer look at +regdance and many improvements were made there. I also finished up the CMP and TEST instruction coverage for x64 fixbss and x86/x64 fixbss and fixptrs.

This is an opportune moment to share a quick thought on my playbook for software projects. I’m a big fan of “the cruise ship model”. That is, when moving a project forward, think about keeping 1/3 familiar and unchanged, 1/3 iterate and subtly improve what’s already working, and 1/3 try something new and bold—that might also fail to take.

In my projects I often aim for 1/3 bug fixes and refactoring (no user facing changes), 1/3 iterating and improving existing features, and 1/3 making the noticeable changes that move the project forward and change the experience of what it is or can do.

While no individual release follows this strictly, the end idea is to balance these three. Projects that neglect their architecture and internals become buggy and eventually, too complex to move forward. Projects that fail to identify needed points of iteration and address them are incomplete and will often disappoint their users. And, projects that fail to bring new things are either complete (that’s valid) or they’re stagnant.

As this project’s progressed over the past year, I hope you’ve seen elements of all three in its development priorities.

To see a full list of what’s new, check out the release notes.

A Long-running BOF Component Contract

Last week, I came across the Asynchronous PICOs project released by Marcos Gonzalez Hermida at NCC Group. The project is a source code framework for in-process long-running PIC jobs in Cobalt Strike.

The Async PICOs project uses Crystal Palace PICOs (output as PIC) to define a base convention.

I like the problem Marcos chose here. And, I like the broad contours of Marcos’ design solution too, which draws inspiration from Outflank’s Async BOFs. My interest in this problem set isn’t the API, but rather to discuss building component contract frameworks with Crystal Palace.

In this post, we’ll walk through the broad ideas in the Asynchronous PICOs design. And, I’ll then walk-through Long-running BOFs which is a demonstration of some Asynchronous PICO ideas but using a component contract.

By the end of the post, I hope to demonstrate that component contracts create flexibility, open up component re-use, and lead to simpler framework and component implementations.

If you’re exploring Crystal Palace to build systems or tradecraft, consider this post a best practice tour using a substantial system as a discussion point.

The Asynchronous PICO Framework

An Asynchronous PICO is a containerized long-running job. Something that you want to run in the same process as your agent, but with the ability to wake the agent when a PICO has output.

The Asynchronous PICO system has three components. A manager BOF, the asynchronous PICOs themselves, and a BeaconGate template that adds supports for this system to Cobalt Strike.

The manager BOF starts, stops, and lists the running asynchronous PICOs. The manager is also responsible for initializing the shared global state for this system. The global state consists of:

The manager BOF kicks off each asynchronous PICO in its own thread. BOFs are ephemeral. So, once the manager BOF is done with a task, its code and state goes away.

Asynchronous PICOs are COFF objects that implement a framework-specific BeaconPrintf. This function grabs the critical section lock, adds output to the shared data structure, releases the lock, and signals any threads waiting on a condition variable to wake up.

Asynchronous PICOs receive their copy of these shared variables via their entry point.

The last piece is the wake-up itself. Async PICOs includes a template BeaconGate for Cobalt Strike. The template is a foundation to port other sleep masking and function call decorating tradecraft to.

The template intercepts calls to Sleep and instead, calls SleepConditionVariableCS. This is a sleep function that wakes when there’s a signal on a condition variable or certain time has passed.

On wake, the template queries Async PICO shared output and dispatches it via Cobalt Strike’s Beacon API.

Aligning to Crystal Palace Best Practices

While most of this post is about re-implementing the high-level design ideas in Asynchronous PICOs, there are a few Crystal Palace best practices I’d like to raise first.

(1) MSVC C++ -> MinGW C

The Asynchronous PICO framework and Asynchronous PICOs are written in C++ and compiled with MSVC. This departs from Crystal Palace’s recommended MinGW C compiled with -O0, -O1, and (somewhat supported) -Os.

The Async PICOs framework makes this work by including a program to convert MSVC C++ COFF to something Crystal Palace can use. While clever, I don’t consider this the optimal path.

Crystal Palace does a lot of bin2bin program rewriting for some of its features. Transforming code to PIC, for example, is an intensive process. All of my automated tests, static analysis to anticipate pitfalls, hands-on QA, anticipation of compiler output, etc. is largely scoped to MinGW C with the described flags. Go too far outside of this scope and you risk surprises I’ve scrubbed from the recommended path.

(2) PIC -> PICO Output

The Asynchronous PICO framework converts user programs to raw PIC with Crystal Palace. PICO output would work better here.

PICOs are both a convention AND an output. PICO output is run by a loader and they work much like BOFs (without a specific API). I introduced PICO output with the first version of Crystal Palace as something to embed within and deploy from bootstrapping PIC. Because PICO outputs are processed by a loader, they have access to global variables and the Win32 API without making their own tradecraft choices.

Crystal Palace PIC are written like PICOs (e.g., they use the DFR MODULE$Function format), but their functionality is realized in a much different way. Because PIC doesn’t have the benefit of a loader, we have to solve resolving Win32 APIs and restoring global variables in creative ways. These steps are swappable tradecraft choices. Crystal Palace’s solution is to insert user-specified helper function calls into the PIC, to restore things as needed. One of the limits Marcos ran into was Crystal Palace’s Simple PIC isn’t friendly to multiple PIC in one process. Loaded PICOs won’t have this issue.

Crystal Palace makes PIC and PICOs look the same, to promote code re-use as shared libraries. They’re not the same though. I’d limit use of PIC to bootstrapping situations. If you’re allocating new memory from pre-existing code, you’re almost always better off with loader-ready PICO output.

(3) Source Code Frameworks -> Component Contracts

Asynchronous PICOs is a source code framework. While the rest of this post is devoted to demonstrating a component contract, I think it’s helpful to contrast the approaches.

A source code framework is a template. The implementation and contract are tied together. If there’s a change to the implementation later, then all of the components built on the template need an update. Further, source code frameworks are hard to compose on top of. They often require editing the template to port things over. Source code frameworks also encourage monolith implementations, where lightly intersecting features and tradecraft are tangled together. The magic of composing things together rarely emerges in these contexts.

Part of the vision of Crystal Palace is to arrive at an eco-system with a common base skill, that’s not specific to a C2 or even offensive security, and to explore sensible ideas to isolate ground truth evasion primitives into usable-elsewhere containers. The entire project is a push for component contracts, both within itself and the systems that use it.

If you want more on this, watch Post-ex Weaponization: An Oral History. That’s my recounting of offensive security’s Win32 component contracts of the last 18 years or so. Crystal Palace is like a meta-component contract.

LR-BOF Component Contract

The above out of the way, let’s start our effort implementing Long-running BOFs. Earlier, I mixed together the Async PICOs implementation details and the convention of what describes an Async PICO. The first rule of component contract fight club is to separate the definition of the convention from the implementation itself. As a designer, it’s critical to have an implementation in mind. And, yes, you should build and test that implementation too. But, where possible, a contract shouldn’t expose or depend on the specifics of that implementation.

A Long-running BOF is a compiled COFF that implements a subset of the Beacon API. I’ve chosen the same subset from Tradecraft Garden’s Simple BOF implementation. And, I’ve extended the API with one function:

BOOL BeaconIsRunning()

Like other Beacon Object Files, our LR-BOFs will use go() as their entry point:

void go(char * args, int alen);

The idea is that we could run a BOF as-is. Or, let something run and give it the opportunity to clean itself up (gracefully) when BeaconIsRunning is FALSE.

Here’s an example of a Long-running BOF that prints a message when the user goes idle or becomes active again:

#include <windows.h>
#include "lrbof.h"

DECLSPEC_IMPORT DWORD WINAPI KERNEL32$GetTickCount(VOID);
DECLSPEC_IMPORT VOID  WINAPI KERNEL32$Sleep (DWORD dwMilliseconds);
DECLSPEC_IMPORT BOOL  WINAPI USER32$GetLastInputInfo(PLASTINPUTINFO plii);

void go(char * args, int alen) {
	LASTINPUTINFO idle;
	idle.cbSize = sizeof(LASTINPUTINFO);

	BOOL isIdle = TRUE;

	while (BeaconIsRunning()) {
		if (USER32$GetLastInputInfo(&idle)) {
			DWORD idleTime = KERNEL32$GetTickCount() - idle.dwTime;
			if (idleTime > 10000 && !isIdle) {
				BeaconPrintf(CALLBACK_OUTPUT, "User is idle (10s)");
			}
			else if (idleTime <= 10000 && isIdle) {
				BeaconPrintf(CALLBACK_OUTPUT, "User is active!");
			}

			isIdle = idleTime > 10000;
		}

		KERNEL32$Sleep(1000);
	}
}

Compile this like any other BOF:

x86_64-w64-mingw32-gcc -O1 -c idlewatch.c -o idlewatch.x64.o

Preparing LR-BOFs for an Asynchronous Runner

Now, let’s dig into the specifics of how to consume Long-running BOFs, in a context similar to Asynchronous PICOs.

At a high-level, we’ll pass our LR-BOFs through the Crystal Palace linker at time of use. Instead of converting the object to PIC output, we’ll turn it into loader-ready PICO output.

Here’s the .spec file to ready an LR-BOF for asynchronous execution:

x64:
	# turn the COFF (on stack) into a PICO, remove unused functions
	make object +optimize

	# prepare this BOF for execution... but call LR_go as our entry point
	run "external/simple_bof/bofprep.spec" "LR_go"

	# bofprep.spec makes BeaconOutput a third import. We don't want that
	import "LoadLibraryA, GetProcAddress"

	# bring in our long-running BOF adapter code (LR_go, LR_BeaconOutput)
	load "bin/lrbof.x64.o"
		merge

	# bring in a module with buffer packing/parsing functions
	load "bin/buffer.x64.o"
		merge

	# bring in LibTCG (for things like dprintf)
	mergelib "external/libtcg/libtcg.x64.zip"

	# attach our non-merged LR-BOF APIs to the internal thing
	attach "$BeaconOutput"    "LR_BeaconOutput"
	attach "$BeaconIsRunning" "LR_BeaconIsRunning"

	export

Let’s walk through what’s going on here. First, we merge in an implementation of the BOF API and wire it up via our .spec file. We’re using Tradecraft Garden’s bofprep.spec for this.

You’ll note that we’re specifying the entry point as LR_go. This entry point is specific to our asynchronous implementation of LR-BOFs. It’s in lrbof.x64.o which we time-of-use merge into our LR-BOF object.

Here’s our asynchronous implementation LR-BOF support code:

#include <windows.h>
#include "tcg.h"
#include "buffer.h"
#include "lrbof_int.h"

DECLSPEC_IMPORT void WINAPI KERNEL32$EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
DECLSPEC_IMPORT void WINAPI KERNEL32$LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
DECLSPEC_IMPORT void WINAPI KERNEL32$WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable);

/*
 * Global variables we need.
 */
LRBOF_SHARED * globals;

/*
 * Implementation of BeaconOutput() API. See lrbof.spec
 */
void LR_BeaconOutput(int type, char * data, int len) {
	/* grab our lock */
	KERNEL32$EnterCriticalSection(&globals->hCritSect);

	/* add a TLV of our output to our global output. We'll walk this on wake */
	BufferPutInt(&globals->output, type);
	BufferPutString(&globals->output, data, len);

	/* release our lock */
	KERNEL32$LeaveCriticalSection(&globals->hCritSect);

	/* wake up the agent */
	KERNEL32$WakeConditionVariable(&globals->hCondition);
}

/*
 * Implementation of BeaconIsRunning() API. See lrbof.spec
 */
BOOL LR_BeaconIsRunning() {
	return TRUE;
}

/*
 * the BOF's entry point
 */
void go(char * args, int alen);

/*
 * Our entry point which is LPTHREAD_START_ROUTINE compatible
 */
DWORD __stdcall LR_go(LRBOF_ARGS * parms) {
	/* set our globals */
	globals = parms->shared;

	/* call the BOF's go() entry point */
	go(parms->args, parms->alen);

	return 0;
}

And, this is the definition of LRBOF_SHARED:

typedef struct {
	CRITICAL_SECTION    hCritSect;
	CONDITION_VARIABLE  hCondition;
	bufferp             output;
} LRBOF_SHARED;

The LR_go function is implemented with a CreateThread-friendly contract. It receives a pointer to a struct with the system’s shared global variables and the BOF’s arguments. Once the shared global variables are set, LR_go calls the LR-BOF’s go entry point.

LR_BeaconOutput and LR_BeaconIsRunning are our asynchronous implementation versions of BeaconOutput and BeaconIsRunning from the LR-BOF API. These are wired up with attach in the .spec file.

LR_BeaconOutput works much like Async PICOs. It grabs a lock (hCritSect), posts the output to a shared data structure, releases the lock, and signals output via our condition variable. Here, we’re using a packed byte buffer to store output as type/length/variable strings.

This demonstration doesn’t implement a mechanism to manage and stop LR-BOFs. So, I’ve made BeaconIsRunning always return true.

An Asynchronous Runner for LR-BOFs

The last piece of this system is the Long-running BOF asynchronous demonstration harness.

Here’s the demonstration’s entry point:

void go() {
	bufferp parser;
	int     boflen;
	char *  bofsrc;

	/* initialize our global vars */
	LR_Init();

	/* parse our BOF data */
	BufferParse(&parser, packedbofs.value, packedbofs.length);

	/* start all of our appended BOFs */
	while (BufferRemaining(&parser) > 0) {
		bofsrc = BufferGetString(&parser, &boflen);
		LR_Run(bofsrc, NULL, 0);
	}

	/* Sleep(), wake up, print a message... yada yada yada */
	while (TRUE) {
		LR_Sleep(15000);
		dprintf("I'm awake!");
	}
}

The demonstration harness accepts a list of LR-BOFs, runs each in a separate thread, and then loops printing a “I’m awake” message every 15 seconds. This creates something that our threaded LR-BOFs can wake up when there’s output.

Here’s the code to run our LR-BOFs:

void LR_Run(char * src, char * _args, int _alen) {
	char        * dstCode;
	char        * dstData;
	IMPORTFUNCS   funcs;
	LRBOF_ARGS    args;

	/* allocate memory for our PICO */
	dstCode = KERNEL32$VirtualAlloc( NULL, PicoCodeSize(src), MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE );
	dstData = KERNEL32$VirtualAlloc( NULL, PicoDataSize(src), MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, PAGE_READWRITE );

	/* setup our IMPORTFUNCS data structure */
	funcs.GetProcAddress = GetProcAddress;
	funcs.LoadLibraryA   = LoadLibraryA;

	/* load our pico into our destination address, thanks! */
	PicoLoad(&funcs, src, dstCode, dstData);

	/* grab our entry point */
	LPTHREAD_START_ROUTINE entry = (LPTHREAD_START_ROUTINE)PicoEntryPoint(src, dstCode);

	/* setup our arguments */
	args.shared = &globals;
	args.args   = _args;
	args.alen   = _alen;

	/* kick off our thread */
	KERNEL32$CreateThread(NULL, 0, entry, &args, 0, NULL);
}

If you’ve spent any time in the Tradecraft Garden, this is a typical runner for PICO output. The only difference is we setup the arguments for our PICO entry point (LR_go) and call CreateThread to execute it.

The last moving part in this system is the Sleep function:

VOID WINAPI LR_Sleep (DWORD dwMilliseconds) {
	/* wait on our condition variable. Returns false if the timeout gets hit */
	if (!KERNELBASE$SleepConditionVariableCS(&globals.hCondition, &globals.hCritSect, dwMilliseconds))
		goto done;

	/* create a view of our output buffer that we can walk */
	bufferp view;
	BufferView(&globals.output, &view);

	/* walk our shared output buffer, printing out the TLVs */
	while (BufferRemaining(&view) > 0) {
		int    type   = BufferGetInt(&view);
		int    length = BufferGetInt(&view);
		char * value  = BufferGetPtr(&view, length);

		dprintf("[%04x] [%04d] %.*s", type, length, length, value);
	}

	/* reset the output buffer */
	BufferReset(&globals.output);

done:
	/* give up our lock when we're done */
	NTDLL$RtlLeaveCriticalSection(&globals.hCritSect);
}

Like the Asynchronous PICO system, this demo calls SleepConditionVariableCS to wait on a condition or a timeout. Assuming there’s output, we walk through our output buffer, extract the type/length/value items, and send it to DebugView.

The above is the Long-running BOF system, in full.

Demonstration

To try this out, you can grab the code at:

https://tradecraftgarden.org/download/lrbofs20260610a.tgz

Build:

make clean ; make all

Linking with Crystal Palace:

/path/to/piclink demo.spec x64 out.bin %BOFS="examples/bin/wakeup.x64.o"

Run it:

path\to\demo\run.x64.exe out.bin

Going beyond asynchronous LR-BOFs

Above, I demonstrated an implementation of LR-BOFs that’s meant as faithful to the Asynchronous PICOs design. But, the beauty of a component contract is that we’re not tied to a single interpretation.

For example, let’s say we want to run a LR-BOF but without creating a new thread. We’re OK losing control of our agent for five minutes or a few hours. In this situation, we could easily make our LR-BOFs synchronous. We’d just implement a runner that merges in supporting code that notes its start time and has BeaconIsRunning return FALSE after the time out has elapsed. One component contract, different uses.

As another speculative possibility, we could try out a fiber-based runner for LR-BOFs. On initialization of the system, we create a thread for all of our LR-BOFs. Our merged implementation support might include a scheduler to call SwitchToFiber on another LR-BOF. A design consideration here is when to yield? Do we do it on BeaconIsRunning? When there’s new output? Hell, with Crystal Palace’s attach, we could easily attach to WaitForSingleObject, Sleep, and other common blocking functions to force a yield. Better, Crystal Palace’s link-time optimizer gets rid of merged-in functions that are never called (e.g., attach hooks with nothing to attach to). We’re not gaining bloat when we design hooks for a lot of situations.

The above is some of what’s possible when moving to a component contract that doesn’t force a specific implementation.

Could this work in Cobalt Strike?

The demonstration I’ve put together is C2 agnostic. But, I’ve implemented the pieces in copy and paste ready chunks. Here’s how I would carry over these ideas to a system like Asynchronous PICOs:

The functions LR_Run and LR_Init would go into a manager BOF that’s responsible to initialize the system, start jobs, stop them, and list them. Instead of storing these as a global, they would live in the C2’s key/value store.

To implement stop functionality, I’d update LRBOF_ARGS to include a pointer to the job’s shared state object. And, I’d update BeaconIsRunning to grab a lock, query that object for a shouldRun boolean, release the lock, and return the value. Stopping the job would involve updating this shared value.

Where this gets really fun though, is the BeaconGate part of the system. The Asynchronous PICO Framework has a BeaconGate template to intercept Sleep calls and carry out the LR_Sleep type of logic. The expectation is that a team using Asynchronous PICOs would need to port their BeaconGate tradecraft to this new foundation.

There’s another way, though.

In April, Daniel Duggan published Crystal Mask. The blog post walks through implementing a BeaconGate with Crystal Palace. He doesn’t link to a source repository, because the code is so short—it doesn’t need one. Everything is in the post.

Daniel’s post includes a basic XOR masking BeaconGate.

He then updates his BeaconGate .spec to merge a Draugr call stack spoofing into it:

x64:
    load "bin/sleepmask.x64.o"
        make coff +optimize

    # weave my evasion
    .draugr
    
    mergelib "libtcg.x64.zip"
    export

draugr.x64:
    # merge the call stack spoofing
    load "bin/spoof.x64.o"
        merge

    # merge the asm stub
    load "bin/draugr.x64.bin"
        linkfunc "draugr_stub"

    # merge the new wrapper
    load "bin/draugr_wrapper.x64.o"
        merge

    # redirect call sites
    redirect "gate_wrapper" "draugr_gate_wrapper"

Here, you’ll see that Daniel redirects gate_wrapper to call draugr_gate instead. This same .spec file could accommodate a module to intercept Sleep, bring in the LR-BOF logic, and pass everything on to the next gate in the chain. Something like this (untested/mockup) code:

void lrbof_gate_wrapper ( PFUNCTION_CALL function_call ) {
	if (function_call->functionPtr == Sleep) {
		/* do LR_Sleep logic */
	}
	else {
		gate_wrapper(function_call);
	}
}

And:

x64:
    load "bin/sleepmask.x64.o"
        make coff +optimize

	# weave in our alternate Sleep
	.lrbof
    
	# weave my evasion
	.draugr
    
	mergelib "libtcg.x64.zip"
	export

lrbof.x64:
	load "bin/lrbof_sleep.x64"
		merge

	redirect "gate_wrapper" "lrbof_gate_wrapper"

draugr.x64:
	# ...

The above requires some explanation. redirect “gate_wrapper” “X” takes over calls to the gate_wrapper function and directs it to X instead. But, what’s really cool, is we can chain these. So, by redirecting gate_wrapper to lrbof_gate_wrapper first, our LR_BOF sleep gets first dibs on this function. But, gate_wrapper inside of lrbof_gate_wrapper is left untouched. It calls the original gate_wrapper. If we opt to include the draugr evasion, its redirect of gate_wrapper will take over the gate_wrapper call in lrbof_gate_wrapper.

In this way, we’re achieving what Crystal Palace is all about: composable tradecraft.

Closing Thoughts

In this post, I walked you through Marcos’ design of Asynchronous PICOs.

Following the Asynchronous PICOs design, we implemented Long-running BOFs. One of the high-level differences is that Long-running BOFs are a component contract, where Asynchronous PICOs are a source code framework.

In implementing a component contract, I showed you how the component itself is kept separate from the implementation details. I’ve also shown you strategies to attach an implementation to the contract, by time-of-use merge with Crystal Palace. And, we discussed other possible spins on these same components. The idea being, we’re not tied to one specific implementation or use of these programs.

I then discussed how LR-BOFs could work with Cobalt Strike, following the same path as Asynchronous PICOs. Specifically, I showed how by working with something like Crystal Mask, it’s possible to merge the wake-on-sleep logic with existing tradecraft rather than needing to port code to a template to support this concept.

I’ll close with one final take-away. Crystal Palace isn’t magic. It doesn’t out of the box create the flexibility and mix/match re-use we’re after. It needs designed base components that we can compose tradecraft onto. Figuring this out is a step we’re taking together. And, furthering that discussion is why I wrote this post.

I’d like to thank Marcos Gonzalez Hermida for the work on Asynchronous PICOs. It’s a meaty system and provided a good opportunity to discuss these ideas in context.

Relax and unwind in the Tradecraft Garden

We’re at the 12th release of Crystal Palace and marking one year in the Tradecraft Garden.

This release adds reference relaxation to make global references PIC-friendly. I’ve also added stack unwinding generation for Crystal Palace outputs too. And, I’ve got some thoughts on an alternative to PE shellcode runners.

Before we go further, I want to say some thanks to some folks:

First, I want to thank Daniel Duggan for building on, championing, and bringing so many ideas to this effort.

I want to thank wtfsck and the other maintainers of the Iced library. Iced is the instruction decoder, disassembler, and assembler foundation Crystal Palace builds on.

I also want thank Will Burgess, Cobalt Strike’s R&D lead, for his advocacy and experimentation. Will’s early talk reached Callum Murphy-Hale, who came in for the combined red/blue/purple unit testing aspect. pard0p followed after, bringing reusable pieces for C2 agents. Since then, we’ve had the benefit of Bingus, Blacksnufkin, CodeX, c0rnbread, KuwaitiSt, Lorenzo Meacci, Maor Sabag, 0xPrimo, SAERXCIT, Sandro Ackerman, WulStack, and others experimenting and publishing under this model too.

I want to thank the students/graduates from Daniel’s CRTO II / CRTL course. What impresses the hell out of me, is the way so many of you take the threads and hints in this project—some very incomplete speculations—and you’ve run hard with them.

In the last year, we’ve had 40 blog posts. 13 of those are mine. 16 of those are Daniel. And, 11 are from other researchers. I’m aware of ~30 projects and POCs that use Crystal Palace in some way.

Thank you for building with me.

A +relax’d PIC development experience

One of the PIC annoyances this release helps with are global variables. If you’ve ever tried to access functions or data by reference, across modules, you’ve likely seen a message like this:

[-] .rdata has relocation for address of symbol 'go'. I can't resolve this from PIC.

At compile time, GCC doesn’t know if the global reference is near your object code or living in some far part of memory. So, it does something very conservative. It creates a slot in .rdata (.refptr.X) to store a full pointer to the item and it expects the loader to populate this slot with that pointer before your program runs. In PIC, we don’t have a loader, and we don’t know that pointer at link time—so we’re screwed.

It turns out, that there’s a common silent linker optimization to get rid of these .refptrs. It’s called reference relaxation. Relaxation gets rid of nearby .refptrs by flipping one byte to change MOV reg64, [.refptr.X] to LEA reg64, [X]. No bin2bin needed.

Crystal Palace now [opt-in] relaxes references too. Use:

make pic +relax.

Stack Unwinding Data

Win32 x64 exception handling and stack unwinding are table-based. That is, Windows doesn’t magically know where stack frames begin and end. Instead, it relies on meta-information registered with the operating system to unwind frames.

Crystal Palace now has tools to generate unwind data for its outputs. Normally, this data comes from your compiler—but Crystal Palace’s bin2bin makes that information stale.

Unwind data is a puzzle piece for reducing use of and finding alternatives to call stack spoofing.

I’ve added some projects to the Garden that use this feature:

Simple (Unwinding) Loader generates unwind information for our PIC loader and our target PICO. It registers both using RtlAddFunctionTable. This program shows how to generate and use the data.

The Module Stomping Loader is more involved. This loader loads a dummy module, lays a PICO over it, and relocates the generated unwind table to the .pdata section. This program shows another way to use unwinding data and documents caveats about operationalizing this data.

For more on unwind data, check out klezVirus’ Fantastic unwind information and where to find them.

Note: If you’re going to use this feature with PIC, I recommend you compile with -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer. Crystal Palace’s unwind generator expects fixed stack frames or else it requires a frame pointer. Crystal Palace’s PIC ergonomic tools (e.g., dfr, fixbss) will make modified functions dynamic. If you forget these flags, no big deal. Crystal Palace will tell you if it can’t generate unwind data for a function.

COFF Mixing

For National CCDC 2026, I built two 4KB PIC agents. My operating tradecraft needed various PE flavors to use for persistence, privilege escalation, and lateral movement. I thought I would re-use a shellcode runner, but I realized: my agents are written to PICO conventions. Maybe I could export my agents as COFF and just… y’know… link them to something.

This led to COFF Mixing which is a thought exercise to obscure a capability in a PE without packing or memory injection.

I call this COFF mixing, because my original interest was to mix my agents with known good code using Crystal Palace’s +disco (function order randomization).

I had to implement some changes to make everything work: Dynamic function resolution is now allowed with COFF output. I’ve added a strip (symbols) command. And, stack unwinding generation works here too.

Wearing a red hat, I see hiding statically linked capability as a worth exploring alternative to unpack+inject shellcode runners. This puts more pressure on the content and size, but loses some of the behavior baggage.

Wearing a purple hat, these features give a means to observe a capability + tradecraft with and without memory injection. This is helpful to avoid overfitting detections to one deployment modality.

Migration Notes

tcg.h now defines _RESOURCE. If any of your projects built on old examples, you may have a duplicate _RESOURCE definition to remove.

Closing Thoughts

The big theme of this release, in both COFF mixing and using unwind data, is to look at the security implications of complying with the operating system rather than trying to spoof and fight everything. Or, to quote the Sizeable Bingus: Stop Being Weird.

I made a lot of back-end changes (beyond +relax) to simply make this tech better in day-to-day use. This is an ongoing activity.

A hidden gem in this release is verify.spec in the Simple (Unwinding) Loader. This is a configuration .spec that’s separate of the main project. When included, it uses ised to insert a debug interrupt (int3) into the capability, redirect to hook into the program flow, and it registers a VEH to print a stack walk when the debug interrupt is fired. All of this is running in a PIC context. This system is bananas!

A year ago, the focus of this project was tradecraft packaged into DLL loaders for DLL capability. A year later, the focus remains tradecraft and capability separation, but what’s possible is different. We now have a JSON-over-HTTP accessible time-of-use linker to merge components, wire them up with hooking-like primitives, and spit out COFF, PIC, and loader-ready PICOs. There’s a lot to explore with dividing tradecraft into defined mix/match pieces and building capability under this model. Looking into these problems is where I hope we go next.

Enjoy the release.

To see what’s new, check out the release notes.

Modules and Monoliths

Last week, memN0ps published DoublePulsar: A User-defined Reflective Loader in the Crystal Palace and Tradecraft Garden Era. It’s a lengthy blog post, about 50 printed pages. And, most of those are devoted to memN0ps’ fantastic deep-dive into evasion mechanics and conquering the challenges of building a monolithic UDRL using Rust’s nightly toolchain.

memN0ps devoted significant space to contrasting monolithic UDRL development to my Crystal Palace and Daniel Duggan’s Crystal Kit. The post spends time on Crystal Kit’s deviations from evasive loader best practice. And, memN0ps celebrates bespoke loader development as a higher-skill, more fundamentals-informed path. The post’s assertions are turn-key operations focused, imply a lack of agency for folks who don’t build monoliths, and don’t represent Tradecraft Garden as I see it.

Daniel didn’t release Crystal Kit as an exemplar trend-conforming evasion cocktail. It started as an exploration of Crystal Palace’s C2 agnostic claim, by adding evasion to Cobalt Strike without Cobalt Strike’s specific interfaces. It’s also an open source modular foundation for students in Daniel’s CRTL course. Its default state provides room to apply specific techniques to push back on specific observables. Relevant: a Crystal Kit fork for the Xenon Mythic agent exists too.

Tradecraft Garden’s mission is to separate tradecraft and capability. I solved time-of-use composition with a linker because linkers compose programs. Going further, I’m breaking the post-exploitation tradecraft chain into discrete components. I’m doing this to encourage folks to conduct new research, write code, and publish defense guidance or document truth on those stand-alone components.

LibTCG and loaders like Crystal Kit are part of an effort to find best practice dividing lines between post-exploitation tradecraft components. For example, LibTCG uses a direct PEB walk. But, let’s say someone has a module lookup technique that does something else: Crystal Palace’s services module pattern can bring that technique into a loader or capability without modification. In this scheme, I didn’t take away PEB walking control from a researcher. I’ve componentized it and made it something that can get swapped out. In doing this, I’m creating space for deep study and experimentation on this one piece of the puzzle. This specialization is how things move forward.

The synergy of this model is showing up. Last year, Daniel Duggan published LibTP for proxying NT API calls via the Threadpool. SAERXCIT published a blog post on how to evade callstack signatures with call gadgets. And, to prove it out SAERXCIT published a LibTP-compatible Crystal Palace shared library to drop-in demonstrate their primitive. None of this demonstration involved weaponization with a C2.

What I see is the red teaming “advanced tradecraft” (Win32) meta is nearly a checklist right now. By breaking down the post-exploitation tradecraft problem set, I’m hoping more ideas and approaches emerge. Where my project is courting red teaming-aligned researchers (and it is, leverage is the candy), it’s an effort to bring their research energy into this ground truth model.

This ground truth model is not about outputs to deliberately “best” EDR X today. I strongly believe that research isn’t for turn-key circumvention of specific security products. It’s higher impact to seek and explain possibilities, blindspots, and areas where whole classes of security technology are weak (e.g., categorical techniques are in scope). Tradecraft Garden’s model encourages componentization, de-escalated release, and favors impact demonstration through system events and properties if possible. Where security exercise use is desired—this use-agnostic model empowers, but also expects, the red team to choose, adapt, and validate before they deploy.

My goal is to platform systems security research, separate from the edit and recompile evasion treadmill, and create an outcome where red and blue efforts may benefit from these fundamental outputs at the same time.

I want to thank memN0ps for publishing their Rust loader and sharing their process with us. As a fellow researcher, passionate about the craft, I appreciate what a gift of labor and creativity their work is. Further, I appreciate their blog post as a mirror of some project messaging gaps I needed to address.

Small PIC Energy

I have a challenge for you:

How much beaconing agent functionality can you fit into 4KB PIC? How do you do it? This isn’t a shellcode golf challenge. It’s about elegant ways to build common agent stuff in C. I was able to get a WinINet loop, a light command kernel, and a BOF runner in mine. To do this, I relied on BOF inversions (discussed in this post). I did fudge a bit and rely on GCC’s (discouraged) -Os to shrink some modules. I didn’t want to though! One caution: if you start on this problem, you might get obsessed, and stop doing other work. That’s what happened to me. You’re warned.

The above is a form of “small PIC” energy. And, with this Tradecraft Garden and Crystal Palace release, I’m hoping to invite this energy into your projects.

The rest of this post is more thought exercise in nature. But, up front, here are the new features:

  • It’s now OK to merge a shared library into a project multiple times. Crystal Palace will ignore follow-on merges.
  • We now have a Simple BOF runner in the Tradecraft Garden.
  • And Crystal Palace is now accessible in a language agnostic way thanks to a new JSON-over-HTTP sidecar service

Modular PIC Agents, Redux

I’m seeing some experimentation with Crystal Palace for both capability development (e.g., PIC C2 agents) and as a composition kernel in some projects too. If I were baking Crystal Palace into a C2, here’s what I would do:

(1) Separate the concerns
I would separate the capability (e.g., a C2 agent) assembly and tradecraft combination as concerns

(2) Assemble the capability
To assemble the capability, I would use a .spec file to merge COFFs, patch in configuration, and (possibly) dynamically merge user-selected features [or variant implementations] in the capability.

This capability .spec would:

  • output a COFF (make coff)
  • merge any libraries the capability depends on

This capability.spec would NOT:

  • use +optimize, +mutate, ised, or other code transformation things
  • use linkfunc (the bin2bin may error out re-processing PIC in (3))
  • use dfr, fixbss, fixptrs, etc. — these are time-of-use tradecraft choices

(3) Pair Tradecraft with the Capability
To pair tradecraft with a capability, let the user choose a tradecraft .spec file. The .spec accepts a COFF, decides how to turn it into PIC (e.g., apply a PIC services module or pair it with a PICO loader), brings in any runtime tradecraft, and outputs shellcode. This is also where +optimize, +mutate, ised, and other stuff should happen.

Take the above as a thought exercise rather than a prescriptive. My thought is that this is a logical way to separate concerns, delegate tradecraft to a single .spec (which may orchestrate multiple components), and keep the integration and UX simple.

BOF Inversions

One of the ideas I’m excited about is using Crystal Palace to apply tradecraft to Beacon Object Files before they’re passed to a C2 agent. Daniel Duggan’s BOF Cocktails shares the details.

I love this, because it feeds the use-case agnostic capability and tradecraft separation this project is after.

Pairing research tradecraft directly to BOFs skips C2 entirely for the development, test, and demonstration of that tradecraft. This opens up room for detection science that treats naked BOFs as a control and tradecraft-paired BOFs as variables. This separation frees research to thrive alongside but culturally separate from operations.

C2 operators benefit too, as it means their BOFs are self-protecting and not dependent on the agent or C2 to bring evasion. And, for C2 engineers—this delegates another piece of your problem set to the BOFs themselves.

The Chicken and Egg Problem
This ideal has a chicken and egg problem though. It makes little sense to integrate Crystal Palace directly or create hooks to intercept and edit BOFs, if there’s no eco-system of BOF cocktails. And, there’s not much incentive to write BOF cocktails, if there’s nowhere to use them yet.

The Solution: BOF Inversions
I have a value proposition for C2 engineers that opens up the door for BOF cocktails and makes it much easier to support BOFs with minimal weight. I call it BOF inversions, as in—we’re inverting the implementation responsibility for BOFs and their API from the agent and into the linker and the BOF itself.

Here’s the idea:

  1. A hypothetical C2 passes every BOF through a user-specified Crystal Palace .spec file
  2. The .spec file turns the BOF into a PICO (via make object).
  3. The .spec file also merges (most of) a BOF API implementation into the BOF.
  4. The agent receives the PICO and uses a simple PICO runner to execute it.

To support the above, the agent needs a PICO runner function and an implementation of any BOF APIs that must live in the agent. In Tradecraft Garden’s Simple BOF runner, I just needed BeaconOutput and that was it.

The fun of this scheme is that the agent no longer has to implement the Beacon API. These functions live inside of the BOF itself. And, thanks to +optimize, any APIs that aren’t needed are removed before the PICO is shipped to the agent.

Now, let the user configure or edit the .spec responsible for turning BOFs into PICOs and you have a natural integration point for BOF cocktails too.

Linker Sidecar Service and API

Using Crystal Palace as a capability composition kernel or implementing BOF inversions requires low latency time-of-use access to Crystal Palace. Prior to this release, that was only possible via the Java API. To open this project up to other language stacks, I’ve added a Linker Sidecar server to Crystal Palace. The new linkserve command starts this process.

The Linker Sidecar is a localhost-only JSON-over-HTTP server to use Crystal Palace.

The API follows the CLI’s concepts closely. The server accepts a JSON object with parameters and gives back a JSON object with base64 encoded output, yara rules, and messages from the linker. It’s a stateless one transaction POST request.

Here’s a Python script to act like ./link:

import sys
import base64
import requests
from pathlib import Path

# Handle our arguments

if len(sys.argv) != 4:
	print("Usage: python client.py <spec_file> <file.dll|.o> <output_file>")
	sys.exit(1)

spec_file  = str( Path(sys.argv[1]).resolve() )
capab_file = str( Path(sys.argv[2]).resolve() )
out_file   = sys.argv[3]

url = "http://127.0.0.1:60060/link"

# Populate our arguments

linkargs = {
	"action": "link",
	"params": {
		"spec": spec_file,
		"file": capab_file
	}
}

# Make a request to the Sidecar

try:
	response = requests.post(url, json=linkargs)
	response.raise_for_status()

	data = response.json()

	if data.get("success") is True:
		print("[*] Success")

		if data.get("message") != "":
			print(data.get("message"))

		raw_binary = base64.b64decode(data["output_b64"])
		with open(out_file, "wb") as f:
			f.write(raw_binary)

	else:
		print(f"[-] Failure {data.get('context')}:\n{data.get('message')}")

except requests.exceptions.RequestException as e:
	print(f"[-] HTTP Connection Error: {e}")

The Linker Sidecar documentation has the API details and example JSON for you. I put special care into the error messages, to make sure you’re not pulling your hair out if there’s a bad or missing parameter. I’m looking forward to seeing what you do with this.

Migration Notes

None

Closing Thoughts

I want to close by sharing how our small PIC is a response to another “Small PIC energy”. These thoughts get to the heart of how I see code and individual behavior as our agency to influence culture.

One of the occupational hazards of offensive security is ego. When I say ego, I’m not referring to a “my work speaks for itself” pride. But, instead, a missing something that drives some in the field to disparage other efforts, play word games to erase others’ contributions, and in the worst cases—drive chat and private conference whisper campaigns to stigmatize peers until their very name is irresistible safe red meat.

All of these things come from a zero-sum approach to participation in the profession. That is, if anyone who threatens their self-image continues to exist or worse–gets acknowledgement, it’s a game they’ve somehow lost. Commercial product interests have made these tendencies much worse. These insecure and ego-driven behaviors are “Small PIC energy”.

This passive-aggressive cruelty can look like bold thought leadership because it’s loud and because community leaders and established peers pretend it’s something else. This looks like permission, but it’s discomfort and fear to act when their “friends” throw the punches. In these situations, tribal cliques form that mix uncomfortable participants, eager followers, partners who sell the tribe’s virtue, and loud leaders that bully “others”. To outsiders, it looks like consensus. To the collective profession, this unchecked dynamic is contagious, divisive, and destructive.

There’s another way. I believe code can shape communities. The secret is to see the ready potential in others. Genuinely want their success. And, work to create space and opportunity for that success. This requires a belief that our success comes with theirs. In this playbook, the collective improvement of our peers and ourselves is the product. Not the technology. I was am proud to see people build on things I put out there and grateful to learn from them as they do. I remain awed that our niche profession drove the industry’s real technical conversation in a way no one else could. Tradecraft Garden is an effort to revive this in a ‘now’ considered way. small PIC > “Small PIC energy”.

To see what’s new, check out the release notes.

A scalpel, a hammer, and a foot gun

Last month, I released a Yara signature generator for Crystal Palace. AKA, an invariant content observation tool. I then used the feature to document the physics of various content-signature parameters (e.g., length, signature agreement, etc.) Now, I’m releasing the follow-on features to give you tools to play with the program content assumptions that signatures act on.

ised – A program rewriting tool

The main feature in this release is the ised command. ised is a tool to insert and replace code in a program at locations matching instruction patterns. This feature can surgically break an attractive signature target. You can also spam junk code everywhere and bloat your program too. And, while this feature is very powerful—it’s also a footgun. But, Crystal Palace has some checks to help with that too.

The syntax is:

ised [verb] [pattern] [$CODE] [+options]

Valid verbs are ‘insert’ and ‘replace’.

Pattern is one or more strings that describe individual instructions.

$CODE is a byte[] of object code.

+options select the instruction verb applies to and a few other things.

$CODE

$CODE is a byte array of valid object code. This is the insert or replace value used by ised. You are responsible for preserving the semantics of the program and not generating harmful side effects. Crystal Palace does not check this value for errors. One way to populate $CODE is with pack:

pack $NOP "b" 0x90

Patterns

Patterns describe which parts of the program ised affects. ised patterns come from Crystal Palace’s disassembler output.

There are three pattern forms:

The specific form is a string that matches the Crystal Palace disassembler output for an instruction. Below is a command to match any instruction that disassembles to call findModuleByHash and insert the contents of $NOP after it.

ised insert "call findModuleByHash" $NOP

The generic form is a string that matches Iced’s string representation of an instruction mnemonic and the types of argument it accepts. Use ./disassemble -f (or disassemble +forms in a .spec) to see an instruction’s generic form alongside the disassembled output. This command finds any call to a local function and inserts $NOP after it:

ised insert "CALL rel32" $NOP

Finally, if you want to get aggressive, Crystal Palace can match the instruction mnemonic from the generic form too. This inserts $NOP after any push instruction:

ised insert "PUSH" $NOP

Each pattern describes one instruction. Multiple patterns, specified together, describe a sequence of instructions. This command inserts $NOP after a mov that immediately follows a local call instruction.

ised insert "CALL rel32" "MOV" $NOP

Crystal Palace’s pattern matching engine depends on specific string matches. If you specify “sub rsp, 32h” as a pattern—don’t expect a match. Crystal Palace does not parse and normalize your pattern strings. It expects that your pattern is “sub rsp, 0x20” as output by the disassembler.

ised commands are evaluated before +regdance and after +mutate. If you’re looking for instructions to target, I recommend you disable +regdance during that process.

Verb: insert

The insert verb inserts the contents of $CODE into a program somewhere relative to the pattern. The default is to insert $CODE after the last instruction in a matched pattern.

Before I can describe the other options, I need to cover ised’s two-pass implementation.

From ised’s view, each instructions has three buckets associated with it. There’s append, prepend, and replace. During ised’s first pass, it walks the program and finds all commands that match a sequence of instructions in the program. When there’s a match, the affected instruction and bucket are found, and the command’s $CODE is dumped into that bucket.

On the second pass, ised walks each instructions and acts on one random value from its buckets. If a prepend bucket has four $CODE options, ised will pick one value to prepend to the instruction. If a bucket is empty, ised does nothing. In this design, multiple ised commands that match the same thing is how you get polymorphism.

The +first, +last, +before, and +after options dictate which instruction (relative to the matched pattern) and which bucket the candidate $CODE goes into.

ised insert "A" "B" "C" $CODE +last +after

+last and +after are the default. They place $CODE into the append (+after) bucket of the last matched instruction (“C”) in this sequence.

ised insert "A" "B" "C" $CODE +last +before

Here, $CODE is placed into the prepend (+before) bucket of the last matched instruction (“C”) in this sequence. So, $CODE would show up between matched instructions “B “ and “C” in the final program.

ised insert "A" "B" "C" $CODE +first +after

Here, $CODE is placed into the append (+after) bucket of the first matched instruction (“A”) in this sequence. So, $CODE would show up between matched instructions “A” and “B” in the final program.

ised insert "A" "B" "C" $CODE +first +before

And, here $CODE is placed into the prepend (+before) bucket of the first matched instruction (“A”). $CODE would show up just before the sequence “A” “B” “C” in the final program.

insert can act and match on most instructions in most situations. There are some checks to discard dangerous changes (e.g., protecting the integrity of fixptrs, rflags/eflags preservation, etc.).

Verb: replace

The replace verb replaces an instruction in the matched sequence with $CODE.

The options here are simpler. +first replaces the first instruction in the matched sequence with $CODE. +last (default) replaces the last instruction in the matched sequence with $CODE.

You’re responsible for making sure $CODE replicates the semantics and side effects of the instruction you’re replacing. If you target add rax, 0x20 with replace, your $CODE better get rax + 0x20 into rax by the end of its run.

There are also instructions you can’t replace. Crystal Palace won’t replace a call, branch, or an instruction with an associated relocation. No error is fired for these. They’re just passed-over if a replace targets them.

Option: +safe

One danger of program modification is the risk of RFLAGS/EFLAGS corruption. The flags register tracks meta-information about the last arithmetic operation and its contents affect follow-on branch instructions. Crystal Palace goes to some length to track flag readers, flag writers, and instructions that fall between these two.

If Crystal Palace determines a target instruction is within a “danger zone”—a run of instructions where a flag is set and later used—Crystal Palace will ignore most ised changes. This is a anti-footgun safety feature. If your $CODE is flags aware (or free of side effects to flags), add +safe to your ised command to override this check.

Option: +split

The +split option applies to both insert and replace. This option inserts a 0-byte jump before (+before) or after $CODE. This dynamically splits a block of code at that specific instruction boundary. This pairs with Crystal Palace’s block randomization features which shuffle blocks function wide (+blockparty) or program wide (+shatter).

If you just want to split at an instruction boundary, without any $CODE, use $NULL. The $NULL value is a (new!) Crystal Palace built-in which is always an empty byte array.

Constant Blinding

While ised has the power to target single instructions well, it’s cumbersome for getting rid of classes of constants (ror13 hashes, stack strings, etc.) used throughout the program. I’ve evolved +mutate to help with this.

+mutate now locks onto key instruction forms and subtracts a 32-bit magic value from their constant. This scatters the constant’s bit pattern. +mutate then inserts an add to restore the constant at runtime. Example:

mov rax, constant

Becomes:

mov rax, constant – magic
add rax, magic

By default, +mutate uses a random magic value for each instruction. Use magic to limit the magic constant to a pool of values you specify:

magic "0x7FFFFFFF, 0x12345678, 0xD34DB33F"

Limiting magic to a few constants makes the add instruction into ised-targetable scaffolding. Use ised and magic together to bring variety to these sequences of instructions.

Migration Notes

(1) Update any use of pack with the template char ‘b’ to ‘v’. ‘b’ was taken over for a single byte and v now means expand a $variable in the packed string.

(2) The ./disassemble CLI changed slightly. You must now prefix +options with -o (e.g., -o +optimize,+regdance,+mutate)

Closing Thoughts

Early in this project, I shared that content signature resilience was one of my goals for Crystal Palace. I didn’t know what that would look like and I didn’t expect the journey to take us here. But, here we are.

First, there’s a belief that an obfuscator is an obfuscator is an obfuscator. Obfuscating a program is just a step that happens and it’s a solved problem. I want to challenge you to imagine that obfuscators may have gaps in what they obfuscate. Or, the obfuscator may produce sequences of instructions that are a new tell. As I thought about +options for Crystal Palace, these were the problems I wrestled with.

The other thing I want to challenge is the notion that detection surface is program content that’s highly unique and specific to the program. In the last development cycle, I sat down with a goodware corpus, and I explored the left and right bounds of content false positives. My conclusion was that a good signature is three or more instructions with a sane minimum number of bytes. A highly program-specific Yara rule is one where six or more of these signatures agree. Any untouched invariance in a program is a place to pull signatures from.

So, when I sat with the content signature resilience problem set, I had a sense that a baked in obfuscation cocktail wasn’t the way. And, I had a sense that I wanted to come up with something that gives you agency over your program’s final content. But, I didn’t know what that would look like. Would I write an API to extend Crystal Palace’s BTF? Would I create hooks for a Python (or Sleep 🙂 always Sleep) script to transform instructions? ised is what I came up with. I’m pretty happy with it. I’m especially curious to see what you do with it.

The above said, there’s another implication for this technology. Within one foundation, we have a tool to generate high-fidelity Yara rules. And, within that same foundation, we have high-leverage tools to break content signatures. A potential outcome is that researchers building tools on this platform may feel quite comfortable releasing Yara rules for all of their capability. It’s no loss, because they and their users would likely have a private ised-cocktail ready to go. What would change in red teaming (or cybersecurity even), if there was no fear of “burning a tool” because of its content tells and behavior was the only meaningful battleground?

The Adversary Fan Fiction Writers Guild is a culture engineering project. One of the ways I’m trying to change culture is by changing the incentives that have made things the way they are. The combination of the Yara generator and ised are a tangible example of this.

To see what’s new, check out the release notes.

The Islands of Invariance

Crystal Palace now has a Yara rule generator. In this blog post, I’ll walk you through the design and evaluation of this feature.

rule PageStream_rDLL_03495de1 {
   meta:
      description = "PageStream rDLL: Use VEHs and guard pages to limit DLL visibility in eXecutable memory"
      author = "Raphael Mudge"
      date = "2026-01-27"
      reference = "https://tradecraftgarden.org/pagestream.html"
      arch_context = "x64"
      scan_context = "file, memory"
      os = "windows"
      license = "BSD"
      generator = "Crystal Palace"
   strings:
      // ----------------------------------------
      // Function: TrackPage
      // ----------------------------------------
      /*
       * 48 69 D2 56 55 55 55          imul rdx, 0x55555556
       * 48 C1 EA 20                   shr rdx, 0x20
       * 89 C1                         mov ecx, eax
       * C1 F9 1F                      sar ecx, 0x1F
       * (Score: 530)
       */
      $r0_TrackPage = { 48 69 D2 56 55 55 55 48 C1 EA 20 89 C1 C1 F9 1F }

      // ----------------------------------------
      // Function: go
      // ----------------------------------------
      /*
       * 48 89 D5                      mov rbp, rdx
       * E8 87 01 00 00                call SizeOfDLL
       * 48 8D 15 20 00 00 00          lea rdx, [.bss+0x20]
       * 48 8D 8A 00 02 00 00          lea rcx, [rdx+0x200]
       * (Score: 142)
       */
      $r1_go = { 48 89 D5 E8 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 8A 00 02 00 00 }

   condition:
      all of them
}

Add -g "outfile.yar" to generate Yara rules alongside ./link and ./piclink’s output. There’s a Java API for this too.

The .spec file rule command gives advice to the Yara generation. This command is optional:

rule "name" [max] [minAgree] [minLen-maxLen] ["funcA, …"]

A rule is a collection of signatures associated with part of a Crystal Palace project (e.g., the main PIC, an embedded PICO, etc.). “name” sets the rule’s name. If name is empty (e.g., “”), the rule generator will derive a name.

max sets how many signatures Crystal Palace allows within a rule. Crystal Palace scores candidate signatures and selects the best ones. This score favors instructions that are information dense (e.g., have a lot of parameters) and it likes unique-seeming constants. The default is 10.

Set max to 0 to disable signature generation for a piece of a project.

minAgree is the number of signatures that must agree for a rule to match a sample. If the number of signatures generated is less than minAgree, Crystal Palace will require all of the signatures to fire. This value is the power tool to reduce false positives.

minLen-maxLen sets the minimum and maximum non-wildcarded bytes in a signature. The default range is 10-16.

The last parameter is a list of functions to generate signatures from. By default, Crystal Palace considers all functions in scope. Use this option to specify functions most-specific to your tradecraft implementation.

The rule generator is scoped to object code (e.g., the .text section) of a program only. It does not generate signatures from .rdata constants (strings) and it does not look at appended shellcode (e.g., linkfunc).

Generating Signatures

The heart of the rule generator is Crystal Palace’s binary transformation framework (BTF). This framework is a pipeline to disassemble a program, lift its contents to a higher-level abstraction, transform the program, and lower the program back to working object code.

I’ve implemented some code randomization features on top of the BTF. +shatter splits a program into basic blocks and randomizes their order program wide. +regdance does some register randomization. One of the benefits of doing this bin2bin is I know which of the instructions changed and which didn’t. Runs of unchanged instructions are our islands of invariance. The rule generator focuses on these runs of unchanged instructions.

The first signature generation step is to identify basic blocks in a program. Basic blocks are runs of instructions with one entry point and one exit point. Crystal Palace uses the leaders algorithm to identify basic blocks. Given that +shatter and +blockparty randomize the order of these blocks, it makes sense that signatures should not overlap blocks.

One difference from normal block splitting: the signature generator uses modified instructions as a block splitting “leader” criteria. This is how the algorithm isolates the islands of invariance.

One limitation in the BTF pipeline is it only knows which instructions changed during the current pass through the pipeline. By itself, this is valuable, as it captures shifting branch targets, RIP-relative references, and call instructions. But, it doesn’t help with the stuff from other passes (e.g., PIC ergonomics transforms, +regdance, etc.). To make all of this work, I’ve made the earlier pass transforms deterministic. They’ll always generate the same output for the same input. And, later passes (e.g., +regdance, if enabled) are dry-run to taint instructions as changed—for the purpose of rule generation.

The signature generator preserves call instructions within an island. I see these as important “something is happening” context anchors. This implementation wildcards the call target and wildcards are excluded from the minimum/maximum signature-length criteria. Relocations, by themselves, are not treated as a change that breaks an island. The relocation values are wildcarded too.

Filtering Signatures

At this point, we have several islands. Each individual island is treated as a signature.  The signature generator’s next step is to filter the list to get rid of unacceptable signatures.

The filter gets rid of instructions that don’t belong in a signature. This includes function prologue register preservation and stack setup. And, function epilogues that undo all of that. I also get rid of INT3 and NOP instructions that pad some functions. These are boilerplate to every program and too false positive prone. The algorithm also gets rid of instructions that are marked as “changed”.

One of this system’s hard-baked criteria is that, regardless of byte length, a signature must contain three or more instructions. Two instructions are too false positive prone. Three is where things start to get unique.

The algorithm also takes care to reject duplicate signatures too.

The filtering process is where signatures below the minimum byte length are removed. Signatures over the maximum byte length are handled differently. The algorithm walks these islands to find the run of instructions that fits within the maximum size and has the highest heuristic score.

The algorithm makes no attempt to derive 2+ signatures from a single large island. I feel the diversity of signatures from different localities, across the program, outweigh having 2+ signatures tied to one island.

Scoring Signatures

The final step is to score signatures and use this score to select signatures that are likely unique to the program. Crystal Palace doesn’t ship with a “good” opcode database or anything like that. We have to rely on a heuristic guess. This project uses information density within each instruction as a proxy for uniqueness.

To calculate information density, I walk each instruction’s operands, I score the individual operands, and I multiply the scores together. The idea is for an individual instruction’s score to grow geometrically with more information. RBP/RSP and constants 0, 1, and -1 are scored the lowest. I score other constants much higher. The heuristic rewards large constant values over small ones.

The effect of the above is that scores bias for instructions that have constants and they bias for instructions with a lot of complexity (e.g., instructions with a base register, displacement, index register, and scale value that’s not 1).

I do artificially bias a couple of situations:

Call instructions get a boost. I do this to make sure where there’s a choice to select call over another low density instruction, the call is chosen. This is because the call has symbol information and that’s helpful context in the signature’s comments.

I also artificially score instructions that use RBP or RSP as their base register. I assume these instructions are register spills or other stack book keeping and do not reward their complexity.

And, after evaluation and investigation of the score heuristic, I reduced the score of RAX relative to other registers. This change is discussed at the end of the false positives section.

I calculate the score for an island by adding the scores of individual instructions together.

Performant Rules

As part of this work, I looked at Yara best practices for performant rules.

Yara’s signature matching is a two phase approach. The first phase is a triage phase. Here, Yara selects a high-entropy 4-byte value (called an atom) from each signature and feeds them as input into the Aho-Corasick algorithm. Yara’s heuristic treats entropy as a proxy for uniqueness.

Signatures that match during triage are fully evaluated during the second phase. This is where the wildcards, jumps, and regular expressions are handled.

Prior to this background research, I assumed any two-byte value could anchor a Yara signature (e.g., a jump and a wildcard). I didn’t know the system’s expectations. Now, if I were trying to break a known signature, I would focus on isolating or changing the candidate 4-byte atom values. Wildcards and regular expressions without a suitable atom are not a performant signature.

During this work, I experimented with bringing Yara’s atom quality heuristic to Crystal Palace’s signature selection process. My thought was to have an option to specify a atom entropy floor as a performance aiding option. But, I found that candidate signatures, pulled from object code, with the three instruction floor enforced, almost universally contained a max score or near max score atom. Any work to select rules with these criteria wouldn’t have an effect.

False Negatives (aka Scope Disclaimers)

The rule generator creates signatures from the instructions that survived Crystal Palace’s binary transformation framework. Signatures generated against an input COFF and .spec should match 100% against the output of that linking process.

The goal of these rules is to zero-in on the specific implementation and configuration. They are not content tells for the technique itself nor are they expected to survive rewrites, compiler changes, or other obfuscations and modifications to the program.

False Positives

I did take steps to validate Crystal Palace’s signature score heuristic. And, during this process, I had a lot of fun playing with the data and parameters.

For this experiment, I put together a 33GB corpus of goodware EXE and DLL files. I took care to include programs probably compiled with MinGW (e.g., Git, QEMU, Inkscape, msys2, GIMP). The corpus also includes EXE and DLLs taken from my on-hand Windows 7 and Windows 10 VMs.

Standing in for my “tradecraft” I opted to generate signatures from a collection of Beacon Object Files. I used TrustedSec’s CS-Situational Awareness BOFs as my non-blind data set to tweak my heuristic and shake bugs out of the system. I used BOFs from TrustedSec’s CS-Remote-Ops, Alfie Champion’s BOF collection, and REDMED-X’s OperatorsKit as my blind evaluation data. There are ~100 BOFs in this data set.

One important note between these data sets: Crystal Palace supports MinGW compiled programs. MSVC is probably OK sometimes, but not encouraged. The TrustedSec BOFs and Alfie Champion’s BOFs are compiled with MinGW. REDMED-X’s BOFs are compiled with MSVC and make up 40% of the evaluation data set.

> Testing Signatures

To generate signatures I used this Crystal Palace .spec file:

process.x64:
	load %1
		make coff +optimize
		rule "" 10 1 10-16
		export

	pop $TEMP
 
x64:
	foreach %BOFS: .process %_
	push $TEMP

This .spec file walks the comma-separated values in %BOFs and runs the callable label process on them.

To generate my comma-separated list of BOFs, I just used:

find /path/to/BOFs | grep \\.x64 | tr '\n' ','

And, to pass this information to Crystal Palace:

./piclink detect.spec x64 out.bin %BOFS="..." -g "rules.yar"

I used yara-x to bounce the generated signatures against the goodware corpus:

./yr scan -r -s rules.yar goodware

> Is the score heuristic better than chance?

My first open question was to evaluate whether or not the score heuristic selects signatures that are better than signatures selected by chance. For this experiment, I generated three groups of information:

  • Pick 10 selects up to 10 signatures at random from each BOF’s valid signatures. These signatures are grouped into a single rule for the BOF. This is my control group to compare the Top 10 to. For islands larger than the max length, Pick 10 uses the same score heuristic to find an optimal run of instructions within the island. 
  • Top 10 selects up to 10 signatures scored highest by the rule generator’s instruction information density heuristic.
  • All contains one signature from each island in each BOF. This is the full pool of signatures that Pick 10 and Top 10 draw from. All shows how many false positives lurk within our programs.

For each of these groups, I ran the experiment with different match conditions: Any (of them) means if one signature in any rule matches contents in a file, it’s a match. 2 (of them) means at least two signatures within the same rule must agree to trigger a file match.

My data counts the number of unique goodware files matched. Even if a single file triggers multiple rules or signatures, it is counted as a single false positive file match in this table.

For this experiment, I focused on the 10-16b length signatures. This experiment, and the ones that follow, use the blind evaluation data.

10-16b# SigsAny23456
Pick 109638,31631248910
Pick 109636,389542532070
Pick 109638,3085719334160
Pick 109636,335379561871
Pick 109636,271275451674
Top 109635,538168321890
All3,09910,5071,3913981075433

Here, we see that individual signature vs. individual signature–the score heuristic selects better candidates than random selection. But, in aggregate, the real false-positive reduction hero is signature consensus. Both the random signatures and score-heuristic selected signatures hit 0 false positives when 6-7 signatures are required to agree.

> Does the compiler matter?

The test data set includes 41 MSVC-compiled BOFs from REDMED-X’s OperatorKit. I took those out and reran the test experiment with just the MinGW compiled BOFs.

10-16b# SigsAny23456
Top 10577140
All2,1413,5151347380

Here, we see a drastic drop in false positives with the heuristic selected signatures. Out of a pool of ~2,000 signatures that generate ~3,500 false positive goodware file matches, the score selected ~550 signatures (the top 25%) that yielded only 14 false positives. Said again, the score picked 1 out of 4 candidate signatures, and ended up with <1% of the false positives. More importantly, this test reached zero false positives with a threshold of two signatures.

I separated the compiler here, because Crystal Palace (today) explicitly supports and encourages MinGW as the compiler of choice. These numbers better represent how I expect the generated rules to work with Crystal Palace outputs.

> How do signature lengths affect false positives?

I played around with the length of signatures (in bytes too). I kept the same window size in each of these runs, but slid it by two bytes each time.

Top 10 # SigsAny23456
4-10b92124,3174,2346801546510
6-12b96114,1612,7493961102817
8-14b96011,525293461892
10-16b9635,538168321890
12-18b9541,984311570
14-20b9512,561260
16-22b934782281070
18-24b9291,227210
20-26b9211,3810
22-28b9151,046100
24-30b8973460
26-32b87061590
28-34b8615850
30-36b819120
32-38b810100

In this data: shorter signatures result in more false positives. Longer signatures, usually, result in fewer false positives. But, with a caveat! The data is noisy and a slightly longer signature length can generate more false positives than a shorter one. What I take from this is that length alone isn’t the singular tool to reduce false positives. But, sufficiently long signatures converge to zero false positives at a 2-3 signature agreement threshold.

> How does signature quantity affect false positives?

The last thing I wanted to ask the data is how do the number of signatures affect potential false positives? Here, I stuck with the default 10-16b window, generated top X rules, and recorded their false positives.

10-16b# SigsAny23456
Top 21971,15026
Top 43921,53840190
Top 65862,57844230
Top 87773,17573281780
Top 109635,538168321890
Top 121,1395,84826135261813
Top 141,3015,99426836261813
Top 181,6046,43634847292118
Top 241,9689,428755122563925
All3,09910,5071,3913981075433

> What about the MSVC false positives?

I did not look at MSVC output when I first created the score heuristic. But, I was very curious about what caused the spike in false positives. And, I decided to investigate this further.

I dumped the yara-x output to a file and I sorted the rule matches by count. Here’s what that yielded:

Notice that the false positive matches are not evenly distributed. There’s one dominant super-matcher signature and a few others that stand out. I took a look at $r12_go (note, there were multiple, so I had to narrow it down) and $r9_go. I saw the same story in both:

These instructions spill a pointer onto the stack. They score low in the existing heuristic, because I penalize complex instructions that use RSP/RBP as the base register. But, I wanted to see if I could improve my system knowing this information. I updated the score heuristic to penalize RAX/EAX and I made the RSP/RBP base register penalty stronger. Sadly, this means the test data is no longer blind 😦 Oh well! Here’s the re-run of the score heuristic vs. chance experiment with these changes:

10-16b# SigsAny23456
Pick 109646,908404641550
Pick 109644,875317401740
Pick 109645,147370421750
Pick 109645,875554411720
Pick 109644,696421451520
Top 109642,892862660
All3,0839,6601,5274481265633

I’d like to draw your attention to the Pick 10 and All numbers in this experiment vs. the first. They’re improved. We’ve shaved nearly 1,000 false positives in our signature set. This is the effect of the improved score heuristic selecting an optimal run of instructions within longer islands. But, even with this improvement across the board, the Top 10 signature selection improved enough to converge to 0 false positives with fewer agreeing signatures than Pick 10.

As a sanity check, I removed the MSVC BOFs and re-ran the GCC-only test. I wanted to see if these changes hurt our GCC-only numbers. They didn’t.

10-16b# SigsAny23456
Top 10577220
All2,1244,4291787990

I investigated the false positives from the updated score heuristic. I didn’t see a single outlier super-matcher. But, I did see a cluster of signatures with high-matches. I looked at the top one and saw information dense instructions setting up arguments for the RegOpenKeyExA API. At this point, our top signature is zeroing in on something interesting our program is doing and not compiler book keeping. I call that a win.

Migration Notes

None

Closing Thoughts

I ran these experiments to validate (and improve) the rule generator’s score heuristic. But, I also wanted to document the rule generation parameters (e.g., signature agreement, number of signatures, and length) to show how those affect false positives. In these experiments, signature agreement showed itself as the high-leverage tool to reduce false positives.

I don’t expect these numbers to land a cover spot in Detection Engineer Magazine. Rather, my goal is to document the physics of content signatures as they relate to Crystal Palace’s PIC and PICO contexts. One take-away is that signatures are drawn from a program’s fingerprint. And, pulling from functions across the program, it’s possible to get a unique fingerprint when enough pieces are expected to agree. And, while program randomization can help, note this: a 4b anchor and 6-10 predictable bytes nearby is a signature.

Tradecraft Garden is a model to develop, release, and demonstrate evasion research that is ground-truth focused and use-case agnostic. The first pillar was to package techniques into standardized artifacts–ground truth that also works as vendor-actionable unit tests. Tools to generate high-quality content signatures is another pillar. Both are efforts to buy good will with other parts of the industry and defuse narratives bad faith actors wield to malign researchers for doing their job.

The above is not incompatible with red teaming. I believe two things: (a) it’s possible to [economically] keep a public and known implementation alive against low-leverage defenses (e.g., content signatures) and (b) security testers provide the most value working within that space of what’s recently known and new, but not fully defended and democratized across the security profession yet.

If you’re with me on point (b), I can help on point (a). I have ideas to empower you to play with these fingerprints and apply your own transform recipes via Crystal Palace. +mutate ain’t it. But these ideas are empty without a way to measure and observe the effect.

The rule generator had to come first.

For a full list of what’s new, check out the release notes.

Keeping bin2bin out of the bin

Happy New Year. I’ve got another Crystal Palace and Tradecraft Garden update for you. My focus this development cycle was making Crystal Palace’s binary transformation framework more robust. I think this is also a good opportunity to brain dump some technical details on this piece of our tradecraft and capability separation stack.

But before we do that, here are the new features:

+regdance randomizes non-volatile registers in some functions.

+blockparty shuffles the order of blocks within a function.

+shatter is a variant of +blockparty. It shuffles the order of blocks program wide.

The above are +options you can use with make pic, make object, etc.

Crystal Palace’s binary transforms are now friendly to MinGW’s -O1 optimizations. Tradecraft Garden’s examples are now compiled with -O1.

Now, let’s dive deep into the binary transformation framework.

What is bin2bin?

Crystal Palace’s binary transformation framework is a program rewriting tool. It’s called bin2bin [1, 2, 3] because we accept a program binary as input, we make changes to it, and our output is a working program binary.

I have a strong aversion to bin2bin tools. My user experience is that they work with their test cases, but break in mysterious ways during production use. While this system will always have limitations, because bin2bin has fundamental limitations, my goal is something that’s robust and predictable—so long as programs stay within what’s supported.

Importantly, while Crystal Palace has a built-in bin2bin framework–it is a linker first. Its job is to compose programs from pieces. But, it’s a linker that can rewrite its programs! Crystal Palace uses this super-power to:

What about LLVM?

An alternative to bin2bin, if you are working with all of the source code (or have a source code-derived LLVM bitcode file), is to write compiler plugins to rewrite a program before executable code is emitted. This is fertile ground for offensive security research:

  • Austin Hudson’s Orchestrating Modern Implants with LLVM (Fortra booth presentation at BlackHat 2025) is a good survey of the LLVM offensive security space.
  • DittoBytes by Tijme Gommers is a metamorphic cross-compiler that relies on LLVM plugins to compile PIC-friendly C into something unique with each run.
  • LLVM-Yx-CallObfuscator by Alejandro González is an LLVM plugin to transparently apply stack spoofing and indirect syscalls to Windows x64 native calls at compile time, driven by a configuration file.

Working from a source code-derived intermediate representation is a high-leverage and safe place to transform a program. Compiler passes are better suited for rewriting programs. A bin2bin tool doesn’t have the same program knowledge that the compiler works from. This means where there’s overlap, some things we do from a bin2bin context will approximate what’s possible via a compiler pass. This will come through when we case study +regdance.

But, with bin2bin we’re also free to deviate from some of the structure assumptions and hierarchy that are baked into a compiler backend. +shatter, discussed later, is an example of what’s possible here.

The above said, I want to share my rationale for bin2bin with Crystal Palace. The goal of Crystal Palace and Tradecraft Garden is to separate capability from tradecraft. More directly: there’s a need to split ops capability development and tradecraft research as disciplines and communities. But, the two have to come together for various use cases too. That’s where Crystal Palace comes in. It’s an end-user tool. My vision is that an operator can edit a .spec to make tradecraft choices for a capability they are about to use. For this model to work, the flexibility needs to exist as close as possible to time-of-use. And, that’s likely after the pieces (possibly proprietary) are compiled. The ability to shuffle registers and play with program structure at time-of-use is just a cool bonus.

How does Crystal Palace’s bin2bin work?

Crystal Palace’s binary transformation framework is based on the fantastic iced. Iced is a disassembler, instruction decoder, and assembler for the x86 architecture (16-bit, 32-bit, and 64-bit). The project has ports for Rust, .NET, Lua, Java, and Python.

I like that Iced is self-contained. It’s a single .jar file and I was able to merge it into crystalpalace.jar. This makes for a low friction and accessible user experience.

In the next sections, we’ll go through the binary transformation framework in detail. But, at a high-level:

  • Disassemble: COFF -> Instructions
  • Lift: Instructions -> Intermediate Representation
  • Transform: Intermediate Representation -> Iced’s Code Assembler
  • Lower: Iced’s Code Assembler -> Object Code -> Updated COFF

The process starts and ends with the COFF. The COFF and object code in it are Crystal Palace’s unit of truth about the program. There is no other meta-information. Each pass of the BTF pipeline expects a COFF as input and it returns the updated COFF as output.

Disassemble

The disassemble step turns object code from a COFF into a linked list of decoded instructions, encapsulated in Iced Instruction objects. This process is also where I match symbols and relocations from the COFF to individual instructions in the program. Symbols are things like: “this is where function X begins”. Relocations are compiler-generated hints for unknown addresses/offsets that the linker and (typically) operating system loader must resolve before the program is truly complete and ready to run. My PIC development crash course goes into more detail on this.

When Crystal Palace works on a program, it does so function by function. The Code class breaks the disassembled program up into linked lists grouped by function for us. I initially did this for the link-time optimization feature.

The link-time optimization is pretty simple: walk the program, from every potential entry point, and find the functions that are called or referenced. After the walk, delete the function -> list mappings that weren’t found in the walk. Pass the remaining functions to the lift/transform/lower pipeline and voila—working optimized program.

See also:

  • src/crystalpalace/btf/Code.java
  • src/crystalpalace/btf/Modify.java
  • src/crystalpalace/btf/pass/CallWalk.java
  • src/crystalpalace/btf/pass/mutate/LinkTimeOptimizer.java

Lift

Lifting is analyzing the disassembled program to elevate it from low-level object code into a safe-to-manipulate intermediate representation (IR).

This is the most critical piece of the binary transformation framework. It’s where the safety, robustness, and creative freedom comes from. Lifting includes several whole-program analysis tasks.

Crystal Palace groups the lift/transform/lower code into “vertical” classes for each analysis and its associated bookkeeping. This organization lets me reason about each analysis and its lift, transform, and lower tasks in isolation.

Rebuilder.java is the heart of the lift, transform, and lower pipeline.

See also:

  • src/crystalpalace/btf/Rebuilder.java
  • src/crystalpalace/btf/lttl/*.java

Transform

The transform step acts on the map of functions -> instructions and the lifting-generated analysis.

Transform walks the program, function by function, to create our rebuilt program. Iced’s CodeAssembler class manages the in-progress rebuilt program state. This API provides the same conveniences one would expect from a CLI assembler, like the ability to declare a label and let the assembler translate that to offsets at assemble time.

The transform walk is where the BTF pass changes stuff in the rebuilt program. This is where the bin2bin features become a framework. The implementation has interfaces to make different kinds of modifications. It can:

  • Pre-walk a function and change the instruction order or edit instruction meta-information before the rebuild walk. +regdance, +blockparty, and +shatter build on this.
  • Inspect symbols referenced by an instruction and swap them for something else. This is what redirect builds on.
  • Replace individual instructions with a sequence of one or more instructions that do something that’s logically equivalent.

Lower

The last step of this pipeline is to lower the program back to machine code. It’s during this process that we ask Iced to assemble the program we built via its CodeAssembler API. There are also a few post-assemble passes needed to patch specific details in this rebuilt object code. Lowering is where the BTF updates the symbols and relocations in the input COFF to match the rebuilt program.

A Day in the Lift, Transform, and Lower

The above describes the high-level architecture of Crystal Palace’s binary transformation framework. Now, let’s go deeper and look at the common lift, transform, and lower actions that are part of each BTF pass.

Jumps

One of the most important tasks for a bin2bin is to deal with branch targets. In object code, a branch target is a fixed offset to some instruction elsewhere in the program. If our bin2bin doesn’t universally detect and fix these offsets, any change that affects the size of the program, even by one byte, will break the rebuilt program.

To handle branches, the jumps module walks the program and identifies branching instructions. Thankfully, Iced provides APIs to determine if an instruction is part of a branching group, which saves me from writing manual detection logic.

For each branch target, the jumps module pre-generates a label. This elevates the branch destination from a brittle fixed offset (e.g., “jump +50 bytes”) to an abstract reference (e.g., “jump to Label A”). This abstraction is critical: it decouples the control flow from the physical byte layout.

During the transform walk, jumps identifies each branch instruction again. Instead of reproducing the original instruction with its fixed offset, jumps emits a branch pointing to our pre-generated label.

When the transform walk encounters an instruction that is a target of a branch, it places the corresponding label at that point in the new program. This happens before any replace logic acts, ensuring the label anchors correctly even if the instructions following it are changed.

One of the cool features of Crystal Palace’s jumps module is the ability to “heal” 0-byte jumps. That is, if Crystal Palace detects an unconditional jump to the next instruction—it’ll opt to not emit the jump instruction. This is useful for features like +blockparty which randomize the order of blocks in a function. Sometimes, something that required a jump won’t require a jump from its new position.

See also:

  • src/crystalpalace/btf/lttl/Jumps.java.

Local Calls

Crystal Palace treats calls, branches, and references to local functions as a special case, different from other branches. The local calls module is the single place to act on label redirecting logic. This is the foundation of the redirect feature in Crystal Palace.

The lifting step pre-generates a label for each function.

During transform, local calls identifies the beginning of a function and plomps its label down before the function’s instructions are emitted.

The local calls module acts on instructions whose offset refers to a function or other symbol from the COFF:

  • Calls are emitted with a reference to a label rather than the original offset.
  • Function jumps are emitted with a reference to the function label rather than the original fixed offset. MinGW’s -O1 does not use jumps in place of calls. But -O2 and -Os do.
  • RIP-relative instructions are emitted with the function’s label as the target.

RIP-relative instructions refer to data relative to the instruction pointer. Any RIP-relative instruction not associated with a relocation (e.g., a string or Win32 API IAT entry) is handled here.

This module attempts to cross-reference the RIP-relative offset to a symbol. If there’s no symbol, the RIP-relative instruction code fails with an error. Similarly, if the local calls module detects a RIP-relative instruction that it wasn’t programmed to re-emit, it will also throw an error.

This strictness can cause problems for hand-written assembly embedded in a program. If hand-written assembly uses LEA for something within itself (that is not a symbol in the COFF), this code will raise an error. While I could probably work around this, for now, I encourage users to generate any hand-written assembly separately and use linkfunc to append it to their program.

One of the design goals of Crystal Palace is to detect situations it wants to handle but that I didn’t anticipate, and give a hard error at those points. I’d rather Crystal Palace raise a deliberate, process-stopping error than silently accept something unexpected and have the resulting program crash later.

At the end of the lowering phase, this module updates the symbols to match the functions and offsets in the rebuilt program.

See also:

  • src/crystalpalace/btf/lttl/LocalCalls.java

Danger Zones

One of the perils of bin2bin transformation is corrupting the RFLAGS register. This is where ALU instructions dump meta-information about the last mathematical operation. Conditional branching instructions key off these flags.

The risk is real: if I replace an instruction with a sequence that changes RFLAGS around, I can corrupt the comparison logic in the program I’m modifying. Not fun!

To mitigate this, Crystal Palace tracks danger zones. It identifies instructions that modify flags and subsequent instructions that read them. Any instruction occurring between that write and read sits in a danger zone.

Different parts of the BTF handle these zones differently:

  • The code mutator (an optional operation) skips mutations in these zones.
  • The PIC ergonomics features (like dfr, fixbss, and fixptrs) throw a hard error.

I could use this meta-information to preserve the RFLAGS register in these situations, but for now, I prefer to fire an error. It’s safer and creates one less code path to test. Modifications within danger zones are rare with -O1, but when they do occur, they are logic bugs waiting to happen. That makes this analysis essential.

See also:

  • src/crystalpalace/btf/lttl/Zones.java

Relocations

Earlier, I mentioned relocations are compiler-generated hints. They are the unknowns in our object code. Having access to these hints is a huge boon for us. Thanks to relocations, we know if an instruction is accessing something in .rdata or .bss. The attach, dfr, fixbss, and fixptrs features all act on instructions with associated relocations.

Like branch targets, relocations are associated with specific offsets in our object code, and we have to sync these offsets with our program’s changes.

The relocations lifter finds all of the relocations in the program. For each relocation, it:

  • Pre-generates a label.
  • Tracks the offset of the relocation within the associated instruction.
  • Ties the original relocation information with this information.

The BTF pipeline doesn’t do anything with relocations on its own. The default behavior is to re-emit these instructions with the pre-generated label marking where in the new program these instructions live. However, any passes (e.g., dfr, fixbss) that modify an instruction with relocations have to take special care.

If a relocation-referencing instruction is changed, the pass needs to associate the relocation label with the new instruction and set the right offset to the relocation within that new instruction. Some passes “swallow” relocations because the new logic renders them unnecessary.

During lowering, the relocations module walks each relocation, finds its new location (thanks to the instruction label and fixed offset into the instruction), and patches in the relocation value from the input program. It’s here the relocations table is regenerated and the COFF is updated.

Confusingly, the relocation value itself is also an offset—but it is an offset into the section or symbol data the relocation is a hint for. When I say “fixed offset into the instruction,” I am referring to the physical location where that relocation value lives.

See also:

  • src/crystalpalace/btf/lttl/RelocationFix.java
  • src/crystalpalace/btf/lttl/Relocations.java

> It works, except when it doesn’t

There’s some room for improvement here. Right now, when I change an instruction, I rely on an explicit offset to mark where the relocation lives inside the instruction. For example, if I put down a MOV instruction to dump an immediate into a register, I assume the instruction is five bytes and the relocation begins at offset 1.

The above works, but it also led to a bug that took some work to track down. During testing, MinGW generated an instruction with an extra prefix byte. When I pushed that program through my binary transform pipeline, the program broke, and I had no clue why.

Iced’s assembler removed the prefix because it was redundant. These optimizations aren’t strange—Iced silently encodes jump instructions to the most size-efficient form for the target.

But, this specific removal was a disaster. Suddenly, the relocation offset was out of sync. Because the instruction had shrunk by one byte, the relocation was pointing to where the value used to be, not where it actually was.

I hadn’t seen this until I got lucky and my test crashed. If this happens with another instruction, I may move to a scheme that matches relocations not to a byte offset, but to an abstract position (e.g., Displacement, Immediate 1, Immediate 2) and uses that to calculate the byte offset dynamically during the rebuild.

This is an example of the tension between doing something that works right now and holds well in most situations–with an occasional exception that I manually deal with–versus engineering a lifting pass and a post-rebuild lowering pass to enforce an always-sane final result.

New Features

Somewhere in here, this is a release blog post.

I’d like to use these new features as a case study for the framework I just described. You might notice I’m downplaying the “why” behind these additions. That’s intentional. What’s implemented right now is only half the story; the rest is coming in a future update.

For now, these new features serve as a good example of what individual passes built on the BTF foundation look like.

Reg Dance

+regdance is Crystal Palace’s take on a popular compiler pass that randomizes register allocation. Because I’ve implemented this in a bin2bin context, I made several conservative choices to protect the integrity of the transformed program.

+regdance uses a lifting pass to determine which non-volatile registers each function pushes to the stack. Non-volatile registers are saved and restored by any function that tampers with their values. This makes them safe to use even if our function makes a call (or if we insert a call to a helper function).

On x64, non-volatile registers include R12-R15, RSI, RDI, RBX, and RBP. However, there are caveats. Sometimes, RSI and RDI are used as mandatory operands for specific instructions (e.g., x64 string instructions). To create a safe randomization set, I walk the function and remove non-volatile registers used in a “fixed” way. My implementation also excludes RBP, though I may lift this restriction in a future release.

Once I’ve identified the “safe-to-randomize” registers, I create a map to shuffle the set (e.g., R12 maps to RSI, R13 maps to RBX, etc.).

During transform, +regdance walks the operands for each instruction. If the operand is a register in our randomization set, it performs the swap. I also check and swap displacement and index registers. The logic normalizes each register to its root to check the set (e.g., ESI -> RSI) and the swapped register is converted to the correct sub-register width. This transformation is done without logic specific to any instruction.

When the randomized register set is big enough, this feature offers enough variance to make it worthwhile. A randomized set of N registers yields N! potential permutations.

  • 3 Registers: 6 permutations
  • 4 Registers: 24 permutations
  • 5 Registers: 120 permutations
  • 7 Registers: 5,040 permutations

That best case requires a hairball of a function; 0-5 registers is typical. If I allow RBP into the mix later, that will unlock more variance. This feature skips randomization if the set contains fewer than three registers.

This is where compilers have the advantage. A compiler pass can work with more of the register set, potentially mixing volatile and non-volatile registers. This yields significantly more permutations. Because Crystal Palace works from a bin2bin context, I’m limited by the need to manage the risks of modifying a compiled program.

There’s one other design choice to note: this implementation avoids modifying the function prologue and epilogue. This means +regdance limits itself to the registers the function was already using. I do this to preserve the push and pop contents at the function boundaries. Prologues and epilogues are deterministic and common compiler outputs. Altering them creates an anomaly that can stand out. I like to avoid these anomalies where I can.

See also:

  • src/crystalpalace/btf/lttl/SavedRegContext.java
  • src/crystalpalace/btf/pass/mutate/RegDance.java

Block Party

While +regdance is a bin2bin attempt to approximate a common compiler obfuscation, +blockparty stands on more neutral ground. +blockparty is a feature to shuffle basic blocks within each function.

A basic block is a sequence of instructions with one entry point and one exit point. It is the fundamental unit of analysis for optimizers, analysis tools, and program editing tools.

One of the Crystal Palace lifting analyses splits the whole program into blocks, grouping them by function. To find blocks in a stream of instructions, I rely on the leaders algorithm. It’s a straightforward walk. The first instruction of a function is the beginning of a block. If an instruction is a jump target, it is the beginning of a block. If an instruction is a branch, the next instruction is the beginning of a new block. Some implementations treat calls as block boundaries, but mine does not.

+blockparty inserts itself into the BTF pipeline as a pre-walk filter on a function’s instructions. This filter retrieves all the blocks for a function, keeps the first block static to preserve the function entry, and shuffles the rest. It then dumps these reordered instructions into a new list for the transform walk.

During transform, Crystal Palace checks if the processed instruction is the end of a fall-through block. It then peeks at the next instruction in the walk. If the next instruction is not the original fall-through target, the BTF dynamically emits a jump instruction to connect the just-ended block to its correct destination.

See also:

  • src/crystalpalace/btf/lttl/Blocks.java
  • src/crystalpalace/btf/mutate/BlockParty.java

Shatter

One of the fun things in this framework is the ability to play with whole program structure. An application of this power is +shatter. The +shatter feature is like +blockparty, but it randomizes blocks across the entire program.

When I built +blockparty and +shatter, I wasn’t thinking about an effect on Ghidra or other analysis engines. That isn’t needed for the use cases I care about. Instead, I am thinking about the assumptions baked into how content signatures are derived and how memory scanners work. +shatter plays with the concept of code locality. What is predictably contiguous becomes limited to the basic block itself.

What I didn’t do with +shatter is opt to dynamically split blocks further. I considered splitting blocks after a fixed number of instructions. However, I have another idea for a complementary primitive that I’ll look at for the next release.

See also:

  • src/crystalpalace/btf/pass/mutate/Shatter.java

-O1 Support (MinGW32 Compiler Optimizations)

The last major feature in this release is support for -O1. This falls into the “I had to do it sometime” category.

A program compiled with -O0 looks drastically different from a program compiled with optimizations. An -O0 binary treats the stack as the source of truth, resulting in a flood of instructions to keep local variables synced on the stack. Almost nothing in production ships with -O0. -O1 is the baseline for production code.

That said, supporting a new optimization level isn’t trivial for a bin2bin tool. Different compiler flags and optimization levels generate different patterns of instructions.

Crystal Palace’s PIC ergonomic features (dfr, fixbss, fixptrs) are not instruction agnostic. I have to anticipate exactly which instructions the compiler will select when your program calls a function, references a function, or interacts with data in .rdata or .bss. Crystal Palace maintains specific transformations for each of these patterns. If an unanticipated instruction appears, Crystal Palace won’t silently generate a broken binary. Instead, it recognizes the gap and raises an explicit error.

> Dirty Leaves Made My Code Fall

Compiler optimizations do weird things. One of the x64 ABI requirements is that the stack is 16 byte aligned before a call. Certain instructions (e.g., SIMD instructions) will crash the program if the stack isn’t 16b aligned. A call, by the way, breaks 16b alignment when it pushes the return address onto the stack. In a -O0 program, every function fixes the stack alignment in the prologue. In a -O1 context, the compiler… takes liberties.

I ran a test where I added a SIMD movaps “crash canary” to dfr and fixbss in my tests:

#ifdef WIN_X64
	__asm__ __volatile__(
        	"sub $0x10, %%rsp\n\t"
        	"movaps %%xmm0, (%%rsp)\n\t"
        	"add $0x10, %%rsp"
        	::: "memory"
	);
#endif

I wanted to see if any of my transformations created an unaligned stack. Sure enough, two of my tests crashed. I was very confused, because the same transforms worked in the other tests.

With -O1 enabled, the compiler opts out of aligning the stack in the prologues of some leaf functions. A leaf is a function that doesn’t make any calls. This is OK, because if the function doesn’t make any calls (or use instructions that require alignment), why waste bytes and cycles to adjust the stack in the prologue and epilogue?

But, if we dynamically insert a function call (e.g., dfr acting on a reference, anything fixbss)—well… we’re no longer a leaf function. The unaligned stack is now a problem. This problem required creating an analysis pass to find dirty leaves. A dirty leaf is a leaf function with an unaligned stack.

The problem is that some leaf functions preserve registers in their prologue. Sometimes, the preserved registers align the stack. Other times, they don’t. The dirty leaf analysis walks the function’s instructions and determines if, accounting for the various stack operations, the stack is aligned. This informs which value dfr and fixbss expand the stack to when inserting their calls.

See also:

  • src/crystalpalace/btf/lttl/DirtyLeaves.java

> Fighting the Compiler

If you compile your programs with -O1, expect that you will fight the compiler more. For example, the compiler might see a small function that you intend as a redirect join point and decide to inline its contents instead of calling it. Or, if it’s empty, just omit the call altogether. I encountered this surprise removal with the Tradecraft Garden Hooking example. In fight-the-compiler situations __attribute__((optimize("O0")) disables optimizations for a specific function. That didn’t help my hooking example though. Here’s how I dealt with the inlining and elimination of my empty setupHooks join point:

/*
 * This is an empty function, but we will use redirect to LAYER setupHooks from our modules on top of this.
 *
 * NOTE: gcc with -O1 likes to inline some functions and an empty or minimal function is a prime candidate for
 * inlining. I'm using noinline to prevent that tragedy, because if a function is inlined, we can't redirect it
 */
void __attribute__((noinline)) setupHooks(char * srchooks, char * dsthooks, DLLDATA * data, char * dstdll) {
	/*
	 * And, in the fighting the optimizer department, -O1 likes to also not call a function it believes has
	 * no side-effects. So, we stick this here to say GCC LEAVE MY EMPTY FUNCTION ALONE!
	 */
	__asm__ __volatile__("");
}

What about -Os and other compiler optimizations?

If you’re going to use Crystal Palace to turn a COFF into PIC, compile your code with -O0 or -O1. This is what’s most likely to work with fixbss, dfr, and fixptrs.

For situations where you’re not turning a COFF into PIC, things are more relaxed. A potential use case of Crystal Palace is to merge + attach tradecraft with Beacon Object Files before they’re run. But, some popular BOFs (e.g., the Situational Awareness commands) are compiled with -Os. For this reason, there’s some -Os support in Crystal Palace. For example, attach and redirect are able to act on jumps to functions. Function jumps don’t show up in -O0 or -O1 code.

Down the road, I imagine sticking with -O1 as the supported optimization level. I can meet -Os halfway in some cases. -O2 or -O3 are probably not going to happen.

Migration Notes

None.

Closing Thoughts

The theme for this release is robustness. This effort was driven by the pain of supporting -O1 optimization. I ran into a lot of situations with -O1 that I simply didn’t see with -O0. While this post focused on x64, the x86 transforms received a rigorous overhaul too.

This release significantly refactored and updated the binary transformation framework. The architecture now strictly separates vertical lift/transform/lower concerns, making it easier to add new “verticals” as needed. It also provides cleaner interfaces to design new passes with.

As a developer, any effort that tames internal complexity and makes it clean and extensible is a win. It means every future feature sits on a reasoned and workable foundation. That’s where we’re at now.

Enjoy the release!

For a full list of what’s new, check out the release notes.

Tradecraft Orchestration in the Garden

What’s more relaxing than a beautiful fall day, a crisp breeze, a glass of Sangria, and music from the local orchestra? Of course, I expect you answered: writing position-independent code projects that separate capability from tradecraft. If you didn’t answer that way, you’re wrong.

In the last six months, Tradecraft Garden has covered a lot of ground. This project started as a linker to make it easier to write position-independent code DLL loaders and it has rapidly evolved into an Aspect Oriented Programming tool to weave tradecraft into PIC and PICO capabilities. It can still write position-independent code DLL loaders too (modular and self-incepting DLL loaders, at that)—but there’s a lot more potential here.

One of the needs in the current model, is that while we can separate tradecraft from capability in C—the linker script forces us to define our architecture and tradecraft as one monolithic thing. Separating base architecture and tradecraft, within the specification files themselves, is the theme of this release.

Build Templates with %variables

This release introduces %variables into the Crystal Palace specification language. These are user-defined strings passed in at the beginning of the program. %variables are usable anywhere you would use a “string” argument in Crystal Palace’s specification language.

For example:

load %foo

This command will resolve %foo and load its contents. Easy enough, right?

We also have string concatenation with the <> operator too. So:

load %foo <> “.x64.o”

This will resolve %foo and append .x64.o to it.

With %variables comes the need to better understand what a script is doing. The new echo command prints its arguments to STDOUT (CLI) or to a SpecLogger object (API):

echo “I am looking at: “ %hooks

%variables evolve the Crystal Palace specification file language from a collection of commands into a build templating language. That is, a specification file may now define the architecture of a loader or capability with the information about specific tradecraft getting added later.

Simple Loader – Execution Guardrails uses these new features. The loader is now agnostic to the follow-on specification file it encrypts and packs. The %STAGE2 variable is a stand-in for that file. You can pair this stage with one of the other examples from the Tradecraft Garden (including a COFF loader or just straight PIC).

Populating %variables (“The Glue”)

For this base architecture and specific tradecraft separation to work, we need a glue. That is a means to specify %variables.

Specifying %variables via the Java API is easy. Put a “%variable” key into the environment map with a “string” value.

You may also specify %var=”value” arguments via the ./link and ./piclink CLI tools too.

If you want to keep a variable configuration in a file, add @file.spec to your program’s CLI. This will read file.spec and use its commands to populate variables before the main linker script is run. And yes, we have new commands to set %variables from a Crystal Palace .spec file.

setg sets a variable within the global scope for this build session:

setg “%bar” “0xAABBCCDD”

Crystal Palace has a concept of scope for variables too. You will almost always want global variables in these configuration files. But, local scope exists for variables that are visible only within the current label’s execution context. Use set to set a local variable:

set “%foo” “file.spec”

With any commands that set or update %variables, quote the variable name to prevent Crystal Palace from evaluating the variable to its contents before command execution.

And, pack is a way to marshal %variables (and other strings) into a $VARIABLE byte array.

pack $VAR “template” “arg1” %arg2 …

The Perl programmer in me can’t resist a good pack command. pack accepts a variable, template string, and list of arguments that correspond to the characters in the template string. Think of the template as a condensed C struct definition. Each character specifies how pack will interpret the argument string and what type it will marshal the value to.

While I see these commands as configuration tools, they are generic Crystal Palace commands. You can use them anywhere.

Layering and Chaining

While straight variable substitution is handy, sometimes, we want to mix and match modules of an unknown quantity. That’s where the next feature helps.

Crystal Palace’s foreach command expects a comma-separated list of items as its argument.

foreach %libs: mergelib %_

The foreach command walks the provided list and calls another Crystal Palace command with %_ set to the current element of the list. %libs needs to exist, but an empty value is OK. The goal of foreach is to act as a placeholder for layered tradecraft that’s dynamically brought into a base architecture.

The next command is a list shift and execute tool. next expects a “%variable” name argument and it expects %variable is a comma-separated list of values. If the list is empty, next does nothing. If the list is not empty, next removes %variable’s first item and runs the specified command with %_ set to that element.

next “%NEXT”: run %_

The goal of next is to support chaining tradecraft together, giving cooperating modules a mean to pass execution through each other.

Modular Specification File Contracts

Crystal Palace’s main modularity tool is to create separate .spec files and use run to execute their commands in another file. This release gives us more modularity options.

The run command now accepts positional arguments. And, it passes them on to the child script as %1, %2, %3, etc.

run “file.spec” “arg1” %arg2

The positional arguments of “file.spec” are local to that run of that file. If file.spec runs another .spec file, the positional arguments are local to that specification file’s runs. The local scope is necessary to prevent our .spec files from stepping on each other’s arguments.

This release also adds callable labels to specification files too. Think of them as local runnable files, user-defined commands, or Crystal Palace functions. To create a callable label use:

name.x64:

Then, list your commands like normal. That’s it. These labels are callable with a dot name syntax and accept positional arguments too:

.name “arg1” “arg2”

The nice thing about this feature is we now have a choice or whether to split a function into its own .spec file or let it co-exist inside of the current .spec file. And, while this is nice, that’s not why I chose to build this feature. The real payoff is the call command.

call runs a callable label from another specification file:

call “file.spec” "name" “arg1” “arg2”

call is an encapsulation tool. That is, our base specification defines the architecture of our capability or loader. %variables become the placeholder for our tradecraft implementation. And, callable labels and call let us present the pieces of a tradecraft in a single file, with a common interface (callable labels), that our base architecture expects to act on.

Simple Loader – Hooking demonstrates this idea. It defines a base architecture for hooking a DLL and a modular contract for hooking tradecraft modules. XORhooks demonstrates this contract. Stack Cutting is now a module that composes on top of this base loader. What’s really cool? You can layer them together.. See the Simple Loader – Hooking notes for more on this.

File Paths

One of the mundane, but important details in this scheme is file path resolution. Crystal Palace commands treat file paths as relative to the current .spec file. With %variables, things get messy, because we might specify a file path in one place (e.g., the CLI, @config.spec, an argument to run/call) and it gets used elsewhere. The resolve command brings some predictability to this.

resolve “%files”

resolve will walk through %files (assuming it’s a comma-separated list of files), canonicalize each entry to a filename [relative to the current .spec], and set %files to these full paths. This gives you control over when this path resolution happens and which .spec context its relative to.

The -r CLI option will resolve file paths in a %key=”value” argument provided that way.

Migration Notes

  1. The link and piclink shell scripts changed in this version. You’ll want to update to the latest from the Crystal Palace distribution or source archive.

  2. I’ve updated the CLI syntax for piclink and link to accept % and $ sigils for %VAR and $DATAKEY values specified on the command-line. The old behavior of KEY=[bytes] to set $KEY still works, but going forward, the documentation and other materials will use explicit sigils. You may want to update your scripts to use explicit sigils too.

  3. Several of the methods in crystalpalace.spec.LinkerSpec were deprecated. I cleaned up the API to make it easier to share arguments between a configuration LinkSpec and the main LinkSpec. https://tradecraftgarden.org/docs.html#javaapi

Closing Thoughts

This release introduced several commands and features to change how Crystal Palace specification files work together.

The combination of %variables and the ability to set those via the Java API or CLI allow our users to combine a base specification with specific tradecraft choices later on. This turns a base PIC into a re-usable component.

Callable labels and call allow us to encapsulate tradecraft modules into a single file with different touch points (e.g., initialization, apply hooks) available via a common convention. Pair this with foreach and we have a means to specify tradecraft modules in one %variable and use them at the right-spots within the base project.

The goal of this release is to move Tradecraft Garden projects from singular monolithic examples, that each redefine everything, into reusable components to compose tradecraft cocktails with.

To see the full list of what’s new, check out the release notes.