PICOs

A Position-Independent Code Object, aka PICOarrow-up-right, is Crystal Palace's convention for running a COFF file in memory. They're written like Cobalt Strike BOFs and support the same MODULE$function convention for referencing Win32 APIs.

Writing a COFF for CrystalC2 is not that much different to how you'd write one for Cobalt Strike or any other C2 that supports them. CrystalC2 supports the typical Data, Format, and Output APIs that have been standardised by Cobalt Strike.

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

void go ( char * args, int args_len )
{
    datap parser;
    BeaconDataParse ( &parser, args, args_len );

    int msg_len;
    char * msg = BeaconDataExtract ( &parser, &msg_len );

    BeaconPrintf ( CALLBACK_OUTPUT, "You said: %s (%d)\n", msg, msg_len );
}

A COFF must pass through a procedure to 'transform' it into a PICO before being sent to CrystalC2's Beacon (via the binline_execute function). This is handled client-side using the the Crystal Palace API. Registering a command to execute a COFF (as a PICO) will look like this:

import crystalpalace.spec.*;
import java.util.HashMap;

sub demo
{
    local ( '$handle $coff $spec $cap $pico $args' );

    $handle = openf ( "demo.x64.o" );
    $coff   = readb ( $handle, -1 );
    closef ( $handle );

    $spec = [ LinkSpec Parse: "demo.spec" ];
    $cap  = [ Capability Parse: cast ( $coff, 'b' ) ];
    $pico = [ $spec run: $cap, [ new HashMap ] ];

    binline_execute ( $1, $pico, $null );
}

register_command ( "demo", "Demo command", "This is just a demo command", &demo );

The real magic occurs in the specification file (demo.spec in this example).

Where:

  • make object is the command to transform the COFF into a PICO.

  • import tells the Beacon's loader what function pointers it needs to patch into the PICO as it's loaded into memory. The functions listed here are the only 'BOF APIs' that Crystal Palace supports.

circle-info

Any other Crystal Palace command is valid here depending on what you want to do.

Last updated