Java字节码增强应用

Java字节码增强技术

认识Instrumentation

 

基本用法:

方法 作用
addTransformer 织入一段代码
removeTransformer 移除被织入的代码
retransformClasses 用于已经载入的类重新植入 Class... classes

其他用法:

方法 作用
appendToBootstrapClassLoaderSearch 将指定路径的jar包用bootstrapclassloader查找路径
getAllLoadedClasses 获取所有已加载的类
getObjectSize 获取指定的对象的大小
isNativeMethodPrefixSupported 是否拦截本地方法

Permain的方式植入代码

程序测试代码

 

package huster.top;

/**
 * Created by longan.rtb on 2019/7/8.
 */
public class JavaAgentTest {

    public String helloWorld() {
        for (int i = 0; i<5; i++) {
            System.out.println("Hello world!");
        }
        return "ok";
    }

    public static void main(String[] args) {
        JavaAgentTest javaAgentTest = new JavaAgentTest();
        javaAgentTest.helloWorld();
    }
}

 

Agent代码(permain方式)

 

package huster.top;

import java.lang.instrument.Instrumentation;

/**
 * Created by longan.rtb on 2019/7/8.
 */
public class AgentMain  {

    public static void premain(String args, Instrumentation inst) {
       System.out.println("permain has been called, arguments is : " + args);
       inst.addTransformer(new AOPImplement());
    }
}

 

public class AOPImplement implements ClassFileTransformer {

    private final static String methodName = "helloWorld";

    private final static String targetClassName = "huster.top.JavaAgentTest";

   public byte[]
    transform(  ClassLoader         loader,
                String              className,
                Class            classBeingRedefined,
                ProtectionDomain protectionDomain,
                byte[]              classfileBuffer)
            throws IllegalClassFormatException {
       className = className.replace("/", ".");
       if (!className.equals(targetClassName)) {
           //只监控需要的类
           return classfileBuffer;
       }

       CtClass ctclass = null;
       try {
           ctclass = ClassPool.getDefault().get(className);
           //只监控需要的方法
           CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);
           //旧方法改个名字
           String oldMethod = methodName + "_____old____";
           ctmethod.setName(oldMethod);
           CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);
           String methodBody = "{" +
                        "System.out.println(\"before called!!\");\n" +
                        "String a = " + oldMethod + "($$);\n" +
                        "System.out.println(\"after method called!!\");\n" +
                        "return a;" +
                   "}";
           newMethod.setBody(methodBody);
           ctclass.addMethod(newMethod);
           return ctclass.toBytecode();
       } catch (Exception e) {
           e.printStackTrace();
           return classfileBuffer;
       }

   }
}

 

pom.xml

 



    
        javaagent-demo
        huster.top
        1.0-SNAPSHOT
    
    4.0.0

    original-agent

    
        
            javassist
            javassist
            3.12.1.GA
        
    

    
        original-agent
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                    utf-8
                
            
            
                org.apache.maven.plugins
                maven-assembly-plugin
                3.0.0
                
                    
                        
                            true
                        
                        
                            huster.top.AgentMain
                        
                    
                    
                        jar-with-dependencies
                    
                
                
                    
                        make-assembly
                        package
                        
                            single
                        
                    
                
            
        
    

 

 

然后在测试代码的vm参数里增加:

-javaagent:/Users/longan.rtb/JavaProject/javaagentdemo/originalagent/target/original-agent-jar-with-dependencies.jar=args1;args2;args3

 

运行结果如下:

 

 

 

Attach的方式植入代码

attach的程序

 

public class AttachAgent {

    public static void main(String[] args)  throws Exception {
        String pid = args[0];
        String jar = args[1];
        if (jar == null  || "".equals(jar)) {
            jar = "/Users/longan.rtb/JavaProject/javaagentdemo/originalagent/target/original-agent-jar-with-dependencies.jar";
        }
        VirtualMachine vmObj = null;
        try {
            vmObj = VirtualMachine.attach(pid);
            vmObj.loadAgent(jar,
                    "this is arguments");
        } finally {
            if (vmObj != null) {
                vmObj.detach();
            }

        }
    }
}

 

agent的代码修改为

 

public class AgentMain  {

    public static void premain(String args, Instrumentation inst) {
       System.out.println("permain has been called, arguments is : " + args);
       inst.addTransformer(new AOPImplement());
    }

    //attach方式.
    public static void agentmain(String args, Instrumentation inst){
        System.out.println("444agentmain has been called, arguments is : " + args);
        System.out.println("is enable :" + String.valueOf(inst.isRetransformClassesSupported()));
        inst.addTransformer(new AOPImplement2(),true);
        try {
            inst.retransformClasses(Class.forName("huster.top.JavaAgentTest"));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

 

坑1: 始终报UnsupportedOperationException错误

是因为我们在植入代码的时候,使用了新增函数的方法,这种方法的是不允许的在Retrans的时候

* Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method* The retransformation may change method bodies, the constant pool and attributes.* The retransformation must not add, remove or rename fields or methods, change the* signatures of methods, or change inheritance.  These restrictions maybe be* lifted in future versions.  The class file bytes are not checked, verified and installed* until after the transformations have been applied, if the resultant bytes are in* error this method will throw an exception.

 

 

将attach程序打包成jar运行

pom.xml

 



    
        javaagent-demo
        huster.top
        1.0-SNAPSHOT
    
    4.0.0


    attach-agent

    
        
            com.sun
            tools
            1.8
            system
            ${java.home}/../lib/tools.jar
        
    
    
        
            
                org.apache.maven.plugins
                maven-assembly-plugin
                
                    
                        
                            attached
                        
                        package
                        
                            
                                jar-with-dependencies
                            
                            
                                
                                    huster.top.AttachAgent
                                
                            
                        
                    
                
            
        
    

 

坑2: VirtualMachine找不到的问题

需要我们在运行的时候指明tool.jar的位置

/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/bin/java -jar -Xbootclasspath/a:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/tools.jar /Users/longan.rtb/JavaProject/javaagentdemo/attachagent/target/attach-agent-1.0-SNAPSHOT-jar-with-dependencies.jar 29808

 

 

作者: 龙安_任天兵

不忘初心,方得始终!