ProGuard代码混淆器如何使用

📌一、概述

1. ProGuard简介

背景:
ProGuard 是一个免费的 Java 类文件的压缩,优化,混肴器。它删除没有用的类,字段,方法与属性。使字节码最大程度地优化,使用简短且无意义的名字来重命名类、字段和方法 。

使用场景:
我们在工程应用中经常会遇到核心代码不希望给别人抄袭,但系统是用java开发的,无法避免被反编译的情况,这样可以用代码混淆的方式来解决。

使用原因:
版本一直在更新维护,功能也比较多。

2. 相关资料

官网地址: Java Obfuscator and Android App Optimizer | ProGuard
官方手册: ProGuard
中文手册: ProGuard 最全混淆规则说明_proguard混淆规则_程序员_jasen的博客-CSDN博客
中文语法基础: 语法基础——Proguard语法基础_-keepattributes innerclasses_Dij__柯南的博客-CSDN博客
Springboot中的使用: Spring boot使用ProGuard实现代码混淆_proguard springboot_Blueeyedboy521的博客-CSDN博客

二、配置说明

1. 配置要点

建议逐个java包定义混淆规则,这样思路更清晰

  • repository(dao)层需要保存包名和类名,因为Mybatis的xml文件中引用了dao层的接口 ;
  • controller层注意在使用@PathVariable、@RequestParam时需要显式声明参数名 ;
  • dao层用于映射数据库表的类和controller层映射前台参数的类,都需要保留类成员 ;
  • 修改spring的bean命名策略,改成按类的全限定名来命名。
  • -entity和dto中的字段要保留命名,否则无法执行拷贝动作
  • -service最好用@Service(“xxxService”)指定名字,否则会模糊成a,导致依赖问题
  • 多模块依赖的模块最好不要模糊,否则无法依赖正常

2. 使用方案

  • 使用proguard.cfg
  • 直接在pom文件里配置插件(推荐,快速方便)

3. 在pom里配置的例子

<build>
        <plugins>
            <!-- ProGuard混淆插件-->
            <plugin>
                <groupId>com.github.wvengen</groupId>
                <artifactId>proguard-maven-plugin</artifactId>
                <version>2.4.0</version>
                <executions>
                    <execution>
                        <!-- 混淆时刻,这里是打包的时候混淆-->
                        <phase>package</phase>
                        <goals>
                            <!-- 使用插件的什么功能,当然是混淆-->
                            <goal>proguard</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- 是否将生成的PG文件安装部署-->
                    <attach>true</attach>
                    <!-- 是否混淆-->
                    <obfuscate>true</obfuscate>
                    <!-- 指定生成文件分类 -->
                    <attachArtifactClassifier>pg</attachArtifactClassifier>
                    <options>
                        <!-- JDK目标版本1.8-->
                        <option>-target 1.8</option>
                        <!-- 不做收缩(删除注释、未被引用代码)-->
                        <!-- proguard会对代码进行优化压缩,他会删除从未使用的类或者类成员变量等,shrink这个功能一般最好别用,所以这里添加了<option>-dontshrink</option>,我就遇到过启动jar的时候不支持压缩jar的问题-->
                        <option>-dontshrink</option>
                        <!-- 不做优化(变更代码实现逻辑)-->
                        <option>-dontoptimize</option>
                        <!-- 不路过非公用类文件及成员-->
                        <option>-dontskipnonpubliclibraryclasses</option>
                        <option>-dontskipnonpubliclibraryclassmembers</option>
                        <!--不用大小写混合类名机制-->
                        <option>-dontusemixedcaseclassnames</option>

                        <!-- 优化时允许访问并修改有修饰符的类和类的成员 -->
                        <option>-allowaccessmodification</option>
                        <!-- 确定统一的混淆类的成员名称来增加混淆-->
                        <option>-useuniqueclassmembernames</option>
                        <!-- 不混淆所有包名-->
                        <option>-keeppackagenames</option>
                        <option>-keepdirectories</option>

                        <!-- 需要保持的属性:异常,注解等-->
                        <option>-keepattributes
                            Exceptions,InnerClasses,Signature,Deprecated,SourceFile,*Annotation*,Synthetic,EnclosingMethod
                        </option>
                        <option>-keep class javax.annotation.**</option>
                        <option>-dontwarn javax.crypto.**</option>
                        <option>-keep class javax.crypto.**</option>
                        <option>-keepnames class com.redotsoft.dmp.**</option>
                        <!-- 此选项将保存接口中的所有原始名称(不混淆)-->
                        <option>-keepnames interface ** { *; }</option>
                        <!-- 此选项将保存所有软件包中的所有原始接口文件(不进行混淆)-->
                        <option> -keep interface * extends * { *; }</option>
                        <!-- 保留参数名,因为控制器,或者Mybatis等接口的参数如果混淆会导致无法接受参数,xml文件找不到参数-->
                        <option>-keepparameternames</option>
                        <!-- 保留枚举成员及方法-->
                        <option>-keepclassmembers enum * { *; }</option>
                        <!-- 不混淆所有类,保存原始定义的注释 -->
                        <option>-keepclassmembers class * {
                        @com.baomidou.mybatisplus.annotation *;
                        @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 *;
                        }</option>
                        <!-- 不混淆当前包中的public方法、变量等等 -->
                        <option>-keep class com.redotsoft.dmp.** {
                            public *; }</option>
                    </options>

                    <outjar>${project.build.finalName}-pg.jar</outjar>
                    <libs>
                        <lib>${java.home}/lib/rt.jar</lib>
                    </libs>
                    <!-- 对什么东西进行加载,这里仅有classes成功,毕竟你也不可能对配置文件及JSP混淆吧-->
                    <injar>classes</injar>
                    <!-- 输出目录-->
                    <outputDirectory>${project.build.directory}</outputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

