50-代码混淆

1. 编译

  • .java文件就被编译成.class文件
Image

2. 反编译

  • 针对编译生成的jar/war包里面的.class文件逆向还原回来
  • 比较常用的反编译工具JD-GUI,直接把编译好的jar丢进去,大部分都能反编译看到源码
Image

3. 混淆

  • 反编译看到的不是真正的代码
Image

4. 正文

Image

1. proguard.cfg

  • 第一步,在项目路径下,新增一份文件proguard.cfg
# 指定Java的版本
-target 1.8
# proguard会对代码进行优化压缩,他会删除从未使用的类或者类成员变量等
-dontshrink
# 是否关闭字节码级别的优化,如果不开启则设置如下配置
-dontoptimize
# 混淆时不生成大小写混合的类名,默认是可以大小写混合
-dontusemixedcaseclassnames
# 对于类成员的命名的混淆采取唯一策略
-useuniqueclassmembernames
# 混淆时不生成大小写混合的类名,默认是可以大小写混合
-dontusemixedcaseclassnames
# 混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代
-adaptclassstrings

# 对异常、注解信息予以保留
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# 此选项将保存接口中的所有原始名称(不混淆)-->
-keepnames interface ** { *; }
# 此选项将保存所有软件包中的所有原始接口文件(不进行混淆)
#-keep interface * extends * { *; }
# 保留参数名,因为控制器,或者Mybatis等接口的参数如果混淆会导致无法接受参数,xml文件找不到参数
-keepparameternames
# 保留枚举成员及方法
-keepclassmembers enum * { *; }
# 不混淆所有类,保存原始定义的注释-
-keepclassmembers class * {
                        @org.springframework.context.annotation.Bean *;
                        @org.springframework.beans.factory.annotation.Autowired *;
                        @org.springframework.beans.factory.annotation.Value *;
                        @org.springframework.stereotype.Service *;
                        @org.springframework.stereotype.Component *;
                        }

# 忽略warn消息
-ignorewarnings
# 忽略note消息
-dontnote
# 打印配置信息
-printconfiguration
-keep public class com.listao.mybatis_plus.MPApplication {
        public static void main(java.lang.String[]);
    }
  • 注意点:
Image
  • 其余的看注释,可以配置哪些类不参与混淆,哪些枚举保留,哪些方法名不混淆等等

2. pom.xml

  • pom文件上加入proguard混淆插件
  • <build>标签里面改动加入一下配置:
