We have previously blogged about using BeaconGate to dynamically instrument Beacon at run time. However, one of BeaconGate’s limitations is that it is not applied throughout Beacon’s entire lifecycle. Specifically, it does not impact the reflective loading process. Hence, if an EDR is monitoring for unbacked VirtualAlloc or LoadLibrary calls, we cannot use BeaconGate to bypass it. To solve this problem, we must get our hands dirty with UDRL development. However, to simplify this process we want to reuse our existing BeaconGate BOF examples from our UDRL.
Until recently this was not possible, however Raphael Mudge’s Crystal Palace, released in June 2025, enables us to do exactly this. With Crystal Palace we can trivially load our call stack spoofing ‘capability’ (i.e. a BeaconGate BOF) and use it from a UDRL. That said, the real magic of Crystal Palace goes much further in that it enables us to rapidly combine different capabilities (i.e. self-contained ‘units of execution’) to create novel loaders/PIC tradecraft. To demonstrate the power of this idea, we have open-sourced Eden Loader.
Eden Loader combines existing open-source techniques, namely Raphael Mudge’s page streaming and Draugr call stack spoofing, to create a novel PoC UDRL for Cobalt Strike. The goal of Eden Loader is to serve as an example resource for others to build on and to inform/stimulate the security conversation. In the following sections, we will outline the steps we took in porting our Draugr BeaconGate BOF over to Crystal Palace and ultimately combining it with page streaming to create a novel UDRL.
This blog assumes familiarity with Crystal Palace, reflective loading and UDRLs. For a quick overview, we recommend Modular PIC C2 Agents, Harvesting the Tradecraft Garden, and Revisiting the User-Defined Reflective Loader. It is also important to note, the goal of Eden is to demonstrate the idea of using Crystal Palace to combine and reuse capabilities. It is not intended to be a fully ‘evasive’ loader. As such, it lacks a number of basic OPSEC features by design. For example, it uses RWX memory and does not track/mask Beacon’s heap memory. It is therefore vulnerable to YARA signatures such as this.

