- 网络安全
【免费下载链接】Havoc
The Havoc Framework
Demon 是 Havoc 框架(Havoc Framework)中的 Windows 端植入体(Agent),其源码完全使用 C 语言与汇编编写,位于仓库的 payloads/Demon 目录。本文以该目录下的 README.md 为核心骨架,结合源码逐目录拆解 Demon 的设计:从汇编层的返回地址栈欺骗(Return Address Stack Spoofing),到核心层的动态 API 解析与系统调用(Syscall)封装,再到加密、注入与三种 PE 入口点的实现,并澄清构建系统的正确使用方式。读完本文,你将能够按图索骥地理解 Havoc 客户端载荷的完整代码组织,掌握每个源文件在 Agent 生命周期中所承担的角色,并知道如何配合 teamserver 构建器 将源码编译为可用的载荷。
一、总体概览:一个"自给自足"的 Windows Agent
payloads/Demon/README.md 开宗明义:Demon Agent 的源码由 C 与汇编构成,其核心职责是与 Teamserver(C2 服务端)建立连接、接收并执行任务。从 src/Demon.c 可以看出,Agent 的启动流程被设计为四步:
- 初始化指针、模块与 Win32 API(
DemonInit); - 初始化元数据(
DemonMetaData); - 解析内嵌配置(
DemonConfig); - 进入主连接与任务循环(
DemonRoutine)。
整个源码按功能划分为五个目录,README 中给出了它们各自的职责:
| 目录 | 职责 |
|---|---|
| src/asm | 汇编代码(返回地址栈欺骗) |
| src/core | 核心功能(连接服务端、动态加载 Win32 API / 系统调用) |
| src/crypt | 加解密函数 |
| src/inject | 注入函数与工具 |
| src/main | PE 可执行文件的入口点 |
此外,include 目录集中存放全部头文件,其中 Demon.h 定义了贯穿整个 Agent 的全局实例结构体INSTANCE——它集中管理会话信息(Session)、配置(Config)、动态解析的函数指针表(Win32)、系统调用表(Syscall)、已加载模块基址(Modules)以及各类链表(令牌、作业、下载、Socket、Pivot、COFF 等)。理解 Demon 的代码组织,本质上是理解这个INSTANCE结构体被各模块如何读写。
二、src/asm:汇编层与返回地址栈欺骗
README 明确指出 src/asm 存放的是"return address stack spoofing"(返回地址栈欺骗)汇编代码。目录内按架构拆分为四个文件:
- Spoof.x64.asm / Spoof.x86.asm:负责在调用敏感函数时伪造栈上的返回地址,使调用链溯源(例如 ETW、EDR 的栈回溯)难以还原真实的函数调用来源;
- Syscall.x64.asm / Syscall.x86.asm:提供
syscall指令的直通与间接调用桩。
这两个主题在核心层均有对应支撑:Demon.h的Config.Implant中有StackSpoof布尔开关,而Syscall结构体保存了间接系统调用所需的SysAddress(syscall指令地址)与各Nt*函数的 Service Number(SSN)。构建时,汇编对象会由 NASM 先编译成.o,再与 C 源码一起链接进最终载荷(见下文构建章节)。
三、src/core:Agent 的心脏——连接、API 解析与主循环
src/core 是源码体量最大的目录,README 将其职责概括为"连接服务端、动态加载 Win32 API / 系统调用"。从 Demon.c 与 CMakeLists.txt 的源文件清单看,核心层覆盖了二十余个 C 文件,本节按运行顺序拆解其关键实现。
3.1 DemonMain 与主循环(DemonRoutine)
src/Demon.c 定义了 Agent 的调度骨架。DemonMain在栈上分配INSTANCE后依次调用DemonInit、DemonMetaData、DemonRoutine。其中DemonRoutine是一个永不返回的循环:
_Noreturn VOID DemonRoutine() { for ( ;; ) { if ( ! Instance->Session.Connected ) { if ( TransportInit() ) { ... } /* 连接监听器 */ } if ( Instance->Session.Connected ) { CommandDispatcher(); /* 任务队列派发 */ } SleepObf(); /* 睡眠(按配置加密内存) */ } }这个循环揭示了 Agent 的核心行为模式:未连接则尝试通过TransportInit建立通道;已连接则进入任务派发;每次循环末尾都执行SleepObf(睡眠混淆,见 core/SleepObf.h)。TransportInit的语义在 core/Transport.h 中定义为"建立到 C2 服务器的 HTTP/HTTPS 连接或到父级 Pivot 的 SMB 连接,并发送收集到的主机信息"。
3.2 动态加载 Win32 API 与系统调用
DemonInit是整个 Agent 的"地基"阶段,其核心工作是不依赖导入表地解析所需函数:
- 通过 PEB(进程环境块)定位
ntdll.dll、kernel32.dll等模块基址(LdrModulePeb); - 再用
LdrFunctionAddr按哈希查找函数地址(哈希由 scripts/hash_func.py 生成),填充Instance->Win32中数百个函数指针。
这一点在Demon.h的函数指针表中体现得淋漓尽致:从NtAllocateVirtualMemory、NtProtectVirtualMemory等原生 API,到WinHttpOpen、CreateProcessW、NetUserEnum、SafeArrayCreate、LsaCallAuthenticationPackage等跨模块 API,全部以WIN_FUNC宏声明为结构体成员,运行时逐一解析。
系统调用侧,DemonInit在解析完基础 API 后检查Config.Implant.SysIndirect:若开启间接系统调用,则调用SysInitialize从 ntdll 中提取每个所需Nt*函数的syscall指令地址与 SSN,填充Instance->Syscall(对应 core/SysNative.c 与 core/Syscalls.c)。
3.3 内嵌配置解析(DemonConfig)
Agent 的运行时配置(睡眠时间、抖动、注入方式、C2 地址等)并不是运行时从外部读取,而是在构建期由 Teamserver 序列化后以内嵌字节数组的形式编译进载荷:源码中的AgentConfig[]由宏CONFIG_BYTES填充(默认值见 CMakeLists.txt 的add_compile_definitions( CONFIG_BYTES={} ))。
DemonConfig使用 core/Parser.c 提供的解析器按固定顺序读取这些字节,依次还原出:
Sleeping/Jitter(睡眠秒数与抖动百分比);- 内存分配 / 执行技术(
Memory.Alloc/Memory.Execute); Spawn64/Spawn86(注入用派生进程路径,如 notepad.exe);- 植入体选项:
SleepMaskTechnique(睡眠混淆技术)、SleepJmpBypass、StackSpoof(栈欺骗)、ProxyLoading(模块代理加载)、SysIndirect(间接系统调用)、AmsiEtwPatch(AMSI/ETW 补丁); - 传输层选项(
TRANSPORT_HTTP编译开关下):KillDate(终止日期,若当前时间已超过则直接退出线程)、WorkingHours(工作时间窗口)、HTTPMethod、HostRotation(主机轮换策略)、主机列表、Secure(SSL)、UserAgent、HTTPHeaders、Uris以及可选的Proxy代理(URL / 用户名 / 密码); - (
TRANSPORT_SMB编译开关下)管道名Name与KillDate、WorkingHours。
值得注意的默认值:HTTP 传输下下载分块大小DownloadChunkSize为0x80000(512 KB),SMB 传输下为0xfc00(约 63 KB,源码注释说明需小于PIPE_BUFFER_MAX,即 Transport.h 中定义的0x10000)。
3.4 元数据上报(DemonMetaData)
连接建立后,Agent 需要向 Teamserver 上报主机信息。src/Demon.c 的DemonMetaData先通过PackageCreateWithMetaData( DEMON_INITIALIZE )创建数据包,随后:
- 生成 32 字节 AES Key 与 16 字节 IV(通过
RandomNumber32),并作为包首部字段发送——这构成了后续通信的会话密钥; - 依次写入:Agent ID、计算机名、用户名、域名、内网 IP、进程路径、PID/TID/PPID、进程架构、是否管理员(
BeaconIsAdmin)、模块基址、操作系统版本信息(RtlGetVersion的 Major/Minor、产品类型、SP、Build)、OS 架构、睡眠与抖动、KillDate、WorkingHours。
源码注释给出了该包的完整内存布局(Header + MetaData),字段长度与顺序与 Teamserver 侧的 parser 严格对应。
四、src/crypt:AES-256 通信加密
README 将 src/crypt 概括为"加密/解密函数"。该目录的核心是 AesCrypt.c,一个自实现的 AES 纯 C 实现:
- 头文件 AesCrypt.h 定义
AES_BLOCKLEN 16、AES_KEYLEN 32(即 AES-256),并默认启用CTR计数器模式与AES256宏; - 密钥长度为 8 个 32 位字(
Nk 8),轮数为 14(Nr 14),与 AES-256 规范一致; - 对外仅暴露两个接口:
AesInit(初始化轮密钥与 IV)与AesXCryptBuffer(CTR 模式下的流式加解密,加密与解密共用同一函数)。
这一实现与通信流程的对应关系是:DemonMetaData中随机生成的 32 字节 Key + 16 字节 IV 在 Agent 侧保存于Config.AES,后续 TransportSend 发送的所有数据均先经AesXCryptBuffer加密;Teamserver 侧则通过 aes.go 做对称解密,从而保证 C2 信道的机密性。
五、src/inject:注入与派生进程
src/inject 对应 README 所述"injection functions and utilities"。头文件 inject/Inject.h 定义了注入技术枚举:
INJECTION_TECHNIQUE_WIN32(1):传统 Win32 API 注入;INJECTION_TECHNIQUE_SYSCALL(2):系统调用注入,且为默认值(INJECTION_TECHNIQUE_DEFAULT);INJECTION_TECHNIQUE_APC(3):APC 注入。
同时定义了派生(Spawn)技术的默认值为系统调用方式(SPAWN_TECHNIQUE_SYSCALL),并封装了统一的Inject入口,区分三种操作方式:INJECT_WAY_SPAWN(派生进程执行)、INJECT_WAY_INJECT(注入现有进程)、INJECT_WAY_EXECUTE。注入相关的错误码(如INJECT_ERROR_PROCESS_ARCH_MISMATCH)也在此定义,用于在注入 32/64 位进程架构不匹配时给出明确反馈。实现细节分布在 Inject.c 与 InjectUtil.c 中。
六、src/main:三种 PE 入口点
README 明确指出 src/main 是"PE 可执行文件的入口点",并给出三个文件对应的产物类型。三者最终都汇聚到DemonMain,但启动方式截然不同。
6.1 MainExe.c —— Windows EXE
MainExe.c 是最直接的入口:WinMain收到参数后仅打印调试信息并直接调用DemonMain( NULL, NULL ),随后返回 0。它是独立 EXE 形态的入口,由 Teamserver 以-e WinMain(x64)或-e _WinMain(x86)链接。
6.2 MainSvc.c —— Windows Service EXE
MainSvc.c 实现标准 Windows 服务框架:
- 全局维护
SERVICE_STATUS(接受 STOP 与 SHUTDOWN 控制码); WinMain构建SERVICE_TABLE_ENTRY(服务名来自SERVICE_NAME编译宏,默认DemonService)并调用StartServiceCtrlDispatcherA注册分发表;- 服务管理器回调
SvcMain后,先RegisterServiceCtrlHandlerA注册控制处理器,再调用DemonMain启动 Agent; - 控制处理器
SrvCtrlHandler在收到SERVICE_CONTROL_STOP或SERVICE_CONTROL_SHUTDOWN时更新状态为SERVICE_STOPPED并上报SetServiceStatus。
服务形态的好处是无需交互登录即可随系统自启,且在任务管理器等常规工具下表现为系统服务。
6.3 MainDll.c —— DLL 与 Shellcode
MainDll.c 的处理最为精细,因为它同时服务 DLL 与 Shellcode 两种形态,通过SHELLCODE编译宏区分:
- 非
SHELLCODE模式(普通 DLL):导出Start函数(注释注明为rundll32等加载器预留,TODO 计划将函数名改为可配置),内部用 PEB 解析Sleep后进入死循环以防止进程退出;DllMain在DLL_PROCESS_ATTACH时新建线程执行DemonMain,源码注释解释了原因:在DllMain内直接发起 HTTP 请求会因 WinHTTP 的限制触发ERROR_INVALID_STATE,因此必须在线程中执行;DEBUG模式下还会AllocConsole以便输出调试打印; SHELLCODE模式:跳过Start导出,DllMain直接调用DemonMain( hDllBase, Reserved ),因为 Shellcode 加载器已经接管了线程上下文,无需再开新线程。
DLL 形态可由rundll32或进程侧加载执行;Shellcode 形态则由 DllLdr 与 Shellcode 加载器配合,最终把 DLL 载荷封装为位置无关的原始字节流。
七、构建系统:CMakeLists 的真实用途
README 专门用一整节对 CMakeLists.txt 做出重要澄清:
Do not modify it or use it. This is only for developing and editing the Demon source code in CLion……It is only there to make Clion happy.
即:该 CMake 文件仅供开发者在 CLion 等支持 CMake 的 IDE 中阅读与编辑源码时使用,用于提供代码引用与索引,并非官方构建入口,读者不应以它作为编译载荷的手段。从内容看,它也确实是一个"示例级"配置:指定x86_64-w64-mingw32-gcc交叉编译器、C_STANDARD 11、CONFIG_BYTES={}空配置与默认SERVICE_NAME="DemonService",并同时启用TRANSPORT_HTTP、TRANSPORT_SMB、AES256三个宏(真实构建中这些开关由 Teamserver 按监听器类型注入)。
真实的生产构建链路在 Teamserver 侧。构建器 builder.go 展示了完整流程:
- 将用户界面配置(睡眠、抖动、注入技术、代理加载、AMSI/ETW 补丁、监听器信息等)通过
PatchConfig序列化为字节流; - 生成
CONFIG_BYTES={0x..,0x..,...}编译宏注入到-D参数; - 按目标架构(
ARCHITECTURE_X64/ARCHITECTURE_X86)选择 x64/x86 mingw 编译器与 NASM,将 src/asm 的汇编按架构过滤编译为.o后与全部 C 源文件(含 src/Demon.c)一起编译; - 按产物类型选择入口:EXE 用
MainExe.c、服务 EXE 用MainSvc.c(额外定义SVC_EXE并链接ntdll)、DLL 用MainDll.c(-shared -e DllMain); - RAW(Shellcode)产物则先编译带
SHELLCODE宏的 DLL,再与前置于 payloads/Shellcode.x64.bin / payloads/Shellcode.x86.bin 的加载器模板拼接。
构建时还依据配置文件(如 profiles/havoc.yaotl 中的Teamserver.Build段指定Compiler64、Compiler86、Nasm路径)进行字符串替换与 MZ 头部魔数修补。注意 profiles/havoc.yaotl 中的Demon段(Sleep、Jitter、Injection.Spawn64/Spawn32)即对应DemonConfig解析出的字段,两个文件共同决定了最终载荷的行为参数。
八、小结:从源码结构理解 Demon 的设计取向
综合五个源码目录与三种入口点,可以归纳出 Demon Agent 的几个设计主线:
- 规避优先:汇编层做返回地址栈欺骗,核心层用 PEB 动态解析替代导入表、可选间接系统调用,
SleepObf在睡眠期加密内存,构建期提供 Ekko / Foliage / Zilean 等睡眠混淆技术与jmp rax/jmp rbx跳板选项(见 builder.go 的常量定义); - 配置内嵌:运行时行为全部由
CONFIG_BYTES编译期注入,避免落盘配置文件; - 传输可插拔:通过
TRANSPORT_HTTP/TRANSPORT_SMB编译宏在 Transport.c、TransportHttp.c、TransportSmb.c 之间切换,SMB 传输同时承担父/子 Pivot 的通信; - 形态多样:同一份核心代码通过 src/main 的三个入口适配 EXE、服务、DLL/Shellcode 三种投递场景。
对于希望深入研究的读者,建议按以下路径阅读源码:先通读 Demon.c 把握生命周期,再对照 Demon.h 的INSTANCE结构体理解各模块的数据契约,随后分别进入 src/core、src/crypt 与 src/inject 验证具体实现,最后回到 builder.go 理解编译期参数如何落到运行时的DemonConfig。这一条阅读路径覆盖了 Agent 从"构建"到"上线"再到"任务执行"的完整闭环。
- 网络安全
【免费下载链接】Havoc
The Havoc Framework
相关推荐
HelloAgents Code Agent CLI 项目结构深度解析:模块化智能体框架的架构设计与源码实现
HelloAgents Code Agent CLI 项目结构深度解析:模块化智能体框架的架构设计与源码实现 本文以 YYHDBL HelloCodeAgent
教程文档AI Agent人工智能大模型当游戏逻辑多到卡帧:bitECS 用三种数据布局思路让你的 TypeScript 项目跑出 C 语言手感
当游戏逻辑多到卡帧:bitECS 用三种数据布局思路让你的 TypeScript 项目跑出 C 语言手感 你有没有过这样的体验:一个网页游戏刚开始只有几十个对象
SBOM工具API深度使用:C集成与自定义扩展开发指南
SBOM工具API深度使用:C 集成与自定义扩展开发指南 SBOM工具(Software Bill of Materials)是一款高度可扩展的企业级工具,用于
供应链安全SBOMCLI开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考