<build>
    <plugins>
        <plugin>
            <groupId>com.github.wvengen</groupId>
            <artifactId>proguard-maven-plugin</artifactId>
            <version>2.6.0</version>
            <executions>
                <!-- 以下配置说明执行mvn的package命令时候,会执行proguard -->
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>proguard</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <!-- 就是输入Jar的名称,我们要知道,代码混淆其实是将一个原始的jar,生成一个混淆后的jar,那么就会有输入输出 -->
                <injar>${project.build.finalName}.jar</injar>
                <!-- 输出jar名称,输入输出jar同名的时候就是覆盖,也是比较常用的配置 -->
                <outjar>${project.build.finalName}.jar</outjar>
                <!-- 是否混淆 默认是true -->
                <obfuscate>true</obfuscate>
                <!-- 配置一个文件,通常叫做proguard.cfg,该文件主要是配置options选项,也就是说使用proguard.cfg那么options下的所有内容都可以移到proguard.cfg中 -->
                <proguardInclude>${project.basedir}/proguard.cfg</proguardInclude>
                <!-- 额外的jar包,通常是项目编译所需要的jar -->
                <libs>
                    <lib>${java.home}/lib/rt.jar</lib>
                    <lib>${java.home}/lib/jce.jar</lib>
                    <lib>${java.home}/lib/jsse.jar</lib>
                </libs>
                <!-- 对输入jar进行过滤比如,如下配置就是对META-INFO文件不处理 -->
                <inLibsFilter>!META-INF/**,!META-INF/versions/9/**.class</inLibsFilter>
                <!-- 输出路径配置,但是要注意这个路径必须要包括injar标签填写的jar -->
                <outputDirectory>${project.basedir}/target</outputDirectory>
                <!-- 特别重要,此处主要是配置混淆的一些细节选项,比如哪些类不需要混淆,哪些需要混淆 -->
                <options>
                    <!-- 可以在此处写option标签配置,不过我上面使用了proguardInclude,故而我更喜欢在proguard.cfg中配置 -->
                </options>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.3.12.RELEASE</version>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                    <configuration>
                        <mainClass>com.listao.mybatis_plus.MPApplication</mainClass>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
  • 注意点:
Image
Image
  • 可以看到:
Image
  • 然后点击package,正常执行编译打包流程:
Image
  • 然后可以看到jar的生成:
Image
  • 效果:
Image

5. try

1. 网址

1. 对jar包进行加密

2. 直接项目打加密jar包

2. pom.xml

<project>
    <properties>
        <proguard.version>6.2.2</proguard.version>
        <proguard.maven.plugin.version>2.3.1</proguard.maven.plugin.version>
    </properties>

    <build>
        <finalName>${project.artifactId}-${revision}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.github.wvengen</groupId>
                <artifactId>proguard-maven-plugin</artifactId>
                <version>${proguard.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>proguard</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <proguardVersion>${proguard.version}</proguardVersion>
                    <!--<proguardVersion>6.2.2</proguardVersion>-->
                    <!-- 是否混淆 -->
                    <obfuscate>true</obfuscate>
                    <!--关键:引入配置文件,这里换成你的配置文件所在路径,一般情况下就是把proguard.cfg放到这个目录-->
                    <proguardInclude>${project.basedir}/src/main/resources/proguard.cfg</proguardInclude>
                    <!-- 混淆时需要引用的java库,这些库的类不会做混淆 -->
                    <libs>
                        <lib>${java.home}/lib/rt.jar</lib>
                        <lib>${java.home}/lib/jce.jar</lib>
                    </libs>

                    <!--当<injar></injar>的值为:classes时,可以结合该标签使用,作用:相关混淆配置只为指定目录下的class文件起效-->
                    <!--<inFilter>com/marydon/**</inFilter>-->
                    <!-- 需要做混淆的jar或class目录,也就是:选择对什么东西进行加载-->
                    <injar>classes</injar>
                    <!--<injar>${project.build.finalName}.jar</injar>-->

                    <!--class 混淆后输出的jar包,说明:这个输出格式可有可无,下面会讲-->
                    <outjar>${project.build.finalName}.jar</outjar>
                    <!-- 输出目录 -->
                    <outputDirectory>${project.build.directory}</outputDirectory>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>net.sf.proguard</groupId>
                        <artifactId>proguard-base</artifactId>
                        <version>${proguard.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <!-- Maven assembly must be run after proguard obfuscation so it take already obfuscated files.-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                        <configuration>
                            <mainClass>com.listao.OoxxApplication</mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

3. proguard.cfg

src/main/resources目录下

# Proguard配置项
# JDK目标版本1.8
-target 1.8
# 默认是开启的,这里关闭shrink,即不删除没有使用的类/成员(删除注释、未被引用代码)
-dontshrink
# 默认是开启的,这里关闭字节码级别的优化(变更代码实现逻辑)
-dontoptimize
# 忽略打包时的警告信息
-ignorewarnings
# 不跳过非公用类文件及成员
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontusemixedcaseclassnames
# 优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
# 混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代
-adaptclassstrings
# 不混淆所有包名,Spring配置中有大量固定写法的包名
-keeppackagenames
# 确定统一的混淆类的成员名称来增加混淆:一个类中的成员不使用重复的命名,如Student类混淆后不能出现a属性和a方法。
-useuniqueclassmembernames
# 不混淆所有特殊的类:对异常、注解信息在runtime予以保留,不然影响springboot启动
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
# 保留接口类名(两种方式:二选一)
-keepnames interface **
#-keep interface * extends * { *; }

# This option will save all original methods parameters in files defined in -keep sections, otherwise all parameter names will be obfuscate.
#-keepparameternames
#-keepdirectories
-keepclassmembers class * {
	@org.springframework.beans.factory.annotation.Autowired *;
	@org.springframework.beans.factory.annotation.Service *;
	@org.springframework.beans.factory.annotation.Value *;
	@org.springframework.web.bind.annotation.PostMapping *;
    @org.springframework.web.bind.annotation.DeleteMapping *;
    @org.springframework.web.bind.annotation.RestController *;
    @javax.annotation.Resource *;
	}

-keep class * implements java.io.Serializable { *; }
-keep class * implements org.springframework.boot.CommandLineRunner { *; }
# 保留枚举类的两种方式(二选一)
#-keepclassmembers enum  * {
#    public static **[] values();
#    public static ** valueOf(java.lang.String);
#}
-keepclassmembers enum * { *; }
# 不混淆启动类的main方法
-keep
	class com.listao.OoxxApplication {
    	public static void main(java.lang.String[]);
    }
# 保留程序入口(springboot项目的话,需要保留启动类)
#-keep @org.springframework.boot.autoconfigure.SpringBootApplication class * {*;}
-keep class com.listao.OoxxApplication { *; }
# dao 保留全部类及类成员,方法命名也不能变,因为与xml文件做了关联
-keep class com.listao.dao.** { *; }
# 不混淆所有的set/get方法,毕竟项目中使用的部分第三方框架(例如Shiro)会用到大量的set/get映射
-keepclassmembers public class * {void set*(***);*** get*();}
# 不对包类的类名进行混淆,但对类中的属性和方法混淆
# 不混淆包下的所有类名,且类中的方法和属性也不混淆

# 混淆指定包下的类   此处需要更改
-keep class !com.listao.** { *; }
#-keep class !com.marydon.controller.**,!com.marydon.*.service.** {
#    <fields>;
#    <methods>;
#}
# 无视跳过警告  因为修改代码的过程程序检测到有些文件不能更改就会报警告导致失败   如果对自己写的代码比较有信心的话可以直接无视
# 不显示警告信息,如果显示则会出现Error无法完成混淆!
-dontwarn **