Skip to content

wwh1004/bof-template-ng

Repository files navigation

BOF Template (bof-template-ng)

English | 简体中文

Introduction

This is a modern Beacon Object File (BOF) development template designed to simplify the process of writing, compiling, and debugging BOFs. It provides a toolchain that allows developers to write code using standard C/C++ syntax, supports both GCC (MinGW) and MSVC (Visual Studio) compilers, and enables source-level debugging in Visual Studio. Additionally, the built-in BOF Linter automatically checks for potential issues during the compilation phase, greatly improving troubleshooting efficiency.

  • build.bat: Compiles the BOF using MSVC on Windows and automatically copies the artifacts to the bin directory.
  • build.sh: Compiles the BOF using MinGW-w64 GCC on Linux/macOS and automatically copies the artifacts to the bin directory.
  • tests/tests.sln: A Visual Studio solution for debugging BOF code locally.

Features

This template offers the following out-of-the-box features:

  • Multi-Compiler Support: Supports both GCC and MSVC compilation without manual code adjustments for compatibility.
  • Source-Level Debugging: Allows direct debugging of BOF logic in Visual Studio, eliminating the need to repeatedly load it into Beacon for testing.
  • Automatic Import Resolution: No need to manually declare KERNEL32$CloseHandle or use DFR macros. The tool automatically parses standard Win32 API calls and modifies the obj file to comply with the BOF format.
  • Intrinsic Function Injection: Automatically handles compiler-generated internal function calls (e.g., memset, memcpy, __chkstk, 64-bit integer division). The tool injects implementations of these functions directly into the obj file, resolving "symbol not found" errors.
  • Logging System: Provides a comprehensive logging macro system (PRINTF, TRACEF) that supports viewing output in debuggers (x64dbg/WinDbg), making it easier to pinpoint issues before Beacon crashes.
  • Privacy Protection: Automatically erases source file path information from the obj file after compilation to prevent sensitive path leakage.
  • BOF Linter: Built-in static analysis tool that automatically checks the generated obj file for BOF compliance (e.g., unresolved symbols, illegal relocation types) to prevent load failures.

Usage

1. Writing a BOF

  • Code Location: Write your code in src/mybof.cc (C++) and src/mybof.h.
  • Script Location: Write the accompanying Aggressor Script in src/mybof.cna.

Caution

BOFs support a maximum of 32 imported functions (excluding those provided by Beacon itself). Calling more than 32 external APIs will result in execution failure: no slot for function (reduce number of Win32 APIs called).

Important

All non-inline functions must be wrapped within an extern "C" block; otherwise, Cobalt Strike cannot resolve the C++ mangled function names. mybof.cc already wraps all functions within extern "C" by default.

Note

The name mybof can be changed. Open Visual Studio Code, search for mybof, and batch replace it with your desired name. Then, rename the mybof.xxx files in the file explorer one by one.

Note

Use PRINTF and PRINTF_ERR macros to output info and error logs, and use the TRACEF macro to output debug logs. The prototypes of these three macros are the same as printf.

2. Compiling the BOF

  • Windows (MSVC): Install Visual Studio 2022 and run build.bat. The script will automatically detect the environment and compile.
  • Linux (GCC): Install MinGW-w64 and run build.sh.

The compilation artifacts (.o/.obj and .cna) will be automatically generated in the bin/ directory.

3. Debugging the BOF

It is recommended to use Visual Studio 2022 for local debugging:

  1. Open tests/tests.sln.
  2. tests.c simulates the Beacon loader and will directly call the BOF entry point.
  3. Press F5 to start debugging with breakpoints.

Principle: In the tests project, the BOF macro is undefined. The code is compiled in standard application mode, and log macros are forwarded to standard printf, enabling local execution.

4. Loading and Usage

Load bin/mybof.cna in Cobalt Strike to use the mybof command.

Beacon Online Debugging: If the BOF crashes while running in Beacon, you can set $use_debug_bof to true in mybof.cna. This enables TRACEF output and sends all logs via OutputDebugStringA. You can attach a debugger (x64dbg/WinDbg) to the Beacon process to view detailed logs.


bof-template-ng Principles

