绕过青X云内存马检测-思路篇"

admin 2026-11-06 03:04:20 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

绕过青X云内存马检测-思路篇

原创

Joy
Joy

安全很忙

2026年11月4日 21:13
湖北

去阅读

温馨提示:本文分析的是青X云的历史版本,并非最新版本,新版检测能力可能已经完全不同,请勿据此对号入座。本文只聊思路和技术原理,不会贴出可直接使用的完整实现,内容仅供交流学习。研究目的仅限自查和攻防对抗理解,请勿用于非法用途——现在溯源技术很强,劝你善良。想看代码细节的兄弟可以先关注,后面单独开一篇讲对抗过程。


事情是这样的:我的内存马,被\"青X云\"标红了

我自己在本地搭了个靶场环境,注了一个 Agent 类型的内存马(就是那种不落盘、直接寄生在 Java 进程里的后门)。

本来一切顺利,连接、执行、回显都正常。

结果我把\"青X云\"的检测 Agent 一开——

直接给我标红了。 屏幕上那个刺眼的\"高危\",属实把我看愣了。

我寻思我也没改名字啊,也没用什么花里胡哨的东西,怎么就被抓了?

于是我一晚上没睡,干了件事:把它抓我的那个 Agent,扒开来研究了一下。(再次强调:这是青X云的历史版本,不代表最新版,仅供交流学习。)


先补课:啥是内存马?

传统的 webshell,是往服务器上传一个 .jsp、.php 文件,你访问它就能执行命令。这玩意儿有个致命缺点——它落在磁盘上。

安全设备一扫盘,文件特征一比对,分分钟给你揪出来。

攻击者一拍脑袋:既然文件会被扫到,那我干脆不写文件,把后门直接塞进内存里跑不就行了?

于是就有了内存马。它不生成任何文件,寄生在正常运行的 Java 进程里,磁盘上干干净净,但后门已经在跑了。

这就是为什么这两年内存马这么火——它把\"取证\"这件事从磁盘搬到了内存,而内存这地方,查起来麻烦得多。


第一层反转:它压根就不在进程里判断谁是坏人

我把\"青X云\"注入到 Java 进程里的那个 jar 反编译出来看,看完最大的感受是:

它自己啥也不判断。

它的套路是这样的:

第一步:想办法钻进你的 Java 进程
第二步:采集进程内全部已加载类的元信息
第三步:打包扔给外面的大脑去分析

它自己只干一件事——当个搬运工。

真正判断\"这个类是不是内存马\"的规则,全在服务端。所以你就算把它的 jar 反编译出来,也看不到那些核心判定逻辑。这一点,做得确实很鸡贼,也意味着我从 jar 里能扒到的,只是它采集了哪些维度。


干货:它到底捞了哪些信息?

一个 Java 类,能被\"扒\"出来的身份信息其实就那么几样。它挨个采集:

  • 类名(className)
  • 加载它的类加载器(ClassLoader)
  • 继承的父类(superClass)
  • 实现的接口(interfaces)
  • 标注的注解(annotations)
  • 以及——最关键的一个:该类在磁盘上的来源路径(classPath / 对应的 .class 资源位置)

前面几个都是常规操作。真正让我拍大腿、也正是把我标红的那一条,是最后那个。


一句话点破我的死穴

我单独拎出来讲。

正常的一个 Java 类,一定是从某个 .class 文件、某个 jar 包里加载进来的。所以你去查它的来源,它能报出一个明确的磁盘路径。

但我的内存马呢?

内存马是凭空在内存里生成的类(用 defineClass 现场生成的)。它没有对应的文件,也就没有磁盘来源路径。

所以你查它的来源,它只会返回一个空值:

classPath = null。

这就是我被抓的真正原因。

不管你怎么给它改名字、怎么伪装、怎么随机化,只要它是内存里现场生成的,这个 classPath 就是空的。而正常的业务类、框架类,几乎不可能是空的。

一个类,没有磁盘来源,却又实现了 Filter、Servlet、Valve 这类处理网络请求的接口——

兄弟,这不用服务端大脑分析,我肉眼都觉得你有问题。


那我是怎么绕过去的?

思路其实不复杂:既然它要查我的磁盘来源,我伪造一个给它不就行了?

我让这个\"凭空生成的类\",在被查来源的时候,返回一个真实存在的、看起来人畜无害的路径——比如 Tomcat 自己的某个核心 jar 包。

