在开发Beacon过程中遇到的一些问题
2026-05-15 23:28:54 # 杂谈

Beacon,一个多么美妙的单词,在安全人的心中,它是:红队弹回的会话,蓝队追猎的心跳,攻防双方在别人机器上角力的核心。

到今天,我已经完成了当初给自己定下的目标——开发出一套完整的C2框架。

如果要给C2框架的开发难度排个序,我的答案是:Server > Beacon > Client。

但如果是给各模块的重要程度排序,权衡之后,我的答案是:Beacon > Server > Client。

我相信大部分师傅来看我的文章,更关注的是底层的防御规避研究和 Beacon的具体实现,开发过程中的工程细节我就不在这里展开了,虽然代码不是我写,全由codex完成,但好歹我提供了想法,设计框架和调试验证>.<。所以我会开源C版本的Beacon,后续再开源Go版本的跨平台Beacon,以供参考和反制。

Beacon项目结构(后期可能会变)

下文是我在开发Beacon过程中遇到的一些问题和解决思路。

一、Job管理系统

起初,我认为Job管理系统是多余的。

Beacon主线程收到任务,执行BOF,返回结果,流程简单清晰,为什么要引入一套复杂的Job管理?

直到我执行了 askcred.o。这个BOF会弹出一个Windows远程登录凭据窗口,等待用户输入。问题来了Beacon主线程同步执行BOF,BOF内部调用了弹窗,弹窗在等待用户点击确认或取消。在用户操作之前,askcred.o 的执行不会返回,导致Beacon主线程被卡死了,我们没办法执行其他命令,这样的单线程命令执行设计显然是不合理的。

为此,我引入了Job管理系统。

  1. 创建流程:首先使用JobCreate创建job,随后在JobStartThread里用CreateThread启动job。
  2. 运行期间:工作线程执行的目标函数需要使用JobIsCancelRequested定期检查取消标志。无论正常结束还是收到取消请求,工作线程的退出路径统一为:清理 → JobComplete → 线程返回,都使用JobComplete标记Job完成并释放jobs。
  3. 取消流程:可以使用killjob对指定任务发出cancel_event,不强制终止工作线程。线程的销毁由工作线程自行完成,这是协作模型的核心。

上面只是我简单的对Job管理系统的简单概况,实际代码更复杂,我就不过多叙说了。

二、Beacon 入口线程不能强制终止,而应该是协作停止

我使用的BOF Loader解析至少有两个线程

  1. BOF Loader工作线程:负责加载COFF、重定位、调用BOF入口。
  2. BOF 入口线程:实际执行bof的go入口函数。

在开发BOF Loader的过程中,我遇到一个诡异的bug,当我使用killjob杀掉需要在后台执行的BOF(比如说askcreds.o),beacon直接终止了BOF Loader的工作线程,使用jobs命令也看不到运行的BOF,这一切看起来正常,但是远程登录窗口的弹窗还是存在!如果各位askcred.o的代码就会明白,它在BOF使用了CreateThread创建了线程,我们只结束了BOF Loader的工作线程,但是BOF内部线程并没有一起结束,导致弹窗还在的bug。

AskCreds这类BOF要想被killjob正确取消,应该使用BeaconGetStopJobEvent主动监听,如果监听到取消事件,则自行关闭窗口和内部线程,避免留下残留窗口和线程。

三、BOF应该使用函数指针的方式执行入口,而不应该使用线程劫持的方式执行BOF

我的BOF Loader主要参考 https://github.com/Cen4enCen/CenCoffLdr ,参考项目中就是使用线程劫持的方式执行入口函数,本Beacon BOF Loader也照搬其实现,但bug也随之而来。

还是askcreds.o这个BOF,当我在自己的win11上执行,成功弹窗远程登录窗口,但是在win10虚拟机上确没有弹出窗口,并且Beacon也掉线了。

这让我百思不得其解,其实这个问题在我开发go版本的beacon时就遇到了,然后我也让朋友试了一下,使用cs的Inline-execute执行askcreds.o正常,使用我开发的beacon,执行askcred.o失败了且Beacon掉线,当时我还认为应该是go版本的bof loader与c版本的bof loader存在执行上的差异,直到着手开发c版本的Beacon BOF loader,再一次遇到beacon 奔溃,而我也陷入了奔溃。

正常来说,我们为BOF注册了VEH异常处理,任何BOF内部发生的异常我们BOF loader都能稳稳接住(哈哈夸张的说法),那么最有可能的问题就是BOF Loader的问题。由于精力有限,没有过多时间调试bof loader奔溃的原因,我让codex分析原因,当我看到它给出的修复的方案中有:BOF应该使用函数的指针方式执行入口,而不应该使用线程劫持的方式执行BOF,这让我联想到havoc的BOF Loader用函数指针的方式执行bof go函数,因为函数指针的方式比线程劫持更普遍,更适用,更不会破坏上下文。

实时也是如此,将执行方式修改成函数指针后,win10虚拟机环境下,成功执行askcred.o,且Beacon没有掉线!

四、模块践踏分配内存不应随便使用

在BOF Loader中,为了尽快为BOF分配足够大的内存空间,我最初选择了Module Stomping,践踏目标是propsys.dll(约950KB,Windows属性系统,负责文件、文件夹等对象的属性管理)。

