Skip to content

nbaertsch/zig-bof-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zig BOF Template

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.

Features

  • 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

Zig BOF running in Adaptix

Quick Start

# Build bof.o
zig build

# Run go() locally (Windows/WSL only)
zig build run

# Run unit tests
zig build test

Output: zig-out/bof.o ready for Cobalt Strike

Usage

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});
}

Windows API Resolution

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

Beacon API Coverage

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

Local Testing

The template includes a test mode that runs BOFs locally:

zig build run

Test 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$Function external symbols
  • Cobalt Strike resolves symbols at runtime
  • Clean object file ready for BOF loading

Resources

License

For authorized security testing only.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors