CobaltStrike逆向学习系列(3):Beacon C2Profile 解析
字数 1414 2025-08-29 08:30:36
CobaltStrike逆向学习系列(3):Beacon C2Profile解析
1. C2Profile概述
C2Profile是CobaltStrike中决定Beacon行为的关键配置文件,它包含了大量控制Beacon行为的参数。理解C2Profile的解析机制对于检测和绕过CobaltStrike具有重要意义。
2. Controller端分析
2.1 核心代码位置
分析位于beacon/BeaconPayload.java中的exportBeaconStage方法,这是处理C2Profile的关键方法。
2.2 数据结构构建
Controller使用四种方法构建C2Profile数据结构:
addShort- 添加短整型数据addInt- 添加整型数据addString- 添加字符串数据addData- 添加任意二进制数据
其中addString本质上调用的是addData方法。
2.3 数据结构格式
每个数据项的存储格式如下:
[索引(2字节)][类型标识(1字节)][长度(1/2/4字节)][数据内容]
类型标识定义:
- 1: Short类型
- 2: Int类型
- 3: Data/Ptr类型
3. Beacon端解析机制
3.1 加载流程
- Beacon由Loader加载执行
- 通过
CreateThread启动执行 - 调用
ReflectiveLoad加载Beacon DLL - DLLMain被调用两次:
- 第一次调用(fdwReason=1):解析C2Profile
- 第二次调用(fdwReason=4):执行核心功能
3.2 内存初始化
- 申请0x800大小的内存用于存储
- 对内存进行异或操作(使用0x2E作为密钥)
- 设置解析结构体:
- 存储两份C2Profile地址
- 存储两份C2Profile大小
3.3 解析流程
-
读取索引:
- 检查剩余大小是否≥2字节
- 读取2字节作为索引(index)
- 调整位置偏移和剩余大小
-
读取类型和大小:
- 读取1字节类型(type)
- 根据类型读取大小:
- Short: 2字节
- Int: 4字节
- Ptr: 4字节(长度)
-
数据存储:
- 计算存储位置:
index*16(实现内存对齐) - 根据类型处理数据:
- Short/Int: 直接存储值
- Ptr:
- 根据size申请内存
- 检查剩余数据是否足够
- 拷贝数据到新内存
- 存储指针
- 计算存储位置:
-
结束条件:
- 当剩余大小<2字节时,解析结束
- 最后将解析区域清零
4. 关键数据结构
4.1 Controller端结构
// 添加Short示例
settings.addShort(index, value);
// 底层实现
void addShort(int index, short value) {
addShort(index, (int)value);
}
void addShort(int index, int value) {
this.addInt(index, 1, 2, value);
}
4.2 Beacon端结构
Beacon中最终存储的结构与Controller端不同,采用索引对齐方式:
+------------+------------+------------+
| 索引0数据 | 索引1数据 | 索引2数据 |
| (16字节) | (16字节) | (16字节) |
+------------+------------+------------+
每个索引位置存储:
- 类型标识
- 实际数据(或指针)
5. 检测与绕过
5.1 检测点
-
内存特征:
- 初始的AAAABBBBCCCCDDDD特征
- 4096的内存大小
- 0x2E的异或操作
-
解析模式:
- 特定的索引对齐方式
- 类型标识的固定值
5.2 绕过思路
- 修改初始内存特征
- 改变异或密钥
- 调整内存对齐方式
- 自定义类型标识值
6. 总结
C2Profile的解析机制展示了CobaltStrike如何高效地配置和控制Beacon行为。理解这一机制不仅有助于分析CobaltStrike的工作原理,也为检测和防御提供了关键切入点。Controller和Beacon端使用不同的结构表示C2Profile,但通过索引和类型系统保持一致性,这种设计既保证了灵活性又提高了效率。