阿里巴巴Arthas原理-JavaAgent技術

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方法

  1. 創建一個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>
  1. 創建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();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章