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

“预期输出信息”是:
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 研发阶段最紧迫的问题之一。