4. 配置项要点说明

  • -keepattributes 保持不会混淆的属性
可配置项:Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod

如果你想混淆方法内部的变量名,则需要去掉LocalVariable*Table这个属性


  • - keepnames 指定要保留名称的类和类成员,前提是在压缩阶段未被删除。仅用于模糊处理

如果你不想混淆所有类的名称(比如你要暴露给父项目调用),需要这么写

-keepnames class com.redotsoft.dmp.**


  • - keep 指定需要保留的类和类成员(作为公共类库,应该保留所有可公开访问的public方法)

如果你想混淆所有的私有内部方法、变量,又希望所有的public公共方法、变量保持不被混淆,需要这么写

-keep class com.redotsoft.dmp.** {public *; }

关于keep命令的一些使用示例
  • 保持了类ClassOneOne里面所有public 修饰的成员和方法
-keepclassmembernames class com.dev.demo.one.ClassOneOne {
public *;
}

  • 保持了ClassOne里面public 修饰的构造函数
-keep class com.dev.demo.ClassOne {
public <init>();
}

  • 保持了类ClassTwoTwo里面public修饰的参数为int的构造函数
-keep class com.dev.demo.two.ClassTwoTwo {
public <init>(int);
}

  • 保持了类ClassTwoThree 里面public修饰的方法和private修饰的成员变量
-keepclassmember class com.dev.demo.two.ClassTwoThree {
public <methods>;
private <fields>;
}

  • 保持了ClassTwoThree的子类以及里面的成员和方法
-keep class * extends com.dev.demo.two.ClassTwoThree {*;}

  • 保持了前缀为ClassOne的类以及里面的成员和方法
-keepnames class com.dev.demo.one.ClassOne{;}

  • 保持了ClassTwoTwo的内部类ClassTwoTwoInner里面的成员和方法
-keep class com.dev.demo.two.ClassTwoTwo$ClassTwoTwoInner{*;}

💭三、语法目录

Input/Output Options 输入输出选项

