Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bof-runner

Standalone macOS POC for loading and executing BOFs.

Build

cd poc/bof-runner
GOWORK=off go build -o bof-runner .

Usage

./bof-runner <bof.dylib|bof.bin> [args-json]

All BOFs must export the standard entry point:

void go(char* args, int args_len, char* output);

Execution modes

Extension Mode Description
.dylib / .so dlopen/dlsym Shared library loaded by dyld. Supports libc calls (snprintf, gethostname, etc).
.bin mmap shellcode Raw .text bytes mmap'd as RWX. Self-contained code only — no external symbol references.

Both modes include signal-based crash recovery (SIGSEGV, SIGBUS, SIGILL, SIGABRT).

Building test BOFs

./build-test-bof.sh

Produces four test BOFs in test-bofs/:

BOF Description
hostname.dylib System hostname
sysinfo.dylib Kernel/OS info via uname()
whoami.dylib UID, username, home dir, shell
echo.dylib Echoes back the args JSON

Examples

# Run a test BOF
./bof-runner test-bofs/hostname.dylib

# Pass arguments
./bof-runner test-bofs/echo.dylib '{"hello":"world"}'

# Compile and run an existing BOF from Bridge/bofs/src/
clang -shared -fPIC -Os -o test.dylib ../../Bridge/bofs/src/sysinfo.c
./bof-runner test.dylib

Writing a BOF

#include <stdio.h>
#include <string.h>

void go(char* args, int len, char* output) {
    snprintf(output, 4096, "Hello from BOF! Args: %.*s", len, args);
}

Compile:

clang -shared -fPIC -Os -o mybof.dylib mybof.c
./bof-runner mybof.dylib

Converting TrustedSec / community BOFs

Existing BOFs (TrustedSec SA, etc.) are typically Windows COFF objects that use the Beacon API. bof-runner expects macOS dylibs with a different output model:

Aspect TrustedSec / community BOFs bof-runner
Format COFF object files (.o) .dylib or .bin shellcode
Platform Windows x86/x64 macOS arm64/x86_64
Output BeaconOutput(), BeaconPrintf() Write directly to output buffer
Args BeaconDataParse() Raw args pointer + len

You cannot load a Windows .o file directly. Conversion requires source and one of:

1. Source port (recommended) — Replace BeaconOutput/BeaconPrintf with writes to the output buffer, replace BeaconDataParse with manual parsing of args, and swap Windows APIs for POSIX equivalents. Then compile as a dylib.

2. Beacon compatibility shim — Implement BeaconOutput, BeaconPrintf, and BeaconDataParse to write into the output buffer. Link the BOF source against this shim. You still need to port any Windows-specific logic to POSIX.

3. TrustedSec ELFLoaderELFLoader loads ELF object files on Linux/macOS with Beacon compatibility. It is a different loader; BOFs must be recompiled as ELF for the target platform.

How it works

This mirrors the BOF execution path from scout-m:

  • dylib mode: dlopen() loads the library, dlsym() resolves the go entry point, then calls it with args and an output buffer. dyld handles all symbol resolution (libc, system frameworks, etc).
  • shellcode mode: mmap() allocates RWX memory (with MAP_JIT on Apple Silicon), copies the raw bytes in, flushes the icache, and calls the memory as a function pointer. On ARM64 macOS this requires pthread_jit_write_protect_np() to toggle between writable and executable states.

The dylib mode is what works for real BOFs since they almost always call libc. The raw shellcode path from the scout only works for fully self-contained position-independent code with no external references.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages