ARTICLE / 安全

Java 内存马应急检测实战:从手册到开源工具的全栈演进

摘要

内存马(Memory Shell)已成为攻陷 Java 应用后的首选持久化手段——它在 JVM 内存中动态注册恶意组件,磁盘无文件、进程无异常、杀软无感知,传统基于文件的检测体系完全失效。本文以实战视角完整呈现 Java 内存马的检测方法论、工具设计演进与取证闭环:从信号分级体系(A1-A5 强信号 + B1-B5 辅助信号)到开源工具 memshell-auditor 的落地,再到双程序防识别架构、类 Metasploit 特征库生态与 AI 增强分析。全部结论基于真实实验:用 java-memshell-generator(JMG)生成 7 种真实内存马载荷,注入 Tomcat 9/10 靶场逐一验证,检出率 100%。

一、引言:为什么内存马检测是应急响应的硬骨头

1.1 研究问题

在应急响应实践中,Java 应用被攻陷后(反序列化、表达式注入、文件上传等入口),攻击者最常植入的就是内存马。它具备三个让传统检测失效的特性:

  1. 磁盘无文件:恶意类通过 ClassLoader.defineClass 直接注入 JVM 内存,磁盘上不存在任何恶意文件
  2. 进程无异常:恶意代码寄生在合法进程(Tomcat/Spring Boot)内,进程列表看起来完全正常
  3. 持久化免杀:攻击者拿到一次执行机会即可植入,且内存马随业务流量长期存活,杀软/EDR 无从感知

由此引出三个核心研究问题:

  • RQ1:内存马有哪些类型与注入方式?最新技术动向是什么?
  • RQ2:如何在运行时可靠检测内存马,且不依赖"已知特征"(能对抗未知变种)?
  • RQ3:检测出内存马后,如何完成取证闭环(dump 恶意类、反编译核心代码、分析回连),并把特征反哺给社区?

1.2 相关工作

现有检测方案存在明显空白:

  • 静态扫描(扫描 class/jar 文件):对内存马无效,因为根本没有文件
  • 容器层扫描器(如 java-memshell-scanner):检测 Filter/Servlet 层,但对 Agent 型内存马、伪装类名(如 org.springframework.ServletRequestAujFilter)无效
  • RASP 类产品:能拦截但误报高、部署重,不适合应急现场

差异化定位:memshell-auditor 通过 attach 方式(类 Arthas 但更聚焦),零依赖纯反射实现,覆盖容器层 + Agent 层 + 启发式行为检测,并内置取证闭环。

二、内存马技术全景

2.1 分类体系

类型注入点特性典型工具
Filter 型Servlet 容器过滤器最主流,拦截所有请求冰蝎/哥斯拉/蚁剑
Servlet 型动态注册 Servlet独立 URL 入口哥斯拉
Listener 型ServletRequestListener每次请求触发,隐蔽冰蝎
Valve 型Catalina Valve绕过 Filter 链检测哥斯拉
WebSocket 型Endpoint 动态注册不走 HTTP Filter少见但更难发现
Upgrade 型协议处理器绕过 Servlet 容器高级对抗
Agent 型Instrumentation transformer改写任意类字节码无侵入 Agent
JSP 变体JSP 预编译磁盘有文件,可静态查传统

2.2 注入链路

典型攻击链:反序列化漏洞(入口)→ 表达式/反射执行(载荷投递)→ ClassLoader.defineClass(内存加载)→ 容器 API 注册(持久化)。JDK17+ 模块系统限制反射 defineClass 后,攻击者改用 MethodHandles.Lookup.defineClass(实测验证)。

2.3 2025-2026 最新技术动向(GitHub 实时情报)

  • 生成器 MemShellParty(⭐1572)适配 JDK 6-21、Tomcat 5-11、Jetty/WebLogic/WebSphere/JBoss 及国产化中间件(TongWeb/BES/InforSuite/Apusic/Primeton)
  • Agent 型内存马 ASM 轻量化:体积减 80%,无侵入注入
  • 探测马自动识别服务类型,集成哥斯拉/冰蝎/蚁剑/Suo5/NeoreGeorg 隧道
  • 检测端对抗升级:改 ClassLoader、隐藏 FilterConfig、ThreadLocal 存恶意逻辑、不死化持久化
  • 类名伪装成常态:JMG 实测所有载荷类名伪装成 Spring/Log4j 框架类(见 §5.2)