-include filename
递归引入目录的配置文件
-basedirectory directoryname
-injars class_path
指定应用程序要处理的jars包(或者wars、ears、zip、或者目录结构),它们里面的class文件会被处理并被写入到输出jars里面。它们里面的任何非class文件会被直接复制过去但是不会处理。(需要注意过滤调一些IDE自动生成的文件);
-outjars class_path
指定输出jars(wars、ears、zip、目录结构)的名称;由-injars 指定的被处理的jars将被写入到指定的输出jars里。如果不指定outjars将不会有class文件被写入。
-libraryjars class_path 不混淆指定的jar库(android 项目中一般不混淆引入的第三方类库)
-skipnonpubliclibraryclasses 不混淆指定jars中的非public calsses
-dontskipnonpubliclibraryclasses 不忽略指定jars中的非public calsses (默认选项)和上面的选手想对
-dontskipnonpubliclibraryclassmembers
不忽略指定类库的public类成员(变量和方法),默认情况下,ProGuard会忽略他们
-keepdirectories [ directory_filter] 指定要保持的目录结构,默认情况下会删除所有目录以减小jar的大小。
-target version
指定java版本号。 版本号可以是1.0,1.1,1.2,1.3,1.4,1.5(或仅5),1.6(或仅6)或1.7(或仅7)中的一个。 默认情况下,类文件的版本号保持不变。 例如,您可能想要将类文件升级到Java 6,通过更改其版本号并对其进行预验证。
-forceprocessing 强制处理输入(-injars)jars。即使输出jars是最新的。通过指定的输入,输出和配置文件或者目录的时间戳判断是否最新。

Keep Options 保留选项

-keep [, modifier,...] class_specification
指定需要保留的类和类成员(作为公共类库,应该保留所有可公开访问的public方法)
-keepclassmembers [, modifier,...] class_specification
指定需要保留的类成员:变量或者方法
-keepclasseswithmembers [, modifier,...] class_specification
指定保留的类和类成员,条件是所指定的类成员都存在(既在压缩阶段没有被删除的成员,效果和keep差不多)
-keepnames class_specification
[-keep allowshrinking class_specification 的简写]
指定要保留名称的类和类成员,前提是在压缩阶段未被删除。仅用于模糊处理
-keepclassmembernames class_specification
[-keepclassmembers allowshrinking class_specification 的简写]
指定要保留名称的类成员,前提是在压缩阶段未被删除。仅用于模糊处理
-keepclasseswithmembernames class_specification
[-keepclasseswithmembers allowshrinking class_specification 的简写]
指定要保留名称的类成员,前提是在压缩阶段后所指定的类成员都存在。仅用于模糊处理
-printseeds [ filename]
指定详尽列出由各种-keep选项匹配的类和类成员。 列表打印到标准输出或给定文件。 该列表可用于验证是否真的找到了预期的类成员,特别是如果您使用通配符。 例如,您可能想要列出所有应用程序或您保存的所有小程序。

Keep选项概述对比(Overview of Keep Options)

作用范围

保持所指定类、成员

所指定类、成员在压缩阶段没有被删除,才能被保持

类和类成员

-keep

-keepnames

仅类成员

-keepclassmembers

-keepclassmembernames

类和类成员(前提是成员都存在)

-keepclasseswithmembers

-keepclasseswithmembernames


通用通配符

通配符

意义

?

匹配名称当中的任意一个字符

*

匹配名称中的任意部分,但是不包括目录的分隔符、包分隔符

**

匹配名称中的任意部分,可以包含任意数量的目录分隔符、包分隔符


类描述通配符

通配符

意义

?

匹配单个字符

*

匹配类名中的任何部分,但不包含包分隔符

**

匹配类名中的任何部分,并且可以包含包分隔符

%

匹配java中的基本数据类型(int, boolean, long, float,double等)

...

匹配任意参数列表

*

匹配所有类型,包括初始类型和非初始类型,数组和非数组

< init >

匹配任何构造器

< ifield>

匹配任何字段名

< imethod>

匹配任何方法

$

指内部类

🚩四、关于打包和发布

  • 需要注意的是,使用ProGuard混淆打包,并不会替换正常的jar包,而是会多出一个代码混淆后的jar包

  • 如果要使用混淆后的jar包进行发布

1. 将原版jar包中的META-INF文件夹拖到混淆后的jar包里去

2. 将混淆后的jar包重命名为原版名称

3. 手动上传至私库(不要用maven的deploy命令上传,这样做会同时上传两个jar包并导致版本混乱)

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值