铸就无特征载荷击败全球杀软(上)
原创
陆安予
陆安予
白帽子安全笔记2.0
2026年11月1日 17:39
江苏
去阅读
摘要:借助强大的Cobalt Strike可塑性可轻松击败全球杀软,这一切是如何做到的?
一、这是什么
本想码一篇无特征载荷吊打全球杀软的文章,但思来想去,如果只贴测试结果,难免会让人难以接受,显得很装。会被问到一万个问题,因为这涉及到非常多的技术,不是几句话就能概括。
比如YARA是什么?,DLL侧载什么时候用?、全球有哪几类杀毒软件?什么时候需要代码混淆?...一口气讲完是不可能的,想要实现这一点绝非易事,因为都是突然进入到这个技术中,如果技术不放在历史中看前后发展过程,那就如同盲人摸象,只会形成只知其一不知二的局面,甚至你研究了半天发现是过去早已淘汰的技术,会形成为什么是这样?这又是什么?就又回到十万个为什么上的问题。
二、技术原理
1. Payloads是什么?
当我们启动强大的Cobalt Strike后,菜单中第三个选项Payloads,这是什么?
Payloads指的是“为了让目标机器上线,而实际发送到目标机器上执行的那段代码”,通常叫载荷。
无论你在这个菜单里选什么,最终生成的载荷核心都是 Beacon。Beacon 是一段运行在目标内存中的轻量级代码,它每隔一段时间(比如 60 秒)就会醒来一次,向你的 CS 团队服务器发送请求:“老板,有任务吗?”,如果有,就执行命令并把结果带回来。这种机制叫“回连”。
什么意思,它的核心就是连哪里,怎么连。
因此,如果想要实现回连,必须想尽一切办法(如钓鱼、侧加载、漏洞利用),把那段Payload送到目标机器的内存里去运行。
2. Payloads为什么有这么多选项?
载荷如果想要实现多功能更隐蔽,比如通信加密、文件管理、进程注入、内存混淆、系统调用等等。功能实现越复杂,文件大小就越大,payload\_x64.bin 默认达到303KB。
因此,意味着在某些情况下,可以牺牲安全性和隐蔽性,只保留一个连接并下载,这叫分阶段,就是Stager Payloads Generator。
为什么要这样设计,这是由Windows设计决定的,比如受Windows命令行最大长度限制,塞不下一个约300KB的完整Beacon,比如缓冲区溢出,攻击者能控制的字节数往往非常有限,载荷它只有909字节。
第一个选项HTA和第二个Ofiice宏选项,宏从2022被微软默认阻止已经无法使用,HTA这个东西,虽然现在也有在APT中看到,但效果不好说。
3. payload\_x64.bin到底是什么
当我们通过菜单Stageless Payload Generator导出一个shellcode文件时,它本质是一个反射DLL。只不过在生成时Cobalt Strike对 DLL 的 DOS 头做了处理,将原本不执行代码的头部区域改写为可执行的shellcode入口,这种技术叫PIC化(Position-Independent Code,位置无关代码),这就让其具备了PIC属性。所以载荷本质上就是一个隐蔽的与位置无关的反射DLL。
4. 为什么要这样设计?
与位置无关代码(PIC),你只要把这个bin丢进目标进程内存并让 CPU 跳转过去,它自己就能跑起来,不需要外部程序做任何其它处理。这种设计让shellcode变得非常通用,你可以自己写任意加载器加载它。而现代普遍都使用这种机制,例如Donut类工具,这使得它绕开了许多杀软对常规DLL加载路径的监控。
但仅仅Donut化显然是不够的,例如Donut格式的shellcode特征检测规则如下。
1
2
3
4
5
6
7
8
function detect() {
// 1. 核心特征一:检查文件开头是不是 0xE8 (CALL 指令)
// 2. 核心特征二:检查 CALL 的偏移量与 5 个字节后的数据是否一致
// 3. 核心特征三:计算 CALL 的目标地址
// 检查目标地址处是不是 0x59 (POP ECX 指令)
// 如果以上三个条件全部满足,直接判定为 Donut 生成
return \"Detected: Donut Shellcode\";
}
而在默认的Cobalt Strike中,默认的头是MZAR,该默认值已被纳入特征。
但之所以Cobalt Strike能成为全球顶级武器,其强大的可塑性设计就是为了规避这一点,既然默认的载荷是反射加载器,我们通过可塑性,使用其自带的强大的用户自定义反射加载器,我们便能直接修改整个默认的反射加载器的心脏,而无需更改任何客户或服务端的代码。
5. 反射DLL又是什么?
该技术于2009年被发布,反射DLL在文件上可以是任意格式,但本质还是DLL,只不过加载方式变了,你只需要加载这个ReflectiveLoader函数就行,它在内存中自我组装运行。
6. 什么是传统DLL注入?
想让代码在别的进程中运行,这叫DLL注入,这种技术在1994年被公开确定,在传统注入中,格式必须是DLL,原因在于DLL是Windows操作系统唯一原生支持的、可被动态加载和执行的代码容器。如果你把一个.exe文件或一段纯文本路径传给 LoadLibrary,系统会直接报错,因为它只解析符合 PE 格式且带有特定 DLL 标识的文件。
但随着发展,这一个巨大的弊端,LoadLibrary,CreateRemoteThread是固有的特征,杀软只需要看静态文件和这个API调用即可确定是DLL注入。
7. 传统DLL注入技术之前是什么?
在传统DLL注入之前,恶意代码的主流形态是独立可执行文件。早期 DOS 时代的文件型病毒感染的就是 .COM 和 .EXE 文件,但这种格式太过显眼且是独立进程容易被检测。
三、总结
1990 年代之前至早期:恶意代码就是独立 EXE(或 COM 文件、引导区病毒)。它直接运行在自己的进程里,没有注入的概念。
1994 年:Richter 公开传统 DLL 注入,恶意代码开始有了“进入别人进程”的标准手段。
2009 年:反射 DLL 注入出现,解决传统注入的落盘和 LoadLibrary 调用问题。
2014 年前后:Cobalt Strike 把反射 DLL 改造成PIC Shellcode形态,进一步简化投递。
与其研究如何免杀到处找加载器,不如先从基础学起,从历史学起,年轻人一定要有一种打破砂锅问到底的精神,先问,这是什么?为什么有这样一个东西?之前之后是什么?一定要这样吗?发展趋势是什么?这样才能真正掌握先进技术,不至于在技术的路上迷路。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论