三、检测方法论:信号分级体系

核心思路:不猜"内存马长什么样",而是检测"类做了什么不该做的事"

信号分级体系 信号分级体系

3.1 强信号(A 系,一锤定音)

信号检测项判定
A1容器组件(Filter/Servlet/Listener/Valve)注册的类在磁盘无对应 class 文件🔴 高度疑似
A2容器组件注册数量异常(对比基线)🔴 需复核
A3非系统 ClassLoader 加载恶意类🟠 需复核
A4-javaagent/agentlib/系统属性注入 + 可疑 ClassFileTransformer🔴 高度疑似
A5启动命令/环境变量异常🔴 需复核

3.2 辅助信号(B 系,组合提升置信度)

信号检测项对抗性
B1类名特征(无包名/短随机名/大小写混淆)可被伪装绕过
B2类名含恶意关键字可被伪装绕过
B3-B5行为模式辅助组合评分

3.3 启发式行为检测(对抗未知变种)

从字节码可读字符串提取行为模式组合评分:命令执行(Runtime.exec/ProcessBuilder)+ 动态加载(defineClass)+ 载荷解密(Base64/AES/Cipher)+ 网络回连(Socket/硬编码 IP)+ WebShell 回显(getParameter + 响应流)。容器组件特征 + ≥2 个行为模式 → 判定可疑,类名伪装无效。

四、工具设计演进:memshell-auditor

版本演进 版本演进

检测架构 检测架构

4.1 v1.0:核心检测能力

零依赖纯反射实现(JDK 8 编译,兼容目标 JVM 8-21+),五大审计器:

模块能力
ContainerAuditorFilter(filterDefs+filterConfigs 双路径)/Servlet/Listener/Valve 全链路审计
AgentAuditor启动参数/Agent 型检测
TransformerAuditorClassFileTransformer 审计
ClassLoaderAuditorClassLoader 血缘
ClassFeatureAuditordefineClass + 磁盘无 class → A1 强信号

关键技术难点:容器上下文定位。WebappClassLoaderBase.getResources() 方法反射在部分版本返回 null,最终改用 resources 字段反射兜底成功拿到 StandardContext(坑 2)。

4.2 v1.1:取证闭环

取证闭环流程 取证闭环流程

检出不是终点——还要能 dump、反编译、分析回连:

  1. ClassDumper:字节码提取落盘(transformer + retransform 技巧,addTransformer(tf, true) 的 canRetransform 必须为 true,坑 4)
  2. Decompiler:javap 反汇编提取恶意核心代码(命令执行/解密/网络方法)
  3. NetworkAnalyzer:进程 ESTABLISHED 外连分析 + 字节码字符串 IP 提取
  4. MemoryForensics:jmap 堆内存取证(跨平台)

实测:冰蝎内存马 dump 4160B 字节码,反编译展示核心逻辑:

public class org.springframework.ServletRequestAujFilter extends java.lang.ClassLoader implements jakarta.servlet.Filter {
  public java.lang.String pass;          ← 连接密码
  public byte[] doBase64Decode(String)   ← Base64 解密载荷
  ... Cipher.getInstance("AES") ...      ← AES 加解密
  ... getHeader ... getSession ...       ← 认证与会话
}

4.3 v1.2:双程序防识别架构(核心对抗设计)

问题:固定名字的取证工具(memshell-auditor.jar)进程一出现,攻击者立刻知道被取证了,可隐藏内存马、销毁证据甚至反制。

方案--gen-agent 生成混淆取证程序——每次随机文件名/包名/类名/字符串特征:

双程序防识别架构 双程序防识别架构