但实践证明这条路走不通。以askcred.o为例,由于很多程序或功能直接或间接依赖propsys.dll,一旦践踏它,askcred.o执行过程中调用链经过被践踏的区域,就会发生异常导致beacon崩溃。

一个直觉是换成不那么常见的DLL,比如clipwinrt.dll。但这治标不治本——BOF的API依赖不可预测,不同BOF的依赖链不同,没有任何一个DLL能保证对所有BOF都安全。只要用Module Stomping,就永远存在这个隐患。

最终结论:Module Stomping不适合作为BOF Loader的通用内存分配方案。我权衡利弊后,决定回归常规的VirtualAlloc或NtAllocateVirtualMemory申请私有内存。虽然会引入RWX页面这个检测信号,但换取的是BOF Loader的稳定性——功能正确永远优先于隐蔽性。

五、后台持久性任务与睡眠混淆的冲突

在上文中,我引入了job管理系统,专门用于管理后台持久性任务生命周期,但这也引入了后台持久性任务与睡眠混淆的冲突。

就以Ekko技术为例,Beacon睡眠混淆的目的就是进入睡眠前,加密/混淆Beacon自身的进程映像或所使用的内存空间,唤醒时再解密恢复。

而后台持久性任务的要求是:在beacon睡眠期间持续运行某些功能。两者在时间维度上直接冲突——一边要把内存锁死,当执行代码(.text)或者使用到某些全局变量(.data,bss)或UNWIND_INFO(.pdata),一边要在锁死的状态下继续干活。

最常见的就是tunnel后台持久性任务,我的想法是只要有台持久性任务,就从睡眠混淆降级到普通的sleep,这也要引入到工作线程和主线程的概念,这一部分属于job管理系统,我不过多介绍。当存在工作线程还在工作,我们就将睡眠混淆降级到普通的sleep,保证后台任务能够正常执行,最也是havoc的实现方案。

检查线程数量,这是非常关键的逻辑。如果当前implant有活动线程则禁止sleep obfuscation

为了提高Beacon的隐蔽性,是非常不建议在Beacon上建立tunnel的,而是单独使用独立的工具建立隐蔽隧道,但我还是保留beacon在复杂网络上建立隐蔽隧道的能力。

当然我这套睡眠混淆机制还不够完善,等我详细研究CobaltStike的SleepMask机制后再完善一下。

六、反射DLL完善计划

我的Beacon是要对标CobaltStrike Beacon(自信>.<),其payload根据执行阶段的不同分为stager和stagerless。

  1. stager:一个由asm汇编实现的小型加载器,可以从server中下载stage,并跳转执行stage。
  2. stagerless:功能完整的可执行Beacon程序,不依赖加载就可以执行。

其中stage就是经过特殊patch的Beacon.dll,patch的过程是在Beacon.dll的DOS头修改成一小段的stub,用于跳转到Beacon.dll内部的导出函数ReflectiveLoader。

PS:一个小插曲,当我在有卡巴斯基的环境上测试Beacon.dll,还没执行呢,就被卡巴斯基给抓到了,这让我百思不得其解。经过一系列的排查,居然是因为导出函数名是:“ReflectiveLoader”,真是蚌埠住了。

我自认为beacon的ReflectiveLoader是完全能够胜任解析并加载自身的,不过还是留下了很多特征点,就比如旧PE数据还没清除呢。我最初是ReflectiveLoader来清除就PE数据,这个是没错的,错就错在ReflectiveLoader是beacon.dll的导出函数,让ReflectiveLoader清除自身,搞笑的吧。

我又转念一想,将RDL独立于反射DLL之后,方便后续清除旧PE数据和消除映射后PE特征,看起来是不是很熟悉呢?这不就是SRDI吗?确实,在Beacon开源后,我将彻底重构Convert2Shellcode项目,进入2.0版本,这次真的是真的。在看了m09的 https://mp.weixin.qq.com/s/8cYwfqXE57yTQjCJGoDBwA 文章之后,我就想完善项目,增加tls data、tls index、延迟导入表解析,支持复杂的rust程序转shellcode。

七、结语

写到这里,我的Beacon开发过程中遇到的问题就算是写完一部分了,还有很多一些问题我先暂时不说,等时机成熟再聊一聊。

不得不承认,差不多两个月的时间同时开发Server、Client、Beacon的难度超乎我的想象,但每一次调试和优化都让我对攻防技术有了更深的理解,也让我对未来的完善充满期待。

至于dumplass、浏览器凭据提取、RunPE、Shellcode 注入、VNC、AMSI Bypass、ETW Bypass、系统调用、调用栈欺骗、反调试、反沙箱、BeaconGate、级联、横向移动等高级功能,涉及较深的对抗深度,我就不公开了,以后有机会我会以文章的形式讲解。

再聊一聊开源计划方面,预计在今年6月只开源Beacon本身,暂时不会开源Server和Client的源码,这样既能避免C2框架被滥用,又方便研究者进行防御与反制。

忘记说了,Codex nb!Codex nb!Codex nb!重要的事情说三遍。

本月还有一篇文章,也请各位师傅关注一下我的 github,我什么都会做的>.<

洛茜.jpg

Prev
2026-05-15 23:28:54 # 杂谈