涛声依旧在 发表于 2024-6-19 21:40:37

代码编译安全之classfinal-maven-plugin插件

ClassFinal-maven-plugin插件是一个用于加密Java字节码的工具
开源地址官网:ClassFinal: Java字节码加密工具 (目前已经停息维护了)
工作原理

        ClassFinal-maven-plugin插件通过调用Java Native Interface(JNI)实现对Java字节码的加密。具体来说,它会在编译阶段对类文件进行混淆和加密,然后在运行时动态解密这些类文件。
        起首,插件会遍历项目中的全部类文件,并对其进行混淆处理。混淆过程包括重定名类名、方法名、字段名以及改变控制流结构等,目的是使反编译后的代码难以阅读。
        接着,插件会对混淆后的类文件进行加密处理。加密过程采用了一种名为CFProtect的算法,该算法基于AES加密标准,具有较高的安全性。加密后的类文件存储为二进制格式,不能直接被Java虚拟机加载。当class被classloader加载时,真正的方法体会被解密注入。运用的是Java Agent可以去实现字节码插桩、动态跟踪分析。比如skywalking、arms等就是Java Agent技能。
        末了,插件会生成一个署理模块(agent module),该模块负责在应用程序启动时加载,并负责解密加密的类文件。署理模块采用JVMTI(Java Virtual Machine Tool Interface)技能实现,可以在运行时监控和控制Java虚拟机的行为。

加密结果

1.加密后,方法体被清空,保留方法参数、注解等信息.重要兼容swagger文档注解扫描
2.方法体被清空后,反编译只能看到方法名和注解,看不到方法体的具体内容
3.加密后的项目必要设置javaagent来启动,启动过程中解密class,完全内存解密,不留下任何解密后的文件
4.启动加密后的jar,生成xxx-encrypted.jar,这个就是加密后的jar文件,加密后不可直接执行
5.无密码启动方式,java-javaagent:xxx-encrypted.jar-jarxxx-encrypted.jar
6.有密码启动方式,java-javaagent:xxx-encrypted.jar='-pwd=密码'-jarxxx-encrypted.jar
加密方法

直接增加插件即可,写在Springboot的spring-boot-maven-plugin后面即可,留意修改要加密的packages配置
          <plugin>
                <!--
1.加密后,方法体被清空,保留方法参数、注解等信息.主要兼容swagger文档注解扫描
2.方法体被清空后,反编译只能看到方法名和注解,看不到方法体的具体内容
3.加密后的项目需要设置javaagent来启动,启动过程中解密class,完全内存解密,不留下任何解密后的文件
4.启动加密后的jar,生成xxx-encrypted.jar,这个就是加密后的jar文件,加密后不可直接执行
5.无密码启动方式,java-javaagent:xxx-encrypted.jar-jarxxx-encrypted.jar
6.有密码启动方式,java-javaagent:xxx-encrypted.jar='-pwd=密码'-jarxxx-encrypted.jar
-->
                <!-- https://gitee.com/roseboy/classfinal -->
                <groupId>net.roseboy</groupId>
                <artifactId>classfinal-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                  <password>#</password><!--加密打包之后pom.xml会被删除,不用担心在jar包里找到此密码-->
<!--packages配置要加入的包名,会把下面的所有类加密-->
             <packages>com.yulang.proguard.demo.utils,com.yulang.proguard.demo.controller</packages>
<!--                  <cfgfiles>application.properties</cfgfiles>-->
                  <excludes>org.spring</excludes>
<!--                  <libjars>a.jar,b.jar</libjars>-->
                </configuration>
                <executions>
                  <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>classFinal</goal>
                        </goals>
                  </execution>
                </executions>
            </plugin> 实践

加密的结果,将全部的方法内容都清空了
https://img-blog.csdnimg.cn/direct/3c4560e71a564680ab59dc138cfc2488.pnghttps://img-blog.csdnimg.cn/direct/9bf88a4646bb4257adafaeb826b07363.png
应用启动方法:
java -javaagent:proguard-demo-1.0-SNAPSHOT-encrypted.jar -jar proguard-demo-1.0-SNAPSHOT-encrypted.jar 问题

启动报错
17:39:11.916 DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7b9a4292
17:39:11.923 DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
17:39:11.949 ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.
      at org.springframework.util.Assert.notEmpty(Assert.java:470)
      at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getCandidateConfigurations(AutoConfigurationImportSelector.java:180)
      at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:123)
      at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:434)
      at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:879)
      at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809)
      at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780)
      at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:192)
      at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331)
      at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)
      at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
      at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)
      at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
      at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)
      at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
      at com.yulang.proguard.demo.ProguardApp.main(ProguardApp.java:19)
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.base/java.lang.reflect.Method.invoke(Method.java:566)
      at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
      at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
      at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
      at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
办理办法,表明掉application的加密和jar包加密就好了
测试

测试启动过程中,除了增加点启动日志,并无什么影响,对于业务日志也没有影响
https://img-blog.csdnimg.cn/direct/feab030e911d43e58c96a615e40c4571.png
可以看到日志的行号也不会做任何的变动 ,相比较商用Allatori混淆,默认会混淆行号,导致日志排查起来相当困难
https://img-blog.csdnimg.cn/direct/b202579b9d1c478abcf18378d233dc21.png
缺点

通过dump仍旧能看到源码,但是相对来说,增加了破解的难度
加密解密开销:每次加载类时都必要进行解密利用,这会增加额外的CPU计算时间。如果加密算法复杂度较高,解密过程可能会成为性能瓶颈。
内存斲丧:加密后的类文件可能占用更多存储空间,并且在运行时必要更多的内存来存放解密后的字节码数据。
类加载时间:由于增加相识密环节,类加载的时间会有所增加,特殊是在启动阶段或大量类必要加载的时候,可能会感受到明显的延迟。
JIT编译:Java虚拟机(JVM)通常会对热点代码进行即时(Just-In-Time,JIT)编译优化以提高执行服从。加密后,JVM可能无法直接识别并优化这些代码,从而影响执行速度。
目前已经停息维护了,后期如果存在问题,必要自己修复

扩展

多模块项目,只必要在对应模块配置相应的jar包即可
<libjars> 呆板绑定

获取呆板码jar包:classfinal-fatjar-1.2.1.jar 
下载地址:https://repo1.maven.org/maven2/net/roseboy/classfinal-fatjar/1.2.1/classfinal-fatjar-1.2.1.jar
呆板绑定只答应加密的项目在特定的呆板上运行;
在必要绑定的呆板上执行以下下令,生成呆板码
java -jar classfinal-fatjar-1.2.1.jar -C
加密时用-code指定呆板码。呆板绑定可同时支持呆板码+密码的方式加密。
https://img-blog.csdnimg.cn/direct/a87e4cd46e714e56b502b8484bf96929.png
如果拷贝到其他呆板上运行,则会报错
https://img-blog.csdnimg.cn/direct/839f632e78be44f9b479552d50c4b85d.png
启动加密后的jar

java -javaagent:yourpaoject-encrypted.jar='-pwd 0000000' -jar yourpaoject-encrypted.jar
//参数说明
// -pwd      加密项目的密码
// -pwdname环境变量中密码的名字
插件中配置的密码
<password>#</password><!--加密打包之后pom.xml会被删除,不用担心在jar包里找到此密码--> 后记

其他原理类似:https://github.com/core-lib/xjar

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 代码编译安全之classfinal-maven-plugin插件