BOF RunPE 是一个用于 Cobalt Strike 的 Beacon 对象文件,可在 beacon 进程内完全在内存中执行 PE 文件。与传统的 fork&run 不同,不会生成子进程,不会创建控制台,也不会使用管道——所有输出均通过 IAT 挂钩捕获并重定向到 beacon 控制台。
架构: 仅 x64
┌──────────────────────────────────────────────────────────────┐
│ Cobalt Strike Beacon │
│ (Current Process) │
└────────────────────────┬─────────────────────────────────────┘
│
│ beacon_inline_execute()
│
▼
┌──────────────────────────────────────────────────────────────┐
│ BOF RunPE │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ VxTable + Draugr Initialization │ │
│ │ (Syscall Resolution + Stack Spoofing) │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────────┐ │
│ │ PE Mapping │ │
│ │ - Section Copy - IAT Patching (with hooks) │ │
│ │ - Relocations - Memory Protection │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────────┐ │
│ │ Thread Execution │ │
│ │ - Spoofed Start Address │ │
│ │ - RIP Hijacking to Entry Point │ │
│ │ - Output Redirection via Hooks │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
printf/WriteConsole 挂钩捕获输出BOF 的行为可以在 Additionals postex -> RunPe Config 中编辑

| 方法 | 描述 |
|---|---|
None | 直接 API 调用 |
Draugr | 栈欺骗 API 调用 |
Regwait | RegisterWaitForSingleObject 回调 |
Timer | 定时器队列回调 |
| 方法 | 描述 |
|---|---|
Heap | 通过 Draugr 使用 RtlCreateHeap 的私有堆 |
VirtualAlloc | 通过 Draugr 使用 NtAllocateVirtualMemory |
Module stomping | 覆盖合法 DLL 的 .text 节区 |
| 选项 | 描述 |
|---|---|
AllocRWX | 以 RWX 分配(相对于 RW→RX 转换) |
UnhookNtdll | 用磁盘上的新副本替换 ntdll.dll 的 .text 节区 |
Timeout | 执行超时时间(毫秒,0 = 无限) |
StompModule | 用于模块踩踏的 DLL 路径(例如 chakra.dll) |
| 选项 | 描述 |
|---|---|
ModuleName | 用于起始地址的合法模块(例如 Kernel32.dll) |
ProcedureName | 模块内的函数名(例如 BaseThreadInitThunk) |
Offset | 距离函数起始的偏移 |
所有 PE 输出通过 IAT 挂钩重定向到 beacon 控制台。不会创建控制台窗口或命名管道。
| 挂钩的函数 | 目标 |
|---|---|
GetCommandLineA/W | 返回欺骗的参数 |
__getmainargs / __wgetmainargs | CRT 参数初始化 |
printf / wprintf | BeaconPrintf 重定向 |
WriteConsoleA/W | BeaconPrintf 重定向 |
__stdio_common_vfprintf | UCRT 打印函数 |
ExitProcess / exit | 转换为 ExitThread |
| 技术 | 绕过对象 |
|---|---|
| 间接系统调用 | 用户层 API 挂钩(EDR/AV) |
| Draugr 栈欺骗 | 调用栈检查 |
| 线程起始地址欺骗 | 线程起始地址分析 |
| 模块踩踏 | 无后备内存检测 |
| 私有堆分配 | VirtualAlloc 监控 |
| Ntdll 取消挂钩 | 用磁盘上的 Ntdll 覆盖内存中的 ntdll |
| IAT 挂钩(无管道) | 命名管道监控 |
NtGetContextThread / NtSetContextThread:
内存操作:
DONT_RESOLVE_DLL_REFERENCES 加载 DLL(如果内存分配器是模块踩踏)Cobalt Strike → Script Manager → Load → BOF_RunPe.cna
beacon> runpe /path/to/binary.exe --arg1 value1

beacon> help runpe

需要 GCC 13 (mingw-w64)。使用提供的 Dockerfile:
sudo docker build -t ubuntu-gcc-13 .
sudo docker run --rm -it -v "$PWD":/work -w /work ubuntu-gcc-13:latest make
输出:Bin/runpe.o
| 限制 | 描述 |
|---|---|
| CET | 控制流强制技术可能会阻止合成栈帧 |
| 仅 x64 | 不支持 x86/WoW64 |
| 内核可见性 | 线程创建对内核回调可见 |
| .NET | 不支持托管可执行文件 |
Windows Native API Programming by Pavel YosifovichWindows Internals, Part 1 by Pavel YosifovichWindows Internals, Part 2 by Andrea Allievi