这样一来,扫描器一采集,\"哟,这类有来源啊,正常正常\",就放过去了。

听起来很美好对吧?但这里面有几个坑,我踩了才敢说:

坑一:不是改一个地方就行。 类的加载分好几层,每一层都可能被查到来源,你得层层都圆过去,漏一个就露馅。我当时只改了最外层,结果它从另一个入口一查,返回的还是空值。

坑二:编的地址得经得起推敲。 有些检测器不满足于\"地址非空\",它还会真去打开那个文件核对内容。这时候你编的地址如果指向一个根本不存在、或者内容对不上的文件,反而更可疑。所以我是直接指向一个真实存在的 Tomcat jar 包。

坑三:还有个更隐蔽的追踪方式。 有些检测器不看你现在的状态,它盯着\"变化\"——注入前有多少类,注入后多出来几个。你每连一次就多造一个新类,它立马就发现\"哎,怎么又多了个来路不明的家伙\"。

对付第三个坑也有招:能复用就别重造。第一次造好存着,后面反复用同一个,不再新增,追踪器就没得报了。


但有一件事,是真的藏不住

聊到这儿,我得说句实在话。

上面这些手段,能让我的内存马在\"扫描采集\"这一关蒙混过关。但还有终极一招,是目前根本没法完全防住的。

就是——直接把你 hook 的那个框架类的字节码 dump 出来,跟原始版本做对比。

Agent 类型的内存马为了劫持请求,本质上是往一个正常的框架类里\"动了手脚\"(用 ASM 改字节码),塞进去了一小段自己的逻辑。

这段逻辑,你可以藏得很深、名字起得很像正常类。但只要检测器把这个类扒出来,跟磁盘上那个原封不动的正版一比——

\"你这里怎么多了一坨?\"

多出来的那段,就是你动过手脚的铁证。这个,改名字没用,伪造磁盘来源也没用,因为改的是\"内容\"本身。

这也是为什么说,Agent 类型的内存马,攻防上是有天花板的。 你能提高被抓的成本,但没法做到理论上绝对隐身。


最后唠两句

写这篇不是教大家怎么绕过安全设备(真要用去干坏事的,劝你善良)。

我更想说的是这背后的一个规律:

安全对抗,本质上是\"信息完整性\"的博弈。

攻击方想让自己看起来\"和正常类一模一样\",防守方则想方设法找出那个\"你无论如何都伪装不掉的东西\"。内存马为空的 classPath 就是那个东西。而一旦你伪造了磁盘来源,防守方就会去找下一个——比如字节码的差异。

这个猫鼠游戏,会一直玩下去。看懂一方是怎么想的,你才能真正看懂另一方。


顺便说说:我把这套分析方法做成了一个 Skill

上面这一整套\"扒检测器\"的流程,我懒得每次重来,就把它沉淀成了一个可复用的 Skill(analyze-memshell-detector)。说白了,就是把\"该怎么分析一个 Java 内存马检测产品\"这件事,固化成了一套标准动作。它能帮你:

  • 定位并反编译 检测产品的多个 jar,自动区分注入器(injector)、引导器(bootstrap)、采集器(collector)三种角色;
  • 追踪数据管线:从持有 Instrumentation 的那个类入手,看它怎么 getAllLoadedClasses、过滤掉哪些类、每个类抽了哪些指纹字段、又通过什么方式(命名管道 / socket / 临时文件)把数据送出去;
  • 提取检测维度:逐条列出它采集了什么、为什么这些字段是判定信号,并标出那个几乎百发百中的最强信号——classPath 为空;
  • 判断裁决位置:区分\"规则在 Agent 内部跑\"还是\"只当搬运工、规则在服务端\"(后者光看 jar 是看不到规则的,只能从采集字段反推);
  • 评估暴露面 + 给出加固方案:把每个检测维度对应到一条具体反制措施(classPath 伪造、payload 复用、hook 隐蔽化、包名伪装……),并诚实地标出那个绕不过去的残留点(字节码 dump + diff)。

最终它会输出四张表:组件表、采集属性表、风险评估表、加固计划——拿来就能对照自查自己的内存马藏得好不好。

这个 Skill 怎么拿?

考虑到内容比较敏感,这个 Skill 不放在公开文章里。想拿的朋友,加入我的内部星球就能获取完整 Skill。星球里我还会陆续更新这类攻防对抗的复盘、工具和踩坑记录。

老规矩:仅限授权环境下的自查和学习交流,别拿去干坏事。

    评论:0   参与:  0