静态注入修改Java代码的一种方法

简介: 在Java开发中我们经常用Spring的AOP方式动态来拦截类方法的执行,做一些公共处理,比如日志、异常等等,这里我要介绍一种静态修改Java代码的方法,这种方法经常被用在破解一些Java工具上面,通过启动时静态修改Java代码,修改里面的处理逻辑达到破解的目的,当然这种方式也可以用于不适合使用Spring的项目中,比如比较有名的监控系统pinpoint就是通过此方式来记录方法的执行时间。 1、

在Java开发中我们经常用Spring的AOP方式动态来拦截类方法的执行,做一些公共处理,比如日志、异常等等,这里我要介绍一种静态修改Java代码的方法,这种方法经常被用在破解一些Java工具上面,通过启动时静态修改Java代码,修改里面的处理逻辑达到破解的目的,当然这种方式也可以用于不适合使用Spring的项目中,比如比较有名的监控系统pinpoint就是通过此方式来记录方法的执行时间。


1、在Maven中引入依赖包:

    <properties>
        <aspectj.version>1.8.13</aspectj.version>
        <javassist.version>3.24.1-GA</javassist.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>${javassist.version}</version>
        </dependency>
    </dependencies>


2、配置编译插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <archive>
            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
        </archive>
    </configuration>
</plugin>


3、在项目的src/man/resources目录下面创建META-INF目录,并添加MANIFEST.MF文件,文件内容为:

Agent-Class: com.nettm.exercise.agent.StaticAgent
Premain-Class: com.nettm.exercise.agent.StaticAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true


4、创建Agent类:

package com.nettm.exercise.agent;

import java.lang.instrument.Instrumentation;

public class StaticAgent {

    public static void premain(String args, Instrumentation inst) {
        TimeLogger transformer = new TimeLogger();
        inst.addTransformer(transformer);
    }

    public static void agentmain(String args, Instrumentation inst) {
        TimeLogger transformer = new TimeLogger();
        inst.addTransformer(transformer);
    }
}


5、创建修改Java类的处理逻辑,这里以添加打印方法执行时间为例:

package com.nettm.exercise.agent;

import javassist.*;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class TimeLogger implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer)
        throws IllegalClassFormatException {

        String clsName = className.replaceAll("/", ".");
        ClassPool pool = ClassPool.getDefault();
        pool.appendClassPath(new ClassClassPath(this.getClass()));
        pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
        pool.appendSystemPath();

        CtClass ctclass;
        try {
            ctclass = pool.get(clsName);
        } catch (Exception e) {
            System.out.println("class not found: " + className);
            return classfileBuffer;
        }

        if (ctclass.isInterface() || ctclass.isAnnotation() || ctclass.isPrimitive() || ctclass.isEnum() || ctclass
            .isFrozen() || ctclass.isModified()) {
            return classfileBuffer;
        }

        try {
            for (CtBehavior method : ctclass.getDeclaredBehaviors()) {
                String methodName = method.getName();

                // 得到这方法实例
                CtMethod ctmethod;
                try {
                    ctmethod = ctclass.getDeclaredMethod(methodName);
                } catch (Exception e) {
                    continue;
                }

                String val = "消耗时间:" + clsName + "." + methodName + ":";
                ctmethod.addLocalVariable("strTime", CtClass.longType);
                ctmethod.addLocalVariable("endTime", CtClass.longType);
                ctmethod.addLocalVariable("cost", CtClass.longType);
                ctmethod.insertBefore("strTime = System.currentTimeMillis();");
                ctmethod.insertAfter("endTime = System.currentTimeMillis();\n");
                ctmethod.insertAfter("cost = endTime - strTime;\n");
                ctmethod.insertAfter(
                    "if (cost > 1000) {\n" + "System.out.println(\"" + val + "\" + (endTime - strTime));\n" + "}");
            }
            byte[] buf = ctclass.toBytecode();
            ctclass.detach();
            return buf;
        } catch (Exception e) {
            System.out.println("编译错误:" + clsName + ":" + e.getMessage());
        }

        return classfileBuffer;
    }

}


6、使用方法
通过Maven命令打成Jar包,在其他项目中添加启动参数,如:

-javaagent:exercise-agent-0.0.1-SNAPSHOT.jar


备注:
1、Javassist是一种可以在JVM加载类之前修改Java字节码的工具。
2、Instrumentation API是JVM提供的当JVM加载类时修改Java字节码的工具。


参考:
1、https://www.baeldung.com/java-instrumentation[](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html)
/>2、
https://www.javassist.org/tutorial/tutorial.html
3、https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html

目录
相关文章
|
2月前
|
Java 开发工具
【Azure Storage Account】Java Code访问Storage Account File Share的上传和下载代码示例
本文介绍如何使用Java通过azure-storage-file-share SDK实现Azure文件共享的上传下载。包含依赖引入、客户端创建及完整示例代码,助你快速集成Azure File Share功能。
344 5
|
3月前
|
IDE Java 关系型数据库
Java 初学者学习路线(含代码示例)
本教程为Java初学者设计,涵盖基础语法、面向对象、集合、异常处理、文件操作、多线程、JDBC、Servlet及MyBatis等内容,每阶段配核心代码示例,强调动手实践,助你循序渐进掌握Java编程。
427 3
|
3月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
296 3
|
2月前
|
Java 数据处理 API
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
245 115
|
2月前
|
安全 Java 编译器
为什么你的Java代码需要泛型?类型安全的艺术
为什么你的Java代码需要泛型?类型安全的艺术
178 98
|
3月前
|
Java
java入门代码示例
本文介绍Java入门基础,包含Hello World、变量类型、条件判断、循环及方法定义等核心语法示例,帮助初学者快速掌握Java编程基本结构与逻辑。
409 0
|
2月前
|
安全 Java 容器
告别繁琐判空:Optional让你的Java代码更优雅
告别繁琐判空:Optional让你的Java代码更优雅
|
2月前
|
安全 Java 容器
告别空指针噩梦:Optional让Java代码更优雅
告别空指针噩梦:Optional让Java代码更优雅
370 94
|
2月前
|
Java 编译器 API
java最新版和java8的区别,用代码展示
java最新版和java8的区别,用代码展示
253 43