Bootstrap

RASP研发踩坑之agent 加载机制(1)

上图是一个典型的Java-Agent实现原理图,外部进程执行attach 后 agent.jar包被目标JVM 的 AppClassLoader 加载。由于 JVM类加载机制的限制,同一个 jar 包无法被 AppClassLoader对象加载第二次。

请看下面的程序演示说明,agent 无法被加载的原因。

01.第一次 attach

主动发起attach的代码

public class Main {
    public static void main(String[] args) {
        VirtualMachine vm = null;
        try {
            vm = VirtualMachine.attach("24968");
            // 第一次 attach使用的 agent.jar
            String agentPath = "/Users/xule05/Desktop/attachDemo/src/main/resources/v1/agent-1.0-SNAPSHOT-jar-with-dependencies.jar";
            // 第二次 attach使用的 agent.jar,两个agent.jar 包名称一样,实现的功能不一样
            //String agentPath = "/Users/xule05/Desktop/attachDemo/src/main/resources/v2/agent-1.0-SNAPSHOT-jar-with-dependencies.jar";
            String cfgStr = "agent init";
            vm.loadAgent(agentPath, cfgStr);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                vm.detach();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

agent.jar代码

public class Agent {
    public static void premain(String param, Instrumentation inst) {
        main(param, inst);
    }

    public static void agentmain(String param, Instrumentation inst) {
        main(param, inst);
    }

    private synchronized static void main(String args, final Instrumentation inst) {
        // 打印 attach时传入的参数
        System.out.println("args: " + args);
        // System.out.println("(v2)args: " + args); // V2版本agent
    }
}

启动一个 Java 进程[PID=24968],执行attach

web 服务输出日志信息:args: agent init

02.第二次 attach

Java进程的 PID=24968,使用 V2版本的 agent.jar,V2版本与V1版本的 agent.jar,jar包名称一样,区别仅仅在于初始化后输出的字符串不同。(V2版本可以看成是 V1版本的迭代版本或者 bug修复版本)

web 服务输出日志信息:args: agent init 一样的!!!

“预期输出信息”是:(v2)args: agent init 实际却不是!!!

03 重启目标 JVM

Java进程[PID=25606],再次执行 attach, V2版本的 agent.jar 被加载并运行

04 结论

同一个 agent.jar 包( jar名称相同)不能被 JVM 二次加载,这意味想要增加新的功能或修复bug,必须等待目标JVM进程重启(重启后,已经加载的Agent对象没了)。也就是说,RASP功能的升级完全依赖于目标JVM进程的重启时机, 令人沮丧的是,线上有些业务重启时间长达6个月甚至更长时间,这使得 RASP的功能很长时间内都无法更新。特别是需要紧急修复RASP的bug 时,升级难的问题更突出。

(原生Java Agent 机制不具备热更新功能)

05 出路

如何解决升级问题是 RASP 研发阶段最紧迫的问题之一。