主程序(分析端)取证程序(现场端)
位置分析者机器目标系统现场
文件名memshell-auditor.jarsystem-diag-2c4488.jar(随机)
类名/包名com.memshellauditor.*net.jvm.check.*(每次随机)
特征字符串memshell/auditorjavacore/classcore(中性化)
规则匹配✅(内置规则,离线可用)
AI 能力❌(AI 仅在主程序)

技术实现:class 文件常量池重写器(ClassRewriter)——纯字节码级替换 Utf8 条目,不依赖 JDK 编译。三个关键坑:jar 重写时 MANIFEST.MF 重复(ZipException)、替换规则顺序导致包名二次替换(LinkedHashMap 保证顺序)、类名不能参与敏感词替换(this_class 不一致,坑 10-12)。

4.4 v1.3:全自动扫描(–scan)

现场取证人员不需要知道审计哪个 PID:自动枚举所有 Java 进程 → 可疑度评分排序(Web 容器 +100 优先、可疑关键字 +60、工具/守护 -30、自身 -1000 跳过)→ 逐个审计 → 汇总 HIGH 排行。

–scan 全自动扫描终端演示 –scan 全自动扫描终端演示

实测 9 进程环境:自动识别 4 个高危目标(HIGH=2/1/1/1)+ 1 个正常,全程无需人工指定 PID。

4.5 v2.0:特征库生态 + AI 增强

特征库(类 Metasploit)x7peeps/memshell-rules 仓库,CLI 在线更新:

特征库管理终端演示 特征库管理终端演示

--rules update            拉取/更新特征库(18 条规则,代理支持)
--rules list              列出规则(提交人/标题/勾选状态)
--rules select --all     全选 / --rules select --id MS-001 逐个勾选
--rules download <repo>  下载他人特征库

AI 增强分析:OpenAI 通用兼容接口(base_url + api_key + model 三要素可配),兼容 OpenAI/DeepSeek/通义/Ollama/vLLM;可配可跳过,未配置时结尾引导三种配置方式,重跑自动带 AI 增强展示(恶意行为解读/回连判断/处置建议)。

特征提交闭环(众包反哺)--analyze 检测未命中规则的高危项 → 提示提交 → --submit --auto-commit 自动推送;push 失败降级生成 GitHub Issue(五大段描述:场景/检出详情/规则/复现/说明)+ 自动打开新建 Issue 页面。

五、实战验证:JMG 真实载荷 7/7 检出

5.1 实验设计

用 JMG v1.0.9 生成 7 种真实内存马(冰蝎 Filter/Listener/Listener2、哥斯拉 Filter/Valve、蚁剑 Filter、Suo5 Filter),注入 Tomcat 9(javax)与 Tomcat 10(jakarta)靶场,逐一验证检测能力。

5.2 实验结果

载荷工具伪装类名检出
冰蝎 FilterBehinderorg.springframework.ServletRequestAujFilter✅ HIGH
哥斯拉 FilterGodzillaorg.springframework.WhiteBlackListGbyfbdFilter✅ HIGH
蚁剑 FilterAntSwordorg.springframework.AbstractMatcherVyjFilter✅ HIGH
Suo5 FilterSuo5org.springframework.SessionKqvcFilter✅ HIGH
冰蝎 ListenerBehinderorg.apache.logging.Log4jConfigEaeListener✅ HIGH
哥斯拉 ValveGodzillaorg.springframework.AbstractMatcherGbValve✅ HIGH
冰蝎 Listener2Behinderorg.springframework.ContextLoaderDmasjListener✅ HIGH

检出率 100%(7/7)。关键发现:所有载荷类名伪装成 Spring/Log4j 框架类——B1 类名特征检测完全失效,但 A1 磁盘无 class 强信号不受影响。这是工具的差异化护城河。

内存马检出终端演示(dump + 反编译 + 回连) 内存马检出终端演示(dump + 反编译 + 回连)

5.3 误报控制

  • 磁盘类(BizFilter)→ 正常(INFO)
  • Agent 自身类 → 动态自排除(ReflectUtil.isSelfClass 兼容混淆版)
  • JDK/框架类 → 豁免
  • 混淆取证程序 attach 后自身误报从 19 条清零到 0

