欢迎加入我的知识星球,目前正在更新免杀相关的东西,129/永久,每100人加29,每周更新2-3篇上千字PDF文档。文档中会详细描述。目前已更新82+ PDF文档
加好友备注(星球)!!!
一些资源的截图:
简介
CobaltStrike Bof的全称是Beacon Object Files,Bof是一种在目标系统上执行自定义代码的机制,Bof是用C编写的小型程序。
Bof是一种通过将自定义的C代码编译成对象文件并通过CobaltStrike的Beacon在目标系统内存中加载和执行的机制。
什么是Bof
Bof在CobaltStrike中是一种插件的基址,它允许用户在Beacon上指定自定义的C代码,Bof文件是通过将C代码编译为对象文件,也就是.o文件。
这些对象文件可以通过CobaltStrike的Beacon模块去加载和执行。
构建环境
构建环境非常简单,如下Bof模版。
https://github.com/securifybv/Visual-Studio-BOF-template/releases
点击创建。
创建之后可能会报错找不到Source.cpp文件,这可能是因为下载的这个模版中没有这个文件。
我们点击确认。
然后将其Source.cpp文件移除掉。
#include <windows.h>
#include <stdio.h>
#include "bofdefs.h"
void go(char* buff, int len) {
BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
}
void main(int argc, char* argv[]) {
}
inline-execute source.obj
理解代码
那么我们接下来就来尝试去理解Bof代码。
首先我们要解释的是bofdefs.h头文件,这个头文件中包含了CobaltStrike Bof特定的函数和宏定义。
而go函数是Bof的入口函数,CobaltStrike在加载和执行Bof的时候会去调用这个函数。
正如上图所见,我们打印出了Hello World。
void go(char* buff, int len) {
BeaconPrintf(CALLBACK_OUTPUT, "Hello world");
}
调用BeaconPrintf函数会在Beacon控制台中输出Hello World字符串,至于CALLBACK_OUTPUT标记表示这是一个普通的输出类型。
那么我们来详细看一下这个函数的原型,这个函数是CobaltStrike中提供的一个API函数,用于在控制台中输出信息。
如下原型:
voidBeaconPrintf(int type, constchar *fmt, ...);
type参数表示的是输出信息的类型,一般的值为CALLBACK_OUTPUT表示普通的输出信息,而CALLBACK_ERROR表示错误信息。
fmt是格式化字符串,类似于printf函数,后面的三个点是一个可变参数,表示输出的实际数据。
那么如果我们想格式化输出值的话,我们只需要给定一个变量以及%d占位符即可。
如下代码:
#include <windows.h>
#include <stdio.h>
#include "bofdefs.h"
void go(char* buff, int len) {
int number = 42;
BeaconPrintf(CALLBACK_OUTPUT, "The number is %d", number);
}
void main(int argc, char* argv[]) {
}
那么我们来学习一下其他的函数。
常见函数学习
BeaconDataParse
BeaconDataParse函数是用于初始化数据解析器的一个函数,它会将传递给Bof的原始数据和解析器关联起来,以便后续的函数来提取各种类型的数据。
voidBeaconDataParse(datap *parser, char *data, int length);
parser参数指向了一个datap的指针,用于保存解析器的状态。
data参数是传递给Bof的原始数据。
length参数是数据的长度。
我们来看一下datap结构,这个结构是用于管理和解析数据的内部结构,BeaconDataParse函数将数据和长度与datap结构关联了起来。
这个函数一般会和BeaconDataInt,BeaconDataShort,等函数来配合使用。
这里我们演示的话就使用BeaconDataint吧。
如下代码:
#include <windows.h>
#include "beacon.h"
void go(char* args, int length) {
// 初始化解析器
datap parser;
BeaconDataParse(&parser, args, length);
// 提取整数值
int value1 = BeaconDataInt(&parser);
int value2 = BeaconDataInt(&parser);
// 输出提取的值
BeaconPrintf(CALLBACK_OUTPUT, "this is number: %d, %d", value1, value2);
}
void main(int argc, char* argv[]) {
}
其实就是在CobaltStrike去运行Bof的时候传递的值会在这里进行接收。
那么接下来这这几种方式其实也是一样的。
都是用来接收参数的。
BeaconFormatAlloc
BeaconFormatAlloc函数用于初始化和分配一个formatp结构的,formatp结构用户构建动态缓冲区,可以在Bof中格式化和存储数据。
函数原型如下:
DECLSPEC_IMPORT void __cdecl BeaconFormatAlloc(formatp *format, int maxlen);
format参数指向formatp结构的指针,这个结构指针将被初始化和分配。
Maxlen参数指向动态缓冲区的初始最大长度。
如下使用方式:
#include <windows.h>
#include <stdio.h>
#include "beacon.h"
void go(char *args, int length) {
formatp buffer;
BeaconFormatAlloc(&buffer, 128); // 初始化并分配一个最大长度为128的formatp结构
BeaconFormatPrintf(&buffer, "Hello: %d", 123);
char *output = (char *)buffer.buffer;
BeaconPrintf(CALLBACK_OUTPUT, "%s", output);
BeaconFormatFree(&buffer);
}
void main(int argc, char *argv[]) {
}
BeaconIsAdmin
BeaconIsAdmin函数是用于检查当前线程是否是以管理员的权限去运行的,比如说如果我们想去添加用户,那么我们首先肯定是需要去判断当前的线程是否是以管理员去运行的。
函数原型如下:
DECLSPEC_IMPORT BOOL __cdecl BeaconIsAdmin();
如果当前线程以管理员权限运行的,那么返回TRUE。
如果当前线程不是以管理员权限运行的,那么返回FALSE。
如下代码:
#include <windows.h>
#include <stdio.h>
#include "beacon.h"
void go(char *args, int length) {
if (BeaconIsAdmin()) {
BeaconPrintf(CALLBACK_OUTPUT, "管理员运行的线程");
} else {
BeaconPrintf(CALLBACK_OUTPUT, "不是管理员运行的线程");
}
}
void main(int argc, char *argv[]) {
}
原文始发于微信公众号(Relay学安全):CobaltStrike Bof开发(1)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论