news 2026/9/25 3:05:46

Havoc Demon Agent 源码结构深度解析:C2 植入体的模块化设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Havoc Demon Agent 源码结构深度解析:C2 植入体的模块化设计与实现
  • 网络安全

【免费下载链接】Havoc

The Havoc Framework

项目地址:https://gitcode.com/gh_mirrors/ha/Havoc
点击查看免费下载

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 的启动流程被设计为四步:

  1. 初始化指针、模块与 Win32 API(DemonInit);
  2. 初始化元数据(DemonMetaData);
  3. 解析内嵌配置(DemonConfig);
  4. 进入主连接与任务循环(DemonRoutine)。

整个源码按功能划分为五个目录,README 中给出了它们各自的职责:

目录职责
src/asm汇编代码(返回地址栈欺骗)
src/core核心功能(连接服务端、动态加载 Win32 API / 系统调用)
src/crypt加解密函数
src/inject注入函数与工具
src/mainPE 可执行文件的入口点

此外,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 展示了完整流程:

  1. 将用户界面配置(睡眠、抖动、注入技术、代理加载、AMSI/ETW 补丁、监听器信息等)通过PatchConfig序列化为字节流;
  2. 生成CONFIG_BYTES={0x..,0x..,...}编译宏注入到-D参数;
  3. 按目标架构(ARCHITECTURE_X64/ARCHITECTURE_X86)选择 x64/x86 mingw 编译器与 NASM,将 src/asm 的汇编按架构过滤编译为.o后与全部 C 源文件(含 src/Demon.c)一起编译;
  4. 按产物类型选择入口:EXE 用MainExe.c、服务 EXE 用MainSvc.c(额外定义SVC_EXE并链接ntdll)、DLL 用MainDll.c(-shared -e DllMain);
  5. 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 的几个设计主线:

  1. 规避优先:汇编层做返回地址栈欺骗,核心层用 PEB 动态解析替代导入表、可选间接系统调用,SleepObf在睡眠期加密内存,构建期提供 Ekko / Foliage / Zilean 等睡眠混淆技术与jmp rax/jmp rbx跳板选项(见 builder.go 的常量定义);
  2. 配置内嵌:运行时行为全部由CONFIG_BYTES编译期注入,避免落盘配置文件;
  3. 传输可插拔:通过TRANSPORT_HTTP/TRANSPORT_SMB编译宏在 Transport.c、TransportHttp.c、TransportSmb.c 之间切换,SMB 传输同时承担父/子 Pivot 的通信;
  4. 形态多样:同一份核心代码通过 src/main 的三个入口适配 EXE、服务、DLL/Shellcode 三种投递场景。

对于希望深入研究的读者,建议按以下路径阅读源码:先通读 Demon.c 把握生命周期,再对照 Demon.h 的INSTANCE结构体理解各模块的数据契约,随后分别进入 src/core、src/crypt 与 src/inject 验证具体实现,最后回到 builder.go 理解编译期参数如何落到运行时的DemonConfig。这一条阅读路径覆盖了 Agent 从"构建"到"上线"再到"任务执行"的完整闭环。

  • 网络安全

【免费下载链接】Havoc

The Havoc Framework

项目地址:https://gitcode.com/gh_mirrors/ha/Havoc
点击查看免费下载

相关推荐

上一篇:wger数据备份策略:结合全量、增量与差异备份的方案
下一篇:小米笔记本Hackintosh无线网卡终极解决方案:Intel Wi-Fi驱动 vs 更换模块

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 3:05:18

STM32恒流源法电阻测量仪设计与实现

做嵌入式这么多年,测电阻这件事我一直觉得没表面上那么简单。手里虽然有万用表,但每次测低阻值电阻或者在线测量时,接触电阻、导线压降带来的误差能让人抓狂。后来项目里需要做一个自动化的电阻测量模块,干脆自己用STM32搭了一个恒…

作者头像 李华
网站建设 2026/9/25 3:03:44

Apereo CAS SAML2 Git 元数据管理:SP 与 IdP 元数据的版本控制实战

后端认证鉴权单点登录 【免费下载链接】cas Apereo CAS - Identity & Single Sign On for all earthlings and beyond. 项目地址: https://gitcode.com/gh_mirrors/ca/cas 点击查看 免费下载 本篇技术指南聚焦 Apereo CAS 中 SAML2 元数据的 Git 托管方案&…

作者头像 李华