六、版本管理产品设计

版本分离管理 版本分离管理

6.1 版本分离自动更新

维度工具版本策略版本
标识v2.21.1.0
更新命令–update(GitHub releases)–rules update
展示banner 署名 x7peepsbanner 显示策略版本

6.2 策略更新失败 → 版本推进

策略更新出问题时本地版本自动 bump(1.1.0 → 1.1.1),banner 显示 partial/failed 状态警告并建议重试——让"更新出问题"可被识别

6.3 自定义规则保护(不误删)

官方规则存 rules/、用户自定义存 rules-custom/(origin 字段区分);更新只覆盖官方,自定义永不触碰。实测 CUSTOM-001 自定义规则经多次 update 均安全保留。

6.4 增量同步不整体替换

只更新官方 index 中的规则,只删除"官方目录中已从 index 移除"的规则,自定义目录完全不动。

七、踩坑大全(实战经验速查)

#根因解决
1lsof 抓错进程连接macOS lsof -p 与 -iTCP 组合混入系统连接外部 IP 过滤 + 代码字符串 IP 提取
2容器上下文定位失败getResources() 方法反射返回 null改 resources 字段反射
3同一 JVM 重复 attach 不生效agent 类被系统 ClassLoader 缓存每次改代码必须重启目标进程
4retransform 不触发addTransformer 的 canRetransform 必须为 true传 true
5JMG 混淆载荷 retransform 失败激进 ASM 处理,JVM 拒绝重定义CodeSource=null 作证据 + jmap 兜底
6混淆 jar 打包规则不生效规则文件名与 id 不匹配按 id 重命名
7取证程序 CLI 暴露分析端Main-Class 指向错误新增 ForensicMain 分离
8自动提交污染官方索引index.json 整体覆盖改 mergeIndex 合并
9class 常量池替换顺序错误HashMap 无序遍历LinkedHashMap 保证顺序
10类名被敏感词替换this_class 与文件路径不一致只替换包名前缀

八、局限性与未来方向

已知局限

  1. JMG 混淆载荷(Suo5 等激进 ASM 处理)无法 retransform dump,依赖 jmap 堆 dump 兜底
  2. Agent 型内存马 transformer 枚举受 JDK 模块限制,内部字段探测 + 类特征间接检测
  3. 回连分析会混入本机其他服务连接(代理/远程控制),需结合威胁情报确认
  4. 运行时窗口:attach 只能看到当前已加载的类

未来方向

  1. 并发扫描提速(资源允许时多线程审计)
  2. 总览报表(HTML 汇总所有进程结果)
  3. 国产中间件专项适配验证(TongWeb/BES)
  4. 规则库持续扩充(社区贡献)
  5. 与威胁情报 API 联动(回连地址自动查询)

九、结语

从信号分级方法论到开源工具全栈实现,本文完整呈现了 Java 内存马应急检测的实战路径。核心结论:检测内存马的关键不是"认出它",而是"发现异常"——A1 磁盘无 class 强信号、行为模式组合评分、双程序防识别、众包特征反哺,构成了可对抗未知变种的完整检测体系。工具开源在 github.com/x7peeps/memshell-auditor,特征库在 github.com/x7peeps/memshell-rules,欢迎社区贡献特征、共同成长。

附录:复现指南

环境

  • macOS (Apple Silicon) / JDK 26 编译(–release 8)/ Maven
  • Tomcat 9.0.89(javax)/ Tomcat 10.1.24(jakarta)
  • JMG v1.0.9(源码构建)

快速复现

# 1. 构建
mvn clean package -DskipTests

# 2. 生成混淆取证程序
java -jar memshell-auditor.jar --gen-agent /tmp/obf --name-prefix system-diag

# 3. 现场扫描(取证程序)
java -jar system-diag-xxxx.jar --scan --dump ./dump --heap ./heap

# 4. 分析报告(主程序,AI 可配)
java -jar memshell-auditor.jar --analyze report.json --ai-config ai.json

# 5. 特征库管理
java -jar memshell-auditor.jar --rules update
java -jar memshell-auditor.jar --rules list