Java 代码混淆

Java 代码混淆

0、序言

近日,产品单位反馈,Java编译得到的字节码文件,容易被反编译得到源代码,被恶意查看、复制、分析、盗用等。

对代码进行混淆,可以在项目交付后,有效预防三方对字节码文件进行反编译、进行恶意操作,保护知识产权,提高代码安全。

代码混淆工具有很多,使用中的难点为:尽可能多的将代码混淆,并正常运行。

我们常采用springboot模块化开发,在此基础上,经过调查本文选择proguard进行代码混淆,后文含注意事项,使用流程,最后附混淆案例,供各位参考。

1、ProGuard 介绍

ProGuard是一个开源的Java类文件收缩、优化、混淆、预检器。ProGuard处理后的程序或库会更小、更快,并在一定程度上提高对反编译的抵抗能力。

ProGuard 官网

ProGuard 使用文档

ProGuard Maven插件github网站

ProGuard Maven插件文档

2、注意事项及建议

1、 被框架反射调用或者代理的类,是不能混淆的,常见的包括spring.factories相关类、spi相关类、bex相关属性、mybatis相关类、@Configuration@ConfigurationProperties@Value相关;

2、特殊的类不能混淆,包括异常、内部类、注解等;

3、pojo不能混淆,否则接口请求序列化会报错;

4、建议在@Controller、@Service@Component、@Bean等处,设置bean名称,@PathVariable、@Param等设置参数名;

5、更多配置可在ProGuard 官方配置说明文档查询,部分配置说明:

  • -dontshrink,关闭删除没有使用的类/成员,否则会把Controller啥的给删掉
  • -dontoptimize,关闭字节码级别的优化,否则aop会报错
  • -adaptclassstrings,反射相关,混淆类名之后,对使用Class.forName(‘className’)之类的地方进行相应替代
  • -keepdirectories,保持目录结构,否则@ComponentScan会无法正常工作
  • keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod,不混淆特殊类
  • -keepclassmembers [,modifier,...] class specification指定要保留的类成员
  • -keep [,modifier,...] class specification指定类和类成员(字段,方法)作为入口点被保留。

6、常见JAVA library

JAVA LIB 说明
rt.jar 运行时包
jsse.jar Java 安全套接字扩展类库,用于实现加密的 Socket 连接
jce.jar Java 加密扩展类库,含有很多非对称加密算法在里面,但也是可扩展的
charsets.jar Java 字符集,这个类库中包含 Java 所有支持字符集的字符
resources.jar 资源包(图片、properties文件)
dnsns.jar 与 DNS 有关
localedata.jar 本地机器语言的数据,比如日期在使用中文时,显示的是“星期四”之类的
sunjce_provider.jar 为JCE 提供的加密安全套件
sunmscapi.jar 数字签名

3、使用方式1,外置proguard配置

1、在要混淆的模块pom.xml中,添加插件

插件:

<build>
    <plugins>
        <plugin>
            <groupId>com.github.wvengen</groupId>
            <artifactId>proguard-maven-plugin</artifactId>
            <version>2.5.3</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>proguard</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <!-- 是否混淆 -->
                <obfuscate>true</obfuscate>
                <!-- 混淆配置文件 -->
                <proguardInclude>${project.basedir}/proguard.cfg</proguardInclude>
                <!-- 依赖的JAVA library -->
                <libs>
                    <lib>${java.home}/lib/rt.jar</lib>
                </libs>
            </configuration>
        </plugin>
    </plugins>
</build>

2、pom.xml同级目录添加proguard.cfg文件

文件内容:

#默认是开启的,这里关闭shrink,即不删除没有使用的类/成员,否则会把Controller啥的给删掉
-dontshrink
#默认是开启的,这里关闭字节码级别的优化,否则aop会报错
-dontoptimize
#混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代
-adaptclassstrings
#不混淆所有特殊的类,否则抛出去的异常会出问题
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
#保持目录结构,解决@ComponentScan失效
-keepdirectories
#不混淆service中函数名
-keepclassmembers  public class com.szkingdom.koca.config.service.** { public *; }
#不混淆Mybatis相关
-keep class com.szkingdom.koca.config.dao.** { *; }
#不混淆@Configuration,@ConfigurationProperties,@Value相关
-keepclassmembers class * {
    @org.springframework.beans.factory.annotation.Value *;
}
-keep class com.szkingdom.koca.config.config.** { *; }
-keep class com.szkingdom.koca.config.server.*Configuration { *; }
#不混淆pojo
-keep class com.szkingdom.koca.config.vo.** { *; }
-keep class com.szkingdom.koca.config.model.** { *; }
-keep class com.szkingdom.koca.config.server.paramer.** { *; }
-keep class * implements java.io.Serializable

3、maven打包,得到混淆后的jar包,使用jd-gui查看混淆结果

如图:

4、使用方式2,pom.xml内置proguard配置

1、在要混淆的模块pom.xml中,添加插件与proguard配置

<build>
    <plugins>
        <plugin>
            <groupId>com.github.wvengen</groupId>
            <artifactId>proguard-maven-plugin</artifactId>
            <version>2.5.3</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>proguard</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <obfuscate>true</obfuscate>
                <libs>
                    <!-- Include main JAVA library required.-->
                    <lib>${java.home}/lib/rt.jar</lib>
                </libs>
                <options>
                    <option>-dontshrink</option>
                    <option>-dontoptimize</option>
                    <option>-adaptclassstrings</option>
                    <option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>
                    <option>-keepdirectories</option>
                    <option>-keepclassmembers  public class com.szkingdom.koca.config.service.** { public *; }</option>
                    <option>-keep class com.szkingdom.koca.config.dao.** { *; }</option>
                    <option>-keepclassmembers class * { @org.springframework.beans.factory.annotation.Value *; }</option>
                    <option>-keep class com.szkingdom.koca.config.config.** { *; }</option>
                    <option>-keep class com.szkingdom.koca.config.server.*Configuration { *; }</option>
                    <option>-keep class com.szkingdom.koca.config.vo.** { *; }</option>
                    <option>-keep class com.szkingdom.koca.config.model.** { *; }</option>
                    <option>-keep class com.szkingdom.koca.config.server.paramer.** { *; }</option>
                    <option>-keep class * implements java.io.Serializable</option>
                </options>
            </configuration>
        </plugin>
    </plugins>
</build>

2、maven打包,得到混淆后的jar包,使用jd-gui查看混淆结果

如图:

5、打包生成文件说明

案例中,使用插件后打包后,会生成下述文件:

文件 说明
koca-cloud-config-server-3.1.0-SNAPSHOT.jar 混淆后的jar
koca-cloud-config-server-3.1.0-SNAPSHOT_proguard_base.jar 未混淆的jar
proguard_map.txt 混淆前后映射说明文件
proguard_seed.txt 未混淆的内容汇总

6、混淆案例

代码混淆.zip (337.1 KB)

  • koca-cloud-config-server:被混淆的模块
  • bootapp:bootapp项目,聚合被混淆的模块

7、其他

Java代码混淆工具有很多,本文仅对proguard的使用,及出现的问题进行了研究,在开发过程中,应根据实际情况选用合适的方案。