Cobaltstrike4.0 —— shellcode分析
字数 2162 2025-08-06 18:07:56
Cobalt Strike 4.0 Shellcode 深度分析
0x01 概述
本文详细分析Cobalt Strike 4.0生成的shellcode工作原理,涵盖从初始shellcode执行到最终Beacon加载的全过程。
0x02 Shellcode分析流程
一、Shellcode生成
- 通过Cobalt Strike生成Payload(1600字节)
- 创建加载器(Stager Loader)执行该Payload
二、Shellcode Loader实现
#include <windows.h>
int main(void) {
unsigned char buf[] = {payload_bytes};
HANDLE myHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
void* exec = HeapAlloc(myHeap, HEAP_ZERO_MEMORY, sizeof(buf));
memcpy(exec, buf, sizeof(buf));
((void(*)())exec)();
return 0;
}
编译注意事项:
- 关闭优化、安全检查、随机基址等选项
- 使用x86 Release模式生成
三、Shellcode执行流程分析
第一阶段:Stager
-
初始执行:
- 通过
pop ebp获取EIP - 压入特征码和参数("wininet"和特征码726774C)
- 通过
-
模块遍历:
- 通过FS寄存器获取TEB→PEB→PEB_LDR_DATA→InMemoryOrderLinks
- 遍历模块列表(_LDR_DATA_TABLE_ENTRY结构)
- 获取模块基址(结构体0x18偏移处)
-
特征码计算:
; 模块名特征算法 1. 对每个字符判断,如果大于0x60就减0x20(小写转大写) 2. 累加前将上一次结果循环右移14位 3. 累加当前字符值 -
函数查找:
- 找到导出表(PE头0x78偏移)
- 遍历导出函数名,计算特征值(算法同上但无大小写转换)
- 比较模块特征值+函数特征值与目标特征码
-
关键函数获取:
- 找到的9个关键wininet函数:
- InternetOpenA (A779563A)
- InternetConnectA (C69F8957)
- HttpOpenRequestA (3B2E55EB)
- HttpSendRequestA (7B18062D)
- GetDesktopWindow (315E2145)
- InternetErrorDlg (0BE057B7)
- VirtualAlloc (E553A458)
- InternetReadFile (E2899612)
- 找到的9个关键wininet函数:
-
Beacon下载:
- 建立与C2服务器的连接
- 发送GET请求
- 使用VirtualAlloc开辟内存
- InternetReadFile读取响应内容并执行
第二阶段:Beacon解密
- 解密过程:
- 从偏移46开始的内容与固定3D偏移内容进行4字节异或
- 解密长度:0x33000字节
- 解密后得到PE文件(起始标志4D5A)
第三阶段:反射加载
-
PE执行:
- 通过
pop edi; jmp edi跳转到解密数据45偏移处 - 使用Reflective DLL Injection技术
- 通过
-
ReflectiveLoader函数:
- 定位PE头部
- 获取kernel32模块基址(特征码6A4ABC5B)
- 查找6个关键函数:
- GetProcessAddress
- GetModuleHandleA
- LoadLibraryA
- LoadLibraryExA
- VirtualAlloc
- VirtualProtect
-
DLL加载过程:
- 文件格式到内存格式的拉伸
- 修复导入表(IAT)
- 修复重定位表(按页处理,每页0x1000大小)
-
DLL入口调用:
- 调用DllEntryPoint(非直接DllMain)
- 首次调用fdwReason=1(初始化)
- 第二次调用fdwReason=4
第四阶段:DllMain分析
-
初始化阶段(fdwReason=1):
- 解密配置数据(循环0x1000次)
- 还原C2配置信息(IP、端口、UA、心跳时间等)
-
主逻辑阶段(fdwReason=4):
- 通过VirtualQuery检查内存页面属性
- 建立心跳循环:
- 使用InternetOpen、InternetConnect等建立连接
- 发送心跳请求
- 通过InternetReadFile获取响应
- 命令分发:
- 根据响应类型执行不同操作
- Beacon内置100种任务类型
0x03 关键技术点
一、特征码算法
目的:
- 缩短shellcode长度
- 对抗静态分析
模块名特征算法:
def calc_hash(name):
hash_val = 0
for c in name:
if c > 0x60: # 小写字母
c -= 0x20 # 转大写
hash_val = ((hash_val >> 14) | (hash_val << (32 - 14))) & 0xFFFFFFFF
hash_val = (hash_val + c) & 0xFFFFFFFF
return hash_val
二、Windows R3模块基址获取
通过FS寄存器链式查找:
- FS[0] → TEB
- TEB → PEB
- PEB → PEB_LDR_DATA
- PEB_LDR_DATA → InMemoryOrderLinks
- 遍历_LDR_DATA_TABLE_ENTRY结构获取模块信息
三、检测思路
流量侧检测:
- Beacon下载请求特征:
- 满足checksum8算法
- 返回约20000字节
- Beacon内容解密:
- 固定异或解密方式
- 心跳流量特征:
- URI模式
- 元数据传输字段
- TLS特征:
- 默认证书
- 特定握手模式
主机侧检测:
- 内存扫描:
- 查找Beacon初始化解密的数据模式
- Shellcode行为检测:
- FS寄存器模块查找
- 反射加载行为
- 动态加载检测:
- 非常规DLL加载方式
0x04 完整执行流程总结
-
初始Shellcode执行:
- 动态解析API
- 下载Beacon
-
Beacon处理:
- 解密PE文件
- 反射加载DLL
-
DLL初始化:
- 解密配置
- 建立通信
-
主循环:
- 心跳机制
- 命令执行
附录:关键数据结构
_LDR_DATA_TABLE_ENTRY
struct _LDR_DATA_TABLE_ENTRY {
struct _LIST_ENTRY InLoadOrderLinks; //0x0
struct _LIST_ENTRY InMemoryOrderLinks; //0x8
struct _LIST_ENTRY InInitializationOrderLinks; //0x10
VOID* DllBase; //0x18
VOID* EntryPoint; //0x1c
ULONG SizeOfImage; //0x20
struct _UNICODE_STRING FullDllName; //0x24
struct _UNICODE_STRING BaseDllName; //0x2c
//...
};
PE导出表结构
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
重定位表结构
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// Followed by WORD TypeOffset[1]
} IMAGE_BASE_RELOCATION;
注:本文分析基于Cobalt Strike 4.0版本,不同版本实现细节可能有所差异。