河曲智叟 发表于 2024-12-15 15:57:23

java agent-03-Java Instrumentation 结合 bytekit 实战条记 agent attach

java agent 系列

java agent 先容
java agent-02-Java Instrumentation API
java agent-03-Java Instrumentation 结合 bytekit 实战条记 agent attach
java agent-03-Java Instrumentation 结合 bytekit 实战条记 agent premain
拓展阅读

前面几篇文档,我们简朴先容了一下 java Instrumentation。
java agent 先容
Java Instrumentation API
本篇我们结合一下 bytekit 进行现实的文件修改。
测试代码

团体目次

    ││└─com
    ││      └─github
    ││          └─houbb
    ││            └─bytekit
    ││                  └─learn
    ││                      └─agentattach
    ││                        │AgentAttachMain.java
    ││                        │MyAttachMain.java
    ││                        │MyClassFileTransformer.java
    ││                        │package-info.java
    ││                        │
    ││                        └─interceptor
    ││                                  SampleInterceptor.java
    ││
    │└─resources
    │      └─META-INF
    │            MANIFEST.MF
    │
    └─test
      └─java
            └─com.github.houbb.bytekit.learn.agentattach
                  Sample.java
                  TestMain.javaMANIFEST.MF

Manifest-Version: 1.0
Agent-Class: com.github.houbb.bytekit.learn.agentattach.AgentAttachMain
Can-Redefine-Classes: true
Can-Retransform-Classes: trueAgentAttachMain-核心入口

这里指定了核心入口 AgentAttachMain

[*]AgentAttachMain.java
动态 Attach 的 agent 与通过 JVM 启动 javaagent 参数指定的 agent jar 包的方式有所差异,动态 Attach 的 agent 会执行 agentmain 方法,而不是 premain 方法。
package com.github.houbb.bytekit.learn.agentattach;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;

public class AgentAttachMain {


    /**
   * 动态 Attach 的 agent 会执行 agentmain 方法,而不是 premain 方法。
   *
   * @param agentArgs
   * @param inst
   * @throws ClassNotFoundException
   * @throws UnmodifiableClassException
   */
    public static void agentmain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException {
      System.out.println("agentmain called");

      Class classes[] = inst.getAllLoadedClasses();
      for (int i = 0; i < classes.length; i++) {
            String className = classes.getName();
            System.out.println(className);
            // 这里是正常的全称
            if (className.equals("com.github.houbb.bytekit.learn.agentattach.Sample")) {
                System.out.println("Reloading start: " + className);

                // 真实的替换
                final ClassFileTransformer transformer = new MyClassFileTransformer();
                inst.addTransformer(transformer, true);
                inst.retransformClasses(classes);
                inst.removeTransformer(transformer);

                System.out.println("Reloading done: " + className);
                break;
            }
      }
    }

}MyClassFileTransformer 和上一篇雷同,这里不外利用 bytekit 时要区分一下 install 的方式,或者会卡主。
此处不再赘述。
attach-main

创建MyAttachMain类,实现attach到目标进程 (为了方便我还是放在agent项目中)
由于是跨进程通信,Attach 的发起端是一个独立的 java 程序,这个 java 程序会调用 VirtualMachine.attach 方法开始和目标 JVM 进行跨进程通信。
下面的PID通过jps查看对应的进程ID,如11901
jarPath 为当前 agent 的完备包路径。
package com.github.houbb.bytekit.learn.agentattach;

import com.github.houbb.bytekit.tool.utils.AttachHelper;
import com.sun.tools.attach.VirtualMachine;

public class MyAttachMain {

    /**
   * 指定 pid 进行 attch
   *
   * @param args
   * @throws Exception
   */
    public static void main(String[] args) throws Exception {
      String pid = "15708";
      String jarPath = "D:\\github\\bytekit-learn\\bytekit-learn-agentattach\\target\\bytekit-learn-agentattach-1.0-SNAPSHOT.jar";
      AttachHelper.attach(jarPath, pid);

      // 通过 jps 查看
      VirtualMachine vm = VirtualMachine.attach(pid);
      try {
            vm.loadAgent(jarPath);
      } finally {
            vm.detach();
      }
    }

}测试

启动测试类

为了演示 attach,我们提供一个不绝循环的测试类:
public class TestMain {

    public static void main(String[] args) throws InterruptedException {
      while (true) {
            Sample sample = new Sample();
            String result = sample.hello("123", false);
            System.out.println(result);

            TimeUnit.SECONDS.sleep(3);
      }
    }

}首先启动测试类。通过 jps 获取对应的信息
14028 RemoteMavenServer36
15100 Launcher
15708 TestMain编译 agent

通过 mvn clean install 编译我们的 agent 包,生成在路径:
D:\\github\\bytekit-learn\\bytekit-learn-agentattach\\target\\bytekit-learn-agentattach-1.0-SNAPSHOT.jar修改 attach-main 并启动

直接把 MyAttachMain 的 pid + agent 路径修改为对应的。启动测试:
对应的日记如下,实现已经被替换了
com.github.houbb.bytekit.learn.agentattach.Sample
Reloading start: com.github.houbb.bytekit.learn.agentattach.Sample
start transform name=== com/github/houbb/bytekit/learn/agentattach/Sample
/*
* Decompiled with CFR.
*/
package com.github.houbb.bytekit.learn.agentattach;

public class Sample {
    private int exceptionCount = 0;

    /*
   * WARNING - void declaration
   */
    public String hello(String string, boolean bl) {
      try {
            String string2;
            void str;
            void exception;
            try {
                String string3 = "(Ljava/lang/String;Z)Ljava/lang/String;";
                String string4 = "hello";
                Object[] objectArray = new Object[]{string, new Boolean(bl)};
                Class<Sample> clazz = Sample.class;
                Sample sample = this;
                System.out.println("atEnter, args: " + objectArray);
            }
            catch (RuntimeException runtimeException) {
                Class<Sample> clazz = Sample.class;
                RuntimeException runtimeException2 = runtimeException;
                System.out.println("exception handler: " + clazz);
                runtimeException2.printStackTrace();
            }
            if (exception != false) {
                ++this.exceptionCount;
                throw new RuntimeException("test exception, str: " + (String)str);
            }
            String string5 = string2 = "hello " + (String)str;
            System.out.println("atExit, returnObject: " + string5);
            return string2;
      }
      catch (RuntimeException runtimeException) {
            int n = this.exceptionCount;
            RuntimeException runtimeException3 = runtimeException;
            System.out.println("atExceptionExit, ex: " + runtimeException3.getMessage() + ", field exceptionCount: " + n);
            throw runtimeException;
      }
    }
}

end transform name=== com/github/houbb/bytekit/learn/agentattach/Sample
Reloading done: com.github.houbb.bytekit.learn.agentattach.Sample
atEnter, args: 123
atExit, returnObject: hello 123
hello 123
atEnter, args: 123
atExit, returnObject: hello 123
hello 123
atEnter, args: 123
atExit, returnObject: hello 123
hello 123
atEnter, args: 123
atExit, returnObject: hello 123
hello 123拓展阅读

VirtualMachine 类不存在

添加jdk tools.jar解决com.sun.tools.attach.VirtualMachine 类找不到的标题
发现设置了 java_home 及相关信息还是不可,可以手动在项目中引入。
idea 就是 libs 种添加依赖。
参考资料

https://blog.51cto.com/zhangxueliang/5667216
https://www.cnblogs.com/756623607-zhang/p/12575509.html

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: java agent-03-Java Instrumentation 结合 bytekit 实战条记 agent attach