The core of this template lies in the post-processing tool (fixbof) located in the tools/ directory. It parses and modifies the .obj file after compilation to ensure it complies with the Beacon Object File specification.

Core Architecture

  1. Object File Parsing (tools/obj.cc): Implements a complete parser for the COFF (Common Object File Format), including the file header, section table, relocations, symbol table, and string table. This allows the tool to read and modify binary object files in a structured way.

  2. BOF Fix Logic (tools/fixbof.cc): This is the core processing unit, performing the following tasks:

    • Automatic API Import: Scans for undefined external symbols. If a symbol matches a known Win32 API (via the apis.txt database), the tool automatically renames it to the __imp_Module$Function format required by Beacon.
      • If the code makes a direct call (e.g., CloseHandle(...)), the tool generates a Thunk (jump stub) in the .text section to redirect the call to the imported symbol.
      • If the code calls via a pointer, the symbol name is directly modified to the import format.
    • Intrinsic Function Injection: Compilers often generate implicit calls, such as memcpy, memset, or the stack check function __chkstk. fixbof extracts the binary code for these functions from pre-compiled libraries and appends it directly to the BOF code section. It also fixes relevant symbol references, effectively "statically linking" these dependencies to avoid runtime "symbol not found" errors.
    • Path Erasure: Iterates through debug sections to find and overwrite strings containing source file paths, protecting developer privacy.

Common Issues and Tips

When writing BOFs, you may encounter the following common issues (some are automatically handled by this template, while others require attention):

  1. Import Function Limit: BOFs support a maximum of 32 dynamic import functions (excluding Beacon APIs). If you call more Win32 APIs than this limit, you will get a no slot for function error. Please streamline your code or resolve function addresses dynamically.

  2. Global Variable Initialization: In older versions of Cobalt Strike (< 4.7), global variables had to be initialized to a non-zero value; otherwise, they would be placed in the BSS section, causing load failures. Although newer versions have fixed this, it is good practice to initialize them or prefer stack variables.

  3. Switch Statement Issues: Large switch statements may cause the compiler to generate jump tables and place them in the .rdata section, which can lead to relocation issues in some cases. If you encounter problems, try using if-else structures instead.

  4. Stack Space Limit (__chkstk): When a function's local variables exceed 4KB (the default page size on Windows), the compiler inserts a __chkstk call to extend the stack. [Automatically Resolved]: This template automatically injects the __chkstk implementation, so you can safely use larger stack variables.

  5. C++ Compatibility:

    • MSVC vs GCC: MSVC-generated obj files are typically larger than GCC's, mainly due to debug information and alignment. The actual code section size transmitted to Beacon is not significantly different.
    • Static Functions: When using MSVC, try to avoid using the static keyword for functions, as it may cause symbol resolution errors (Unknown symbol). GCC does not have this issue.
    • Intrinsic Functions: When performing 64-bit integer operations (like division) on 32-bit systems, the compiler calls helper functions like _alldiv. [Automatically Resolved]: This template includes logic to inject these functions.
  6. GCC Built-in Functions: GCC often fails to generate standard __imp_ import prefixes for functions like memcpy, memset, strcmp, and strlen, and may even forcibly replace custom implementations with built-ins. [Automatically Resolved]: The fixbof tool detects these cases and automatically injects the necessary function implementations without requiring developer intervention.

  7. String Constant Pooling: When compiling with MSVC, the default string pooling optimization (/GF) can generate complex symbol references. [Automatically Resolved]: The build script defaults to adding the /GF- option to disable this behavior.

  8. COMDAT Sections: When compiling with MSVC, the compiler may place each function in a separate COMDAT section by default, leading to a complex obj file structure. [Automatically Resolved]: The build script defaults to adding the /Gy- option to force grouping of same-type data into the same section.

  9. Undefined Symbols and Format Check: Traditional development often requires manually checking the UNDEF section of the obj file to find improperly imported functions. [Automatically Resolved]: The built-in BOF Linter automatically scans the obj file after compilation, checking for unresolved symbols, illegal relocation types, or duplicate section names to ensure the artifact is valid.

About

Next-Generation BOF Template | BOF Linter | Obj Rewriter

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors