代码加固:使用ClassFinal实现对代码加密混淆
一、背景
在部分软件项目中,存在需要将业务代码提供给第三方使用的场景,比如提供一个Java Jar SDK包给第三方引用,提供一个Java Jar启动包部署到第三方环境。如果我们不想让第三方通过逆向工程反编译程序包,防止代码泄露,我们一般需要对程序包的代码做加密或者混淆。
本文通过使用ClassFinal对项目中的业务代码进行加密打包,从而实现对程序包的代码加固和反逆向破解。
二、组件介绍
ClassFinal
ClassFinal是一款java class文件安全加密工具,支持直接加密jar包或war包,无需修改任何项目代码,兼容spring-framework;可避免源码泄漏或字节码被反编译。
功能特性:
1.无需修改原项目代码,只要把编译好的jar/war包用本工具加密即可。
2.运行加密项目时,无需求修改tomcat,spring等源代码。
3.支持普通jar包、springboot jar包以及普通java web项目编译的war包。
4.支持spring framework、swagger等需要在启动过程中扫描注解或生成字节码的框架。
5.支持maven插件,添加插件后在打包过程中自动加密。
6.支持加密WEB-INF/lib或BOOT-INF/lib下的依赖jar包。
7.支持绑定机器,项目加密后只能在特定机器运行。
8.支持加密springboot的配置文件。
GItHub地址:https://github.com/roseboy/classfinal
三、项目集成
下文以一个采用Maven打包的SpringBoot多子项目工程为例,介绍如何使用ClassFinal对业务代码进行加密。(ClassFinal也支持对单独的程序包,比如jar,通过命令行的方式进行代码加密)
项目结构如下图所示:
该项目包含yhaf-admin-base-api、yhaf-admin-base-servic、yhaf-admin-base-security-core、yhaf-admin-base-security、yhaf-admin-base-web等多个子模块。yhaf-admin-base-web子模块依赖其他子模块,并最终通过maven打包出Jar运行包。
我们想要实现的目标是对最终交付的Jar运行包加密yhaf-admin-base-api、yhaf-admin-base-servic、yhaf-admin-base-security-core、yhaf-admin-base-security、yhaf-admin-base-web等多个子模块的业务代码。
1.使用ClassFinal对业务代码加密
首先,在子模块yhaf-admin-base-web的pom文件引入ClassFinal插件。
<build>
<plugins>
<!--springBoot项目打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<!--classfinal项目打包插件-->
<plugin>
<groupId>net.roseboy</groupId>
<artifactId>classfinal-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<!--加密密码,如果是#号,则使用无密码模式加密,加密打包之后pom.xml会被删除,不用担心在jar包里找到此密码-->
<password>shemg</password>
<!--加密的包名(可为空,多个用","分割)-->
<packages>com.yhtech.backend</packages>
<!--需要加密的配置文件,一般是classes目录下的yml或properties文件(可为空,多个用","分割)-->
<cfgfiles>application.yml</cfgfiles>
<!--排除的类名(可为空,多个用","分割)-->
<excludes>org.spring</excludes>
<!--jar/war包lib下要加密jar文件名(可为空,多个用","分割)-->
<libjars>
yhaf-admin-base-api-1.6.6-SNAPSHOT.jar,
yhaf-admin-base-service-1.6.6-SNAPSHOT.jar,
yhaf-admin-base-security-core-1.6.6-SNAPSHOT.jar,
yhaf-admin-base-security-1.6.6-SNAPSHOT.jar
</libjars>
<debug>true</debug>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>classFinal</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
参数说明
-file 加密的jar/war完整路径
-packages 加密的包名(可为空,多个用","分割)
-libjars jar/war包lib下要加密jar文件名(可为空,多个用","分割)
-cfgfiles 需要加密的配置文件,一般是classes目录下的yml或properties文件(可为空,多个用","分割)
-exclude 排除的类名(可为空,多个用","分割)
-classpath 外部依赖的jar目录,例如/tomcat/lib(可为空,多个用","分割)
-pwd 加密密码,如果是#号,则使用无密码模式加密
-code 机器码,在绑定的机器生成,加密后只可在此机器上运行
-Y 无需确认,不加此参数会提示确认以上信息
上述配置中,libjars配置了对yhaf-admin-base-web引用的yhaf-admin-base-api、yhaf-admin-base-servic、yhaf-admin-base-security-core、yhaf-admin-base-security等子模块的jar包进行加密。packages配置了加密的代码包名,password配置了密码,如果是#则使用无密码加密方式,插件将自动生成一个随机密码进行加密。
使用mvn clean package命令进行打包,打包后将生成一个yhaf-admin-base-web-1.6.6-SNAPSHOT-encrypted.jar加密包
我们使用解压工具对yhaf-admin-base-web-1.6.6-SNAPSHOT-encrypted.jar进行解压,可以看到这个包是一个正常的springboot项目结构
正常springboot项目的class文件在\BOOT-INF\classes\路径下,我们在这个路径下查看业务代码,通过反编译工具可以看到,ClassFinal加密完代码后,原始的class文件并不会完全被加密,只是方法体被清空,保留方法参数、注解等信息,这是为了兼容spring,swagger等扫描注解的框架; 方法体被清空后,反编译者只能看到方法名和注解,看不到方法的具体内容;
真实的代码和配置被移动到\META-INF.classes\中加密保存
现在,使用正常的jar启动命令已无法正常启动程序,系统将提示:Startup failed, invalid password.
启动加密后的jar命令:
java -javaagent:yhaf-admin-base-web-1.6.6-SNAPSHOT-encrypted.jar -jar yhaf-admin-base-web-1.6.6-SNAPSHOT-encrypted.jar
如果密码不是设置为#,则在启动中会提示输入密码,输入正确密码后程序将正常启动
也可以使用如下命令进行启动:
java -javaagent:yhaf-admin-base-web-1.6.6-SNAPSHOT-encrypted.jar='-pwd shemg' -jar yhaf-admin-base-web-1.6.6-SNAPSHOT-encrypted.jar
//参数说明
// -pwd 加密项目的密码
// -pwdname 环境变量中密码的名字
ClassFinal使用javaagent机制,当class被classloader加载时,真正的方法体会被解密注入。
ClassFinal使用AES算法加密class文件,密码是保证不被破解的关键,请保存好密码,请勿泄漏。为了保证项目在运行时的安全,启动jvm时请加参数: -XX:+DisableAttachMechanism 。
更多详细的配置可查看GItHub地址:https://github.com/roseboy/classfinal
至此,已完成了原生的ClassFinal代码加密集成。
四、加固优化
原生的ClassFinal是开源的,插件会在启动的时候打印出ClassFinal Logo,同时解密代码直接暴露在根目录的net/roseboy包下,即使代码混淆了也很容易被人找到源码,通过阅读源码很容易被破解。
为了防止通过反编译解密代码,从而导致业务代码被解密,我们需要对ClassFinal的解密代码进行混淆。
我们可以Fork ClassFinal的源码,对其进行改造,比如修改加解密逻辑,隐藏解密代码包的路径,混淆加解密代码、取消控制台logo打印等,从而加大逆向工程的难度。
五、加固后项目集成
下文使用加固优化后的classfinal插件进行代码加密和混淆
在子模块yhaf-admin-base-web的pom文件引入ClassFinal插件。
<build>
<plugins>
<!--springBoot项目打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<!--classfinal加密打包插件-->
<plugin>
<groupId>com.yhtech.classfinal</groupId>
<artifactId>classfinal-maven-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<configuration>
<!--加密密码,如果是#号,则使用无密码模式加密,加密打包之后pom.xml会被删除,不用担心在jar包里找到此密码-->
<password>123</password>
<!--加密的包名(可为空,多个用","分割)-->
<packages>com.yhtech.backend</packages>
<!--需要加密的配置文件,一般是classes目录下的yml或properties文件(可为空,多个用","分割)-->
<cfgfiles>application.yml</cfgfiles>
<!--排除的类名(可为空,多个用","分割)-->
<excludes>org.spring</excludes>
<!--jar/war包lib下要加密jar文件名(可为空,多个用","分割)-->
<libjars>
yhaf-admin-base-api-1.6.6-SNAPSHOT.jar,
yhaf-admin-base-service-1.6.6-SNAPSHOT.jar,
yhaf-admin-base-security-core-1.6.6-SNAPSHOT.jar,
yhaf-admin-base-security-1.6.6-SNAPSHOT.jar
</libjars>
<debug>true</debug>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>classFinal</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
其中,com.yhtech.classfinal:classfinal-maven-plugin:1.0.0-SNAPSHOT为我们优化后的插件。
使用mvn clean package命令进行打包,打包后将生成一个yhaf-admin-base-web-1.6.6-SNAPSHOT-final.jar加密混淆包。
六、使用命令行加密和混淆
1.ClassFinal使用命令行加密
对classFinal项目打包后,会获得classfinal-fatjar.jar程序包,也可以在官网下载到。
通过使用classfinal-fatjar执行下面命令可对jar、war包进行加密
java -jar classfinal-fatjar.jar -file yourpaoject.jar -libjars a.jar,b.jar -packages com.yourpackage,com.yourpackage2 -exclude com.yourpackage.Main -pwd 123456 -Y
参数说明
-file 加密的jar/war完整路径
-packages 加密的包名(可为空,多个用","分割)
-libjars jar/war包lib下要加密jar文件名(可为空,多个用","分割)
-cfgfiles 需要加密的配置文件,一般是classes目录下的yml或properties文件(可为空,多个用","分割)
-exclude 排除的类名(可为空,多个用","分割)
-classpath 外部依赖的jar目录,例如/tomcat/lib(可为空,多个用","分割)
-pwd 加密密码,如果是#号,则使用无密码模式加密
-code 机器码,在绑定的机器生成,加密后只可在此机器上运行
-Y 无需确认,不加此参数会提示确认以上信息
以上示例是直接用参数执行,也可以直接执行 java -jar classfinal-fatjar.jar按照步骤提示输入信息完成加密。
结果: 生成 yourpaoject-encrypted.jar,这个就是加密后的jar文件;加密后的文件不可直接执行,需要配置javaagent。