JavaAgent有兩種實現機制,一種是在Java進程啓動的時候在jvm啓動參數通過--javaagent將帶來jar掛載,比如註明的pinpont就是通過這種方式實現,另外一種是動態掛載,比如阿里巴巴Arthas就是通過這種方式,掛載以後可以通過javassit、ASM、CGLib等字節碼修改技術實現劫持。
Demo示例工作過程。
1.第一步生成編寫Agent代碼(此處是一個maven項目),監聽被劫持進程的 com.yzy.arthas.agent.UserService的sayHello方法。
2.編譯maven項目生成jar,啓動要代理的JVM進程(com.yzy.arthas.agent.Main), 然後運行下面的 main方法
- 創建一個maven項目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>myagent</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.24.1-GA</version>
</dependency>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8.0</version>
<scope>system</scope>
<systemPath>/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/tools.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>test.agent.AgentMain</Premain-Class>
<Agent-Class>test.agent.AgentMain</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 創建Agent類
package test.agent;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
/**
* Created by yzy on 2021-06-08
*/
public class AgentMain {
/**
* premain 啓動被代理進程的時候需要加上-javaagent 參數,才能代理成功
* -javaagent:/Users/yzy/Desktop/code2/lib/myagent-1.0-SNAPSHOT.jar
* @param agentArgs
* @param inst
*/
public static void premain(String agentArgs, Instrumentation inst){
}
/**
* agentmain ,目標進程啓動以後仍然可以劫持, 更加方便
* @param agentArgs
* @param instrumentation
* @throws ClassNotFoundException
* @throws UnmodifiableClassException
*/
public static void agentmain(String agentArgs, Instrumentation instrumentation)
throws ClassNotFoundException, UnmodifiableClassException {
instrumentation.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer){
System.out.println("premain load Class2:" + className);
if(!"com/yzy/arthas/agent/UserService".equals(className)){
return null;
}else {
try {
return buildMonitorClass();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
},true);
instrumentation.retransformClasses(Class.forName("com.yzy.arthas.agent.UserService"));
}
public static byte[] buildMonitorClass() throws NotFoundException, CannotCompileException, IOException {
ClassPool classPool=new ClassPool();
classPool.appendSystemPath();
CtClass ctClass = classPool.get("com.yzy.arthas.agent.UserService");
CtMethod ctMethod = ctClass.getDeclaredMethod("sayHello");
ctMethod.insertBefore("System.out.println(\"開始執行yyy:\"+(System.nanoTime()));");
ctMethod.insertAfter("System.out.println(\"結束執行yyy:\"+(System.nanoTime()));");
return ctClass.toBytecode();
}
}
3.編譯maven項目生成jar,啓動要代理的JVM進程(com.yzy.arthas.agent.Main), 然後運行下面的 main方法
package test;
import java.util.List;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
/**
* Created by yzy on 2021-06-08
*/
public class Boot {
public static void main(String[] args) {
List<VirtualMachineDescriptor> list = VirtualMachine.list();
try{
for (VirtualMachineDescriptor vmd : list) {
System.out.println(vmd.displayName());
if (vmd.displayName().endsWith("com.yzy.arthas.agent.Main")) {
VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
virtualMachine.loadAgent("/Users/yzy/Desktop/myagent/target/myagent-1.0-SNAPSHOT.jar");
System.out.println("load agent");
virtualMachine.detach();
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}