A template for developing Cobalt Strike Beacon Object Files (BOFs) using Zig. Features a complete pure-Zig implementation of the Beacon API with zero overhead - only functions you call generate symbols.
- Zig Beacon.h - Re-written beacon.h in pure Zig.
- Local Testing - Unit testing with real Windows and Beacon (partial) API calls.
- Windows API Resolution - Correct CS-style Win API export names (e.g.
DLL$Function). - Stack Strings - Included comptime stack string helper.
- Zero Bloat - Lazy loading: only called functions generate symbols
- Cross-Platform Build - Compile Windows x64 BOFs from any platform
# Build bof.o
zig build
# Run go() locally (Windows/WSL only)
zig build run
# Run unit tests
zig build testOutput: zig-out/bof.o ready for Cobalt Strike
Edit src/bof.zig:
const ss = @import("stack_string.zig").ss;
const beacon = @import("beacon.zig");
pub export fn go(args: [*c]u8, length: c_int) callconv(.c) void {
// Parse arguments
var parser: beacon.DataParser = undefined;
beacon.BeaconDataParse(&parser, args, length);
const value = beacon.BeaconDataInt(&parser);
var msg = ss("Received: %d\n");
beacon.BeaconPrintf(beacon.CALLBACK_OUTPUT, &msg, .{value});
// Call Windows APIs
const GetPID = beacon.kernel32.call("GetCurrentProcessId", fn () callconv(.c) u32);
const pid = GetPID();
var pid_msg = ss("PID: %d\n");
beacon.BeaconPrintf(beacon.CALLBACK_OUTPUT, &pid_msg, .{pid});
}Three methods to call any Windows API:
// Method 1: Common library helper wrappers
const GetLastError = beacon.kernel32.call("GetLastError", fn () callconv(.c) u32);
// Method 2: Generic win32() helper
const GetPID = beacon.win32("KERNEL32", "GetCurrentProcessId", fn () callconv(.c) u32);
// Method 3: Direct @extern
const GetComputerName = @extern(*const fn ([*:0]u8, *u32) callconv(.c) u32, .{
.name = "KERNEL32$GetComputerNameA",
.is_dll_import = true,
});Available DLL helpers: kernel32, ntdll, advapi32, user32, wininet, msvcrt
Object mode (beacon.zig): All 53 Beacon API functions available via @extern declarations
- Generates proper
__imp_symbols for Cobalt Strike to resolve at runtime - Complete API surface: Data, Format, Output, Token, Spawn+Inject, Information, Data Store, Syscall, Beacon Gate, Utility
Test mode (beacon_impl.zig): 15 core functions with actual implementations
- Data API (6) - BeaconDataParse, BeaconDataPtr, BeaconDataInt, BeaconDataShort, BeaconDataLength, BeaconDataExtract
- Format API (7) - BeaconFormatAlloc, BeaconFormatReset, BeaconFormatAppend, BeaconFormatToString, BeaconFormatFree, BeaconFormatInt, BeaconFormatPrintf
- Output API (2) - BeaconOutput, BeaconPrintf (with full printf formatting support)
- Additional APIs can be added as needed for testing specific BOFs
The template includes a test mode that runs BOFs locally:
zig build runTest mode features:
- Real Windows API calls via LoadLibrary/GetProcAddress
- BeaconPrintf outputs to stdout with printf formatting
- Data/Format API implementations for parsing arguments
- Separate test runner executable invoked
Object mode:
- Generates
__imp_DLL$Functionexternal symbols - Cobalt Strike resolves symbols at runtime
- Clean object file ready for BOF loading
For authorized security testing only.
