CobaltStrike逆向学习系列(3):Beacon C2Profile 解析
字数 1519 2025-08-07 08:22:12

CobaltStrike Beacon C2Profile 解析技术文档

0x00 概述

本文档详细解析CobaltStrike中Beacon的C2Profile配置机制,涵盖Controller端生成和Beacon端解析的全过程。通过深入理解C2Profile的存储和解析原理,可以更好地进行工具定制和检测规避。

0x01 Controller端生成机制

1.1 核心代码位置

  • 主要逻辑位于beacon/BeaconPayload.javaexportBeaconStage方法
  • 最终将settings转换为byte数组,经过混淆后Patch到Beacon中

1.2 数据结构构建

Controller使用三种方法构建C2Profile数据结构:

  1. addShort方法

    • 添加短整型数据(2字节)
    • 结构:[索引(2字节)][类型标记1(1字节)][长度2(2字节)][数据(2字节)]
  2. addInt方法

    • 添加整型数据(4字节)
    • 结构:[索引(2字节)][类型标记2(1字节)][长度4(4字节)][数据(4字节)]
  3. addData/addString方法

    • 添加字符串/二进制数据
    • 结构:[索引(2字节)][类型标记3(1字节)][长度N(4字节)][数据(N字节)]

1.3 类型标记常量

Controller定义了三个关键常量标识数据类型:

private static final short TYPE_SHORT = 1;
private static final short TYPE_INT = 2;
private static final short TYPE_PTR = 3;

1.4 完整数据结构

Controller生成的C2Profile采用以下结构:

[索引(2字节)][类型(1字节)][长度(2/4字节)][数据(...)]

0x02 Beacon端解析机制

2.1 加载流程

  1. Loader阶段

    • 通过ReflectiveLoader加载Beacon DLL
    • 首次调用DllMain时fdwReason=1(解析C2Profile)
    • 第二次调用fdwReason=4(执行核心功能)
  2. 内存准备

    • 申请0x800大小的内存区域
    • 使用0x2E异或解密C2Profile数据
    • 设置解析结构体,存储C2Profile地址和大小

2.2 解析过程

  1. 索引解析

    • 检查剩余大小是否≥2字节
    • 读取2字节索引值(小端序)
    • 偏移位置+2,总大小-2
  2. 类型和长度解析

    • 读取1字节类型标记
    • 根据类型读取长度(2或4字节)
    • 类型标记与Controller端定义一致
  3. 内存对齐处理

    • 索引值×16得到存储偏移(内存对齐)
    • 将类型标记存入对应位置
  4. 数据存储

    • Short/Int类型:直接读取对应字节数
    • Ptr类型
      • 根据长度申请内存
      • 检查剩余数据是否足够
      • 内存拷贝数据到申请的空间

2.3 关键数据结构

Beacon端使用以下结构存储解析后的C2Profile:

struct C2ProfileEntry {
    short index;    // 索引×16后的偏移
    char type;      // 数据类型标记
    union {
        short s_val;
        int i_val;
        void* p_val; // 指向动态分配的数据
    } data;
};

0x03 内存布局对比

3.1 Controller端布局

+--------+--------+--------+----------------+
| 索引   | 类型   | 长度   | 数据            |
| (2B)   | (1B)   | (2/4B) | (变长)          |
+--------+--------+--------+----------------+

3.2 Beacon端布局

+---------------------+--------+------------------+
| 对齐偏移(索引×16)   | 类型   | 数据/指针        |
+---------------------+--------+------------------+

0x04 技术要点总结

  1. 索引作用

    • 用于快速定位配置项
    • Beacon端通过索引×16实现内存对齐访问
  2. 类型系统

    • 三种基本数据类型覆盖所有配置需求
    • 动态内存分配处理变长数据
  3. 安全设计

    • 解析前异或解密(0x2E)
    • 严格的长度检查防止越界
    • 解析完成后清零敏感内存
  4. 扩展性

    • 通过索引系统可方便添加新配置项
    • 类型系统支持未来扩展新数据类型

0x05 检测与规避

  1. 检测点

    • 内存中的C2Profile特征(如0x2E异或)
    • 特定索引位置的已知配置值
    • 解析过程中的内存分配模式
  2. 规避建议

    • 修改默认异或密钥
    • 自定义索引映射关系
    • 实现替代的解析逻辑
    • 混淆内存中的数据结构

0x06 参考实现

