This project implements a Beacon Object File (BOF) Runtime/Loader intended to be ran on Windows. It is heavily inspired by https://github.com/trustedsec/COFFLoader (almost all of the relocation code is from there).
The special feature of this loader compared to other loaders is that this implementation is suitable for use as position independent (shell)code ("PIC")! This is achieved by following these restrictions:
- No non-const globals are used. This means that there is no writable
.data
section that needs to be merged into the shellcode. - The functions of the C Standard Library are not used - the required functions are implemented by the loader itself. Other dependencies (e.g.
VirtualAlloc
) can (and must) be loaded outside the loader and passed to it. This makes it more flexible, since the function loading process can be done by different means. - Heap usage is avoided as far as possible. Most variables reside on the stack. This makes memory scans more difficult for EDR software.
Another difference to other loaders is the fact that the beacon API functions must be implemented by the user, not the loader itself. This makes it way easier (but also more time consuming) to integrate the loader in your own projects, e.g. your own C2 agent, and use the agent code within the API implementations.
The loader also allows the definition of an own function to resolve external functions from the Windows API. It does not do any LoadLibrary
or GetProcAddress
by itself.
The following Beacon APIs are currently supported (more can be added easily):
- Data Parser API
- Format API
- Output API
- Token API
- Utility functions
This project was part of the master's thesis written by Leon Schmidt:
@thesis{Schmidt2025,
author = {Schmidt, Leon},
title = {Enhancing Command & Control Capabilities: Integrating Cobalt Strike's Plugin System into a Mythic-based Beacon Developed at cirosec},
institution = {University of Applied Sciences Offenburg, Faculty EMI},
year = {2025},
language = {en}
}
The Loader resides within the "BOFLoader" Visual Studio Project and is built with clang-cl
as a static Windows library (.lib
). This means that linking takes place in your own application, not within the loader project!
The project "TestMain" is a small test program that uses the BOF loader project. It thus implements the compatibility layer functions and passes all external dependencies. It then loads a BOF from disk, and invokes the loader. The "TestMain" project can be used as a reference.
- Implement the beacon API functions
- Implement the resolver function (
ResolveFunc_t
) - Store function pointers to required Windows functions in
external_functions_ptr_t
- Store function pointers to beacon API implementations in
cs_compat_functions_ptr_t
- Prepare BOF argument string with
UnhexlifyArgs
- Generate the argument string with this script
- Execute the BOF with
RunBOF
, passing it the results fromUnhexlifyArgs
You can consult the "TestMain" project in this repo, where all these steps were also done.
#include "BOFLoader.h"
#include "ExternalFuncs.h"
#include "my_beacon_apis.h" // implement the beacon APIs here
// simple resolve function example for DFR
static void *ResolveFunc(const char *lib, const char *func) {
return GetProcAddress(LoadLibraryA(lib), func);
}
int execute_bof(const unsigned char *coff_file, int coff_size, const unsigned char *args) {
// External functions (e.g. use the one from Windows.h or resolve them manually)
external_functions_t external_functions = {
(VirtualAlloc_t)VirtualAlloc,
(VirtualFree_t)VirtualFree,
(HeapAlloc_t)HeapAlloc, // you can also use RtlAllocateHeap here
(HeapFree_t)HeapFree,
(GetProcessHeap_t)GetProcessHeap,
(LoadLibraryA_t)LoadLibraryA,
(GetModuleHandleA_t)GetModuleHandleA,
(GetProcAddress_t)GetProcAddress,
(FreeLibrary_t)FreeLibrary,
(ResolveFunc_t)ResolveFunc // this is the DFR resolv function
};
// CS Compat functions (a.k.a. beacon APIs)
cs_compat_functions_t compat_functions = {
(BeaconDataParse_t)BeaconDataParse,
(BeaconDataInt_t)BeaconDataInt,
(BeaconDataShort_t)BeaconDataShort,
(BeaconDataLength_t)BeaconDataLength,
// all other functions ...
(toWideChar_t)toWideChar
};
// Prepare arguments
int arg_size = 0;
unsigned char *prepared_args = UnhexlifyArgs(
&external_functions,
(unsigned char*)args,
&arg_size
);
// Invoke BOF runtime
int retcode = RunBOF(
&external_functions,
&compat_functions,
(char*)"go",
(unsigned char*)coff_file,
coff_size,
prepared_args,
arg_size
);
return retcode;
}
This project is licensed under the GNU Affero General Public License v3.0.
Parts of the code are based on COFFLoader by TrustedSec and used under a BSD-style license. All original rights and attributions remain with the respective authors. See third_party_licenses/
for details.