主流的加密方式有两种:
字节码混淆就是对类名、字段名、方法名进行替换,让其变得无意义,使其他人反编译后很难读懂,但并不影响逻辑。
字节码转换是指对编译后的class文件进行加密,在类加载的时候再解密。加密直接使用加密算法;解密通过类加载器,基于-agentJava:xxx.jar
,通过Premain-Class
向Instrumentation
注入ClassFileTransformer
,然后在ClassFileTransformer
中解密。
代码混淆,上手最简单,加密级别比较低,也容易破解;字节码转换相对加密安全系数较高,所以我们采用后者进行加密。
字节码转换方式有两个主流的开源框架:
可以避免源码泄露以及反编译。该项目的实现方式是对class文件完全加密,修改了jar中的META-INF/MANIFEST.MF,将原有的Main-Class
修改为Jar-Main-Class
,并增加Main-Class
,启动Main-Class
时,在原有类加载器的同级别中增加一个自定义的类加载器,通过该类加载器实现加密文件的解密,然后反射调用Jar-Main-Class
对应类的main方法去启动应用。支持SpringBoot应用。
整体不错,对SpringBoot支持也好,其逻辑就是基于-agentJava:xxx.jar
这一套原理,加密时对class文件做了两次处理,一次是对class文件的字节码完全加密,一次是对class文件混淆,这个混淆是保留成员变量和方法,只对方法的内部实现进行隐藏;解密时,判断如果该类是自己加密过的,就找到加密的字节码进行解密,如果不是自己加密的就跳过。其对class文件混淆,就是方便SpringBoot这种三方框架直接分析class文件。
首先导入依赖:
<project>
<repositories>
<repository>
<id>jitpack.ioid>
<url>https://jitpack.iourl>
repository>
repositories>
<dependencies>
<dependency>
<groupId>com.github.core-libgroupId>
<artifactId>xjarartifactId>
<version>4.0.2version>
dependency>
dependencies>
project>
通过测试类对目标jar包进行加密处理:
public class MainTest {
public static void main(String[] args) throws Exception {
XCryptos.encryption()
.from("/Users/wshuo/Developer/demo1/target/demo1-0.0.1-SNAPSHOT.jar")
.use("io.xjar")
.include("/com/example/**/*.class")
.to("/Users/wshuo/Downloads/encrypted.jar");
}
}
其中use方法里的参数即为用于加密的密码。
可以得到三个文件:
其中第二个是go编写的脚本文件,称之为go启动器,需要在有go开发环境的机器上编译:
go build ./xjar.go
编译完成之后会得到一个xjar
的可执行文件,如果是windows系统则为xjar.exe
。
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED -jar ./demo1-0.0.1-SNAPSHOT.xjar
最后执行该可执行文件即可。
执行xjar的时候发现报错:
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private final jdk.internal.loader.URLClassPath java.net.URLClassLoader.ucp accessible: module java.base does not "opens java.net" to unnamed module @34340fab
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
at io.xjar.reflection.XReflection.field(XReflection.java:20)
at io.xjar.boot.XBootClassLoader.<init>(XBootClassLoader.java:41)
at io.xjar.boot.XJarLauncher.createClassLoader(XJarLauncher.java:31)
at org.springframework.boot.loader.ExecutableArchiveLauncher.createClassLoader(ExecutableArchiveLauncher.java:109)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:55)
at io.xjar.boot.XJarLauncher.launch(XJarLauncher.java:26)
at io.xjar.boot.XJarLauncher.main(XJarLauncher.java:22)
panic: exit status 1
网上给出相关错误原因,以及解决方案,但是未生效。
https://www.coder.work/article/61641
https://stackoverflow.com/questions/41265266
尝试了以下命令,都不行:
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED -jar ./encrypted.jar
sudo ./xjar java --permit-illegal-access -jar ./encrypted.jar
sudo ./xjar java --illegal-access -jar ./encrypted.jar
报错如下:
Unrecognized option: --illegal-access
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
panic: exit status 1
执行下面的命令:
sudo ./xjar java --illegal-access=warn --add-opens java.base/jdk.internal.loader=ALL-UNNAMED -jar ./encrypted.jar
这段命令的意思是,在启动虚拟机的时候,增加一个选项表示违法访问,出现以下提示信息:
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0
说明强制非法访问,已经在JDK17中移除了。不可以再使用illegal-access选项来访问JDK的内部元素。
出现这个错误的原因是JDK9往后引入了Java Platform Module System(模块化)的概念,每个模块都是强封装的,而我们启动JAR包需要用到反射去访问目标类,这里提示没有权限;那我们只能在命令里增加参数,来特定打开某些需要打开的包才能正常启动项目,下面的命令增加了启动参数,JAR包可以正常执行。
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED -jar ./encrypted.jar
如果觉得以上加密操作很麻烦,还需要编写单元测试,XJar还提供了maven插件,可以在打包的同时对jar包进行加密。使用方法如下:
<project>
<pluginRepositories>
<pluginRepository>
<id>jitpack.ioid>
<url>https://jitpack.iourl>
pluginRepository>
pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>com.github.core-libgroupId>
<artifactId>xjar-maven-pluginartifactId>
<version>4.0.2version>
<executions>
<execution>
<goals>
<goal>buildgoal>
goals>
<phase>packagephase>
<configuration>
<password>io.xjarpassword>
configuration>
execution>
executions>
plugin>
plugins>
build>
project>
加密操作的命令就简化为了:
mvn xjar:build -Dxjar.password=io.xjar -Dxjar.targetDir=/directory/to/save/target.xjar
和打包操作一起:
mvn clean install -Dxjar.password=io.xjar -Dxjar.targetDir=/directory/to/save/encrypted
命令执行完成之后会在指定目录出现三个文件:
继续编译go文件:
go build ./xjar.go
编译完成之后得到一个xjar文件,执行下面的命令:
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED -jar ./demo1-0.0.1.xjar
项目即可正常启动。