以下是简化的C2Profile解析伪代码:

void parse_c2profile(void* profile_data, int total_size) {
    while (total_size >= 2) {
        short index = read_short(&profile_data, &total_size);
        char type = read_byte(&profile_data, &total_size);
        
        int entry_offset = index * 16;
        *(char*)(entry_offset) = type;
        
        switch(type) {
            case 1: // SHORT
                short s_val = read_short(&profile_data, &total_size);
                *(short*)(entry_offset + 4) = s_val;
                break;
                
            case 2: // INT
                int i_val = read_int(&profile_data, &total_size);
                *(int*)(entry_offset + 4) = i_val;
                break;
                
            case 3: // PTR
                int data_size = read_int(&profile_data, &total_size);
                void* p_val = malloc(data_size);
                memcpy(p_val, profile_data, data_size);
                profile_data += data_size;
                total_size -= data_size;
                *(void**)(entry_offset + 4) = p_val;
                break;
        }
    }
}

通过深入理解C2Profile的生成和解析机制,安全研究人员可以更有效地分析CobaltStrike流量和行为,同时为工具定制提供理论基础。

CobaltStrike Beacon C2Profile 解析技术文档 0x00 概述 本文档详细解析CobaltStrike中Beacon的C2Profile配置机制,涵盖Controller端生成和Beacon端解析的全过程。通过深入理解C2Profile的存储和解析原理,可以更好地进行工具定制和检测规避。 0x01 Controller端生成机制 1.1 核心代码位置 主要逻辑位于 beacon/BeaconPayload.java 的 exportBeaconStage 方法 最终将settings转换为byte数组,经过混淆后Patch到Beacon中 1.2 数据结构构建 Controller使用三种方法构建C2Profile数据结构: addShort方法 添加短整型数据(2字节) 结构: [索引(2字节)][类型标记1(1字节)][长度2(2字节)][数据(2字节)] addInt方法 添加整型数据(4字节) 结构: [索引(2字节)][类型标记2(1字节)][长度4(4字节)][数据(4字节)] addData/addString方法 添加字符串/二进制数据 结构: [索引(2字节)][类型标记3(1字节)][长度N(4字节)][数据(N字节)] 1.3 类型标记常量 Controller定义了三个关键常量标识数据类型: 1.4 完整数据结构 Controller生成的C2Profile采用以下结构: 0x02 Beacon端解析机制 2.1 加载流程 Loader阶段 : 通过ReflectiveLoader加载Beacon DLL 首次调用DllMain时fdwReason=1(解析C2Profile) 第二次调用fdwReason=4(执行核心功能) 内存准备 : 申请0x800大小的内存区域 使用0x2E异或解密C2Profile数据 设置解析结构体,存储C2Profile地址和大小 2.2 解析过程 索引解析 : 检查剩余大小是否≥2字节 读取2字节索引值(小端序) 偏移位置+2,总大小-2 类型和长度解析 : 读取1字节类型标记 根据类型读取长度(2或4字节) 类型标记与Controller端定义一致 内存对齐处理 : 索引值×16得到存储偏移(内存对齐) 将类型标记存入对应位置 数据存储 : Short/Int类型 :直接读取对应字节数 Ptr类型 : 根据长度申请内存 检查剩余数据是否足够 内存拷贝数据到申请的空间 2.3 关键数据结构 Beacon端使用以下结构存储解析后的C2Profile: 0x03 内存布局对比 3.1 Controller端布局 3.2 Beacon端布局 0x04 技术要点总结 索引作用 : 用于快速定位配置项 Beacon端通过索引×16实现内存对齐访问 类型系统 : 三种基本数据类型覆盖所有配置需求 动态内存分配处理变长数据 安全设计 : 解析前异或解密(0x2E) 严格的长度检查防止越界 解析完成后清零敏感内存 扩展性 : 通过索引系统可方便添加新配置项 类型系统支持未来扩展新数据类型 0x05 检测与规避 检测点 : 内存中的C2Profile特征(如0x2E异或) 特定索引位置的已知配置值 解析过程中的内存分配模式 规避建议 : 修改默认异或密钥 自定义索引映射关系 实现替代的解析逻辑 混淆内存中的数据结构 0x06 参考实现 以下是简化的C2Profile解析伪代码: 通过深入理解C2Profile的生成和解析机制,安全研究人员可以更有效地分析CobaltStrike流量和行为,同时为工具定制提供理论基础。