From BOF to PICO
Crystal Palace is a linker designed specifically for position-independent code that enables us to embed, link and run COFFs from PIC. At a high level, this means Crystal Palace can take an object file and embed it into our reflective loader. The loader can then access the embedded ‘capability’, load it into memory, and execute its entry point. Hence, we can utilise existing capabilities like our Draugr call stack spoofing BOF from Sleepmask-VS to easily add call stack spoofing to a UDRL.
In Crystal Palace nomenclature, a COFF which can be run from PIC is referred to as a PICO, which is different to a traditional Beacon Object File (BOF). BOFs are a special type of COFF which support Beacon specific conventions (i.e. the BOF C API etc.) and which are intended to be executed by Beacon. Crystal Palace does not understand Beacon conventions (i.e. it cannot resolve BeaconOutput) and so these must be removed for Crystal Palace to be able to ingest the object file.
Furthermore, Crystal Palace is built on top of MinGW and therefore only supports object files which have been compiled with it. The Draugr BOF was originally written with Sleepmask-VS, which uses Clang. As a result, Crystal Palace’s ./link command will throw an error when trying to convert the object file produced by Sleepmask-VS to a PICO.
Therefore, we needed to make the following changes to enable our Draugr BOF to be ingested by Cystal Palace:
- Remove any BOF/Beacon specific conventions (i.e. remove any use of the BOF C API, e.g.
BeaconOutput). - Tweak the Draugr code to compile with MinGW.
- Remove any static variables as these generated relocation errors in Crystal Palace.
At this stage, we have a Draugr object file which we can embed and run from a PIC loader. The following pseudo code shows how to access embedded PICOs from a loader written with Crystal Palace:
char __DRAUGR__[0] __attribute__((section("draugr")));
char * findAppendedPICO() {
return (char *)&DRAUGR;
}
/* Load appended PICO and run it */
char * src = FindAppendedPico();
/* Allocate memory for our PICO */
dstData = funcs->VirtualAlloc(NULL, PicoDataSize(src), MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);
dstCode = funcs->VirtualAlloc(NULL, PicoCodeSize(src), MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
/* load our pico into our destination address, thanks! */
PicoLoad((IMPORTFUNCS *)funcs, src, dstCode, dstData);
/* execute our Draugr pico */
((PICOMAIN_FUNC_3)PicoEntryPoint(src, dstCode)) (<draugr args>);
This is complemented by a Crystal Palace spec file which instructs Crystal Palace to load our Draugr object file, convert it into a PICO, and link it to our code above:
load “bin/draugr.x64.o”
make object # Convert this object file into a PICO
export
link “draugr” # Link it into our loader code above as "draugr"
My Kingdom For A Debugger
By default, MinGW does not support pdb files, which means debugging on Windows (say via an IDE) can be difficult. This was one of the main reasons why our BOF development templates (BOF-VS/Sleepmask-VS) use MSVC/Clang, as they have full debug support in Visual Studio.
However, you can add the -g compiler flag to output COFFs/PEs with DWARF debugging information, as shown in the Makefile snippet below:
$(CC_64) -DWIN_X64 -DDEBUG_EXE=1 -g -shared -masm=intel -Wall -Wno-pointer-arith -fno-jump-tables -c src/draugr/draugr.c -o bin/draugr_dbg.x64.o
$(CC_64) -DWIN_X64 -nostartfiles -g -Wl,-e,dbg_go bin/draugr_dbg.x64.o -o bin/draugr.x64.exe
This addition makes it possible to step through the code in WinDbg:

WinDbg debugging draugr.x64.exe. This is an executable build (with debug info) of the Draugr object file and can be used to step through, set breakpoints, and debug the Draugr call stack spoofing code.This is extremely helpful when writing complex loaders as it means you can debug IAT hooks on a live Beacon and step through tradecraft on the fly. For a more comprehensive overview of setting up debugging for Crystal Palace see this blog by Rastamouse (who provided us with the trick above).
cv2pdb can also be used with MinGW to convert
DWARF debug information to pdb files but that is beyond the scope of this blogpost.
PICO to PIC
At this point, we can load our Draugr object file from a PIC loader and proxy WinAPI calls through it. However, loading and running a PICO from a UDRL will still require us to make a VirtualAlloc call (i.e. we still need to allocate memory for the PICO, as shown in the pseudo code above). If our PICO contains our call stack spoofing routine, then we will always have an unbacked VirtualAlloc call. This may or may not be a problem (the majority of EDRs are probably not going to care about a single unbacked VirtualAlloc call) but let’s assume it is.
To solve this, we need to go one level deeper and modify our Draugr code so that it can be compiled as PIC. This PIC stub can then be embedded in our loader and called directly without needing to first load a PICO (and hence allocate memory).
Crystal Palace provides the make pic command to facilitate this process. The make pic command will instruct Crystal Palace to load the target object file and, at a high level, extract the .text (i.e. the code) and the .rdata (string constants etc.) sections from the given COFF and combine them together while resolving relocations. If your COFF has any relocations/issues that are not PIC safe (i.e. you have a DFR import such as KERNEL32$Sleep) Crystal Palace will throw an error. Hence, we need to modify our Draugr COFF further to make it compatible with Crystal Palace’s make pic command.
The first change we needed to make was to manually handle any dynamic function resolution, which (as stated above) is not supported in PIC. Hence, DFR syntax such as KERNEL32$Sleep cannot be used and the target Windows API must be resolved manually. To solve this, we passed a vtable of function pointers (i.e. a Crystal Palace IMPORTFUNCS struct) to our PIC stub so that it could resolve what it needed.
The second change we needed to make was to remove any globals, as these will throw relocation errors. This is because they live in a section which is not extracted by make pic. We used globals originally in Sleepmask-VS to save state (i.e. resolved gadgets for Draugr). The upside of this change is that our Draugr callstack spoofing stub will now use a different gadget each time (at the slight cost of a performance hit).
This is one area where Crystal Palace has received significant updates while this research was on-going. For example, it now supports globals and DFR in PIC. We did migrate Eden to use newer features of Crystal Palace, however we lost debugging support as a result. Debugging was seen as a priority in terms of enabling others to build on and understand the techniques involved so we reverted back to the original Crystal Palace features.
After making the above changes, we can modify our spec file as shown below:
load “bin/draugr.x64.o”
make pic # This will load the target object file and strip certain sections to create a PIC stub
export
preplen # This prepends the length of the PIC stub directly before it
link “draugr
Following these changes, we now have a (self-contained) Draugr PIC stub that we can directly embed in a UDRL and use to proxy all our API calls through. To do this, we just need to pass a FUNCTION_CALL structure for the target function we want to execute (à la the Sleepmask entry point). This is shown in the pseudo code below:
functionCall.functionPtr = funcs->VirtualAlloc;
functionCall.numOfArgs = 4;
functionCall.args[0] = (ULONG_PTR)NULL;
functionCall.args[1] = 0x1000;
functionCall.args[2] = MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN;
functionCall.args[3] = PAGE_READWRITE;
/*
* The IMPORTFUNCS struct contains a vtable of ptrs
* so that our PIC stub can resolve what it needs.
*/
funcs->proxy((IMPORTFUNCS*)funcs, &functionCall);
char* pBuffer = (char *)functionCall.retValue;
We can use this approach to proxy all the VirtualAlloc and LoadLibrary calls used during the reflective loading process through our Draugr PIC stub.
However, we can go further and combine our Draugr PIC stub with IAT hooking. Through this we can also proxy all of Beacon’s ‘suspicious’ Windows API calls via our Draugr callgate. This approximates (with a slightly different implementation), Rastamouse’s CrystalKit, which uses IAT hooking in combination with a Draugr BeaconGate BOF.
We have deliberately attempted to decouple our call stack spoofing routine from our loader to make it as modular as possible (i.e. so it could be swapped in for another object file with the same entry point without needing to change anything in the loader).
Dino Megazord!

While this is impressive in itself, the real power of Crystal Palace is in combining and repurposing existing capabilities to quickly develop novel loaders and PIC tradecraft. For example, we can now easily combine the Draugr call stack spoofing example described above with other techniques, such as Raphael Mudge’s page streaming loader.
This uses guard pages and a vectored exception handler (VEH) to stream Beacon pages into memory as they are needed. Page-streaming supports customising the max number of visible pages and ensures that unused pages are removed, minimizing Beacon’s in-memory footprint. The net result of combining these different capabilities is Eden. By combining these two techniques, we can rapidly produce a UDRL which performs call stack spoofing for all of Beacon’s suspicious WinAPI calls along with a custom runtime obfuscation technique.
Furthermore, Eden is modular in the sense that we can swap out the Draugr object file with another stack spoofing technique. This idea could be taken much further to create genuinely modular PIC loaders which are configurable via a Crystal Palace spec file to select and apply different capabilities (i.e. a ‘static’ PIC loader which is fully customisable via COFF ‘modules’, such as different guardrails/stack spoofing/sleep obfuscation techniques etc.)
This blog has largely discussed the power of combining and repurposing capabilities in the context of loaders. For a broader discussion on wider PIC tradecraft see the following talk, ‘Linkers and Loaders: Experiments with Crystal Palace’.
Closing Thoughts
The aim of this blog post was to share our experiments and experience of writing a UDRL with Crystal Palace by reusing existing capabilities. In doing so, we wanted to demonstrate the power of Crystal Palace in enabling users to quickly combine capabilities (i.e. COFFs/DLLs) to rapidly develop custom loaders/PIC tradecraft. In showing our steps in creating a novel UDRL, we hope to demystify low level PIC development and give a path for security practitioners to start creating their own novel PIC tradecraft and contribute to the security conversation.
