网络安全是数字时代的基石,但学习过程中必须严守法律红线 。
根据《中华人民共和国网络安全法》《数据安全法》等法律法规,任何未经授权的网络测试、数据访问或攻击行为均属违法 。本文所有技术讨论与实例均基于合法授权的靶场环境(如Metasploitable、DVWA、Hack The Box等),严禁将文中方法应用于真实系统或非授权场景 。
网络安全学习应以提升防御能力为目标,而非成为攻击工具。
写在前边:此文章现在看不懂没关系,后边的内容是重点,最后再回头看这个文章,会用茅塞顿开来形容。
一、相关名词解释
1、JVM
JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,它是一种抽象化的计算机,也是 Java 实现"一次编写,到处运行"的核心基石。
简单来说,JVM 就像是一个"翻译官"或"中间层"。我们写的 Java 源代码(.java 文件)会被编译成一种特殊的中间代码------字节码(.class 文件)。JVM 的工作就是读取并"翻译"这些字节码,把它们变成特定操作系统(Windows、Linux、macOS 等)能够理解和执行的机器指令。
JVM 的核心价值
-
跨平台性
这是 JVM 最广为人知的特点。只要在不同的操作系统上安装了对应版本的 JVM,同一份 Java 字节码就可以不加修改地在任何平台上运行。开发者不需要为每个操作系统单独编译一份程序。
-
自动内存管理(垃圾回收)
JVM 会自动管理内存的分配和回收,程序员不再需要手动释放内存,大大降低了内存泄漏和程序崩溃的风险。JVM 提供了多种垃圾回收算法(如 Serial GC、Parallel GC、G1 GC、ZGC 等),可以根据不同场景灵活选择。
-
安全沙箱
JVM 为 Java 程序提供了一个受控的运行环境,能够有效隔离恶意代码,防止其损害底层操作系统。
JVM 的核心组成部分
JVM 的内部结构
JVM 的内部结构主要包含以下几个关键模块:
| 组件 | 作用 |
|---|---|
| 类加载器(Class Loader) | 负责查找并加载 .class 字节码文件到 JVM 内存中 |
| 运行时数据区 | JVM 的内存管理区域,包含堆(存放对象)、方法区(存放类元数据)、虚拟机栈、程序计数器、本地方法栈 |
| 执行引擎 | 负责解释或编译执行字节码,包含解释器和 JIT 即时编译器(将热点代码编译成本地机器码以提升性能) |
| 本地方法接口(JNI) | 允许 Java 调用 C/C++ 等语言编写的本地方法库 |
JVM、JRE、JDK 的关系
这三者经常被放在一起提及,它们的包含关系如下:
JVM:Java 虚拟机本身,负责执行字节码。
JRE(Java Runtime Environment):Java 运行环境,包含 JVM + Java 核心类库,是运行 Java 程序的必备条件。
JDK(Java Development Kit):Java 开发工具包,包含 JRE + 开发工具(如编译工具 javac、调试工具等),是开发 Java 程序的必备条件。
可以这样理解:JDK 是工具箱,JRE 是运行平台,JVM 则是这个平台最核心的发动机。
2、 Attach
Java 的 Attach 机制,是 JDK 提供的一套允许外部进程动态连接到正在运行的 JVM 并注入代理程序的技术方案。它解决了 Java Agent 只能在 JVM 启动时通过 -javaagent 参数加载的限制,让开发者可以在程序运行期间随时进行诊断、监控和调试。
核心 API
Attach API 不是 Java 的标准 API,而是 Sun 公司提供的一套扩展 API,位于 com.sun.tools.attach 包中,主要包含两个类:
| 类名 | 作用 |
|---|---|
| VirtualMachine | 代表一个目标 JVM,提供 JVM 枚举、Attach 动作(连接)、加载代理、Detach 动作(断开连接)等核心操作 |
| VirtualMachineDescriptor | 描述虚拟机的容器类,配合 VirtualMachine 完成各种功能,如列出所有正在运行的 JVM 进程 |
工作流程
Attach 机制的运行流程大致分为以下几个步骤:
- 发现目标 JVM :Attach 进程首先需要找到目标 JVM。这通常通过操作系统的进程列表来实现,
jps命令就是基于这个原理工作的。 - 建立连接 :Attach 进程使用 Attach API 向目标 JVM 发送 Attach 请求。这个请求实际上是一个本地 Socket 连接,目标 JVM 会监听特定的 Socket 地址来接收请求。
- 加载 Agent :目标 JVM 接收到 Attach 请求后,会加载一个特殊的 Agent,这个 Agent 负责处理 Attach 进程发送的各种命令。在 Linux/Unix 系统上通常是
sun.tools.attach.BsdVirtualMachine,在 Windows 系统上则是sun.tools.attach.WindowsVirtualMachine。 - 执行命令:Attach 进程通过 Agent 向目标 JVM 发送各种命令,例如加载 JDWP Agent、获取 JVM 信息、执行线程转储等。
- 触发 agentmain :如果 Attach 进程请求加载自定义的 Agent JAR 包,目标 JVM 会执行该 JAR 包中指定的
agentmain方法,从而完成字节码增强、类重定义等操作。
底层原理
Attach 机制的实现依赖于底层的操作系统 API。例如在 Linux 上,它使用 ptrace 系统调用来访问目标进程的内存空间。当外部进程发起 Attach 请求时,操作系统层面的信号机制会通知目标 JVM,目标 JVM 内部的 Attach Listener 线程会被唤醒并处理后续命令。
与 premain 的对比
Java Agent 有两种加载方式,Attach 机制对应的是动态加载模式:
| 对比维度 | premain(静态加载) | agentmain(动态加载) |
|---|---|---|
| 加载时机 | JVM 启动时,在应用程序 main 方法执行之前 |
JVM 运行期间,随时可以触发 |
| 启动方式 | 通过 -javaagent 参数指定 |
通过 Attach API 动态附着 |
| 典型场景 | APM 监控探针、代码覆盖率工具 | 远程调试、线上诊断(Arthas)、热部署 |
| 是否需要重启 | 需要重启应用 | 不需要重启应用 |
主要应用场景
Attach 机制凭借"无需重启"的优势,被广泛应用于以下场景:
- 远程调试:通过动态加载 JDWP Agent,实现本地 IDE 对远端服务器程序的断点调试。
- 性能诊断:Arthas、VisualVM 等工具通过 Attach 连接到目标 JVM,实时采集线程状态、内存使用等性能数据。
- 热更新与热部署:在不重启服务的情况下,动态修改类定义或替换字节码。
- 安全测试:在授权环境中,通过 Attach 注入 Agent 型内存马进行渗透测试。
Attach 机制让 JVM 从一个封闭的运行环境变成了一个可被动态观测和干预的开放平台,极大地扩展了 Java 应用的可维护性和可调试性
3、Java Agent
Java Agent 是一种在 JVM 启动或运行时,对 Java 程序进行字节码修改和监控的探针技术。它可以在不改变原有代码的情况下,实现对程序行为的增强、监控或调试,常用于性能监控、日志埋点、热部署等场景。
核心原理
Java Agent 通过 JVM 提供的 Instrumentation API,在类加载或运行时动态修改字节码,实现类似 AOP(面向切面编程)的效果。它有两种加载方式:
- 静态加载 :通过
-javaagent:jar路径参数在 JVM 启动前加载,执行premain方法。 - 动态加载 :在 JVM 运行中通过 Attach API 动态注入,执行
agentmain方法。
典型应用场景
- 开发工具支持:如 IDEA、Eclipse 的调试功能。
- 热部署工具:如 JRebel,实现代码修改后无需重启应用。
- 线上诊断工具 :如阿里开源的 Arthas、Skywalking 等。
- 性能监控与追踪:如 APm 探针、Prometheus 监控集成。
如何使用
- 编写一个包含
premain或agentmain方法的类。 - 在
MANIFEST.MF文件中指定Premain-Class或Agent-Class。 - 将其打包为 JAR 文件,并通过
-javaagent参数启动目标程序。
二、Java内存马的表象
webshell可以正常访问,并且从log日志中可以看到webshell的访问状态码为200,但是在对应的目录中却找不到文件。如果存在此类情况,可以判断服务器中了内存马。
三、实现禁止Attach JVM的原理
禁止 JVM Attach 机制的核心原理是通过 JVM 启动参数 -XX:+DisableAttachMechanism 控制内部线程的初始化逻辑,从而阻断外部进程与目标 JVM 建立通信通道。
JVM 的 Attach 机制依赖于两个关键线程:"Signal Dispatcher"和"Attach Listener"。其中,"Signal Dispatcher"线程在 JVM 启动时始终创建,负责监听操作系统信号;而"Attach Listener"线程负责处理具体的 Attach 请求(如线程 dump、加载 Agent 等),它默认不会在启动时立即创建,而是由"Signal Dispatcher"在接收到特定信号后懒加载创建,或者通过参数 StartAttachListener 强制在启动时创建。
当设置 -XX:+DisableAttachMechanism 参数时,JVM 在初始化阶段会检查该标志位。如果该标志位为 true,JVM 将跳过"Attach Listener"线程的初始化代码,既不会在启动时创建该线程,也不会允许"Signal Dispatcher"在后续运行时创建它。由于缺少了处理 Attach 命令的核心线程,外部工具(如 jstack、jmap、Arthas 等)即使通过文件 Socket 或信号机制尝试连接目标 JVM,也无法得到响应或执行任何操作,从而实现了禁止 Attach 的效果。
此外,从 JDK 9 开始,为了增强安全性,默认配置进一步收紧,不仅可以通过上述参数完全禁用 Attach 机制,还默认禁止了动态加载 Agent(即 Attach API 中的 load 命令),除非显式启用 -XX:+EnableDynamicAgentLoading。这种设计旨在防止恶意代码通过 Attach 机制动态注入 JVM,提升了平台的安全性。
四、禁止Attach JVM情况下如何分析
1、还原被删除的Agent库文件(jar文件)
为防止被监控到,冰蝎 Agent 内存马会删除 /tmp/.java_pid4233 文件,该文件为AttachJVM时产生的socket文件。
可以使用命令恢复文件
bash
ls -la /proc/4233/fd/ | grep deleted | grep /tmp
然后通过日志查看,反向找到 冰蝎 源码
2、内存dump分析-MAT工具
将内存dump文件拖入MAT工具,打开dominator_tree 。搜索相关关键字:rebeyond
内存dump分析-关键词
bash
# 强制导出目标 JAVA 进程 22868 的堆内存转储
jmap -F -dump:format=b,file=heap.hprof 22868
jcmd 22868 GC.heap_dump /tmp/heap.hprof
# 查看堆栈中可疑类和方法
yum install binutils
# 1. 关键字简单分析 JAVA Heap
# "POST /memshell HTTP/1.1"
strings heap.bin | grep "POST /"
strings heap.bin | grep -E "/webapps/.*?\!" |sort -u
# 已知 memshell 特征
# fastjson 反序列化 classloader
strings heap.bin | grep 'com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$TransletClassLoader'
# behinder
strings heap.bin | grep -i 'rebeyond\|behinder\|Agent Injected Successfully\|MemShell'
# n1ntyfilter
strings heap.bin | grep -i 'inject n1ntyfilter injected'
# msf
strings heap.bin | grep 'com.metasploit.'
# 查找执行过的可疑 jsp 代码
# 寻找已知的恶意代码模式,如动态执行 Bash 命令、动态注册 /shell 路径、以及反射加载和解密操作。
strings heap.bin | grep -i 'new ProcessBuilder("/bin/bash","-c", req.getParameter("\|Runtime.getRuntime().exec(\|standardCtx.addServletMapping("/shell\|.getClass().getClassLoader()).g(c.doFinal('
# 寻找所有与进程执行、加密库和 Spring 映射机制相关的痕迹
strings heap.bin|grep -i 'javax.crypto.\|ProcessBuilder\|getRuntime\|ProcessImpl\|shell\|org.springframework.web.servlet.handler.AbstractHandlerMapping'
# 查找最近可疑访问记录
strings heap.bin | grep '\b\(whoami\|pwd\|ps\|netstat\)\b'