• 极客文库-知识库上线!
  • 极客文库小编@勤劳的小蚂蚁,为您推荐每日资讯,欢迎关注!
  • 每日更新优质编程文章!
  • 更多功能模块开发中。。。

玩转代码混淆工具:ProGuard

维基百科对 ProGuard 的介绍是:ProGuard 是一个压缩(shrink),优化(optimize)与混淆(Obfuscate)Java 代码的开源命令行工具。也就是说混淆只是 ProGuard 的其中一个功能,本文也只介绍它的混淆功能。
About ProGuard
ProGuard 能通过重命名类名,字段名,方法名为一些没有意义的名字来混淆 Java 和 Android 程序,从而使逆向工程更难。并且 ProGuard 已经是 Android SDK 的一部分了,当然本文只讲如何混淆 Java 代码。

用法

本文介绍两种方式:使用 UI工具 和 Maven 集成。
这个比较简单,先从 https://sourceforge.net/projects/proguard/下载最新版的 ProGuard,然后解压,到 bin 目录下找到proguardgui.bat双击即可,程序运行后的界面如下:
ProGuard
Input/Output中,Add input选择要混淆的 JAR 包(ProGuard 不支持混淆单个 class 文件,但是我们可以通过命令把一个或多个 class 打成 jar 包:jar cvf Test.jar com/afei/test/*.class),Add output指定输出文件名。Obfuscation中有很多混淆自定义规则,根据需求自行调整,ShrinkingOptimization视情况而定是否需要勾选(如果只是混淆代码,则建议关闭)。最后选择Process,点击右下角的Process!即可。如下图所示,User.java 混淆前后对比,我们发现类名,属性名都被重命名为一些毫无意义的命名了:
image.png
  • Maven 集成
通过 maven 集成方式从而在 maven 打包阶段就达到了混淆的目。集成方式非常简单,只需修改 POM,添加如下一些代码即可:
— 添加依赖
<dependency>
    <groupId>net.sf.proguard</groupId>
    <artifactId>proguard-base</artifactId>
    <version>5.3.3</version>
</dependency>

— 添加插件
<plugin>
    <groupId>com.github.wvengen</groupId>
    <artifactId>proguard-maven-plugin</artifactId>
    <version>2.0.15-SNAPSHOT</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>proguard</goal></goals>
        </execution>
    </executions>
    <configuration>
        <proguardVersion>5.3.3</proguardVersion>
        <injar>${project.build.finalName}.jar</injar>
        <outjar>${project.build.finalName}.jar</outjar>
        <obfuscate>true</obfuscate>
        <libs>
            <lib>${java.home}/lib/rt.jar</lib>
            <lib>${java.home}/lib/jce.jar</lib>
        </libs>
        <options>
            <!–不需要压缩–>
            <option>-dontshrink</option>
            <!–不需要优化–>
            <option>-dontoptimize</option>
            <option>-keepnames interface **</option>          
            <option>-keepparameternames</option>
            <option>-keeppackagenames</option>                    
        </options>
    </configuration>
</plugin>
集成后执行maven package打包,就会看到 target 目录下会多出一些命名中带有 proguard 的文件,例如afei-demo-1.0.0-SNAPSHOT_proguard_base.jarproguard_map.txtproguard_seed.txt等,并且每个目录下的类名都被重命名为 a, b, c 等毫无意义的类名:
confusion target

  • 配置说明
maven 集成 ProGuard 后,可自定义的地方主要集中在 options 标签中,一些 option 说明如下:
-dontshrink
shrink 是 ProGuard 几大核心功能之一,配置了 dontshrink 表示不需要压缩,即关闭 shrink 功能

-dontoptimize
optimize 也是 ProGuard 几大核心功能之一,配置了 dontoptimize 表示不需要优化,即关闭 soptimize 功能

-keeppackagenames
是否不混淆包名,如果配置了该项,那么包名会完整保留。如果没有配置,那么除了根目录(com.afei.test),子目录(例如 bean, controller, impl 等)都会被混淆。

-keepattributes SourceFile,LineNumberTable
抛出异常时保留代码行号,在异常分析中可以方便定位

-dontusemixedcaseclassnames
这个是给 Windows 系统用户的,因为 ProGuard 假定使用的操作系统是能区分大小写名,但是 Windows 不是这样的操作系统,所以必须为 ProGuard 指定-dontusemixedcaseclassnames 选项

-keep class com.afei.test.Application
Application 这个类名不混淆,但是方法名,属性名,以及方法中的内容还是会混淆;

-keep class com.afei.test.Application { *; }
Application 这个类的类名,方法名,属性名完全保留不做任何混淆,但是方法体内的内容还是会混淆。

-keepnames interface **
所有接口类命名不做任何混淆,但是接口里的属性和方法会还是会被混淆;

-keepnames enum **
枚举类名不做任何混淆,但是枚举的值会混淆;

-keep public class * implements java.io.Serializable{
    public *;
    protected *;
    private *;
}
保留实现了 Serializable 接口类中的公有的,友好的,私有的成员(属性和方法),这个配置主要是对应实体类的配置。

-keep class com.afei.test.dao.** { *; }
dao 层的类名和方法都不混淆;

-keepnames
用法如下,保留 Application 的类名和 main 方法不混淆:
<option>-keepnames public class com.afei.test.Application {
        public static void main(java.lang.String[]);
    }
</option>

用法如下,保留 FtpConfigSaveJob 的类名和 execute 方法不混淆:
<option>-keepclasseswithmembers public class com.afei.test.job.FtpConfigSaveJob {
        com.xxl.job.core.biz.model.ReturnT execute(java.lang.String);
    }

</option>

配置中符号的含义:
!:感叹号表示除这个以外,例如-keep class !com.afei.test.Application表示出了Application.java,其他的类名都不混淆

遇到的问题

通过使用 ProGuard 混淆 springboot 项目的过程,笔者遇到了下面这些问题:
  1. 工程目录要清晰,比如 VO, DTO, PO 存放目录要统一,自定义 Exception 子类存放目录等。因为这些类名,甚至属性名都不能混淆,VO 属性名与接口定义有关,PO 与 Mapper.xml 有关;
  2. 与序列化反序列相关的对象也不会混淆,与第三方约定的接口相关的模型也不能混淆等;
  3. xxl-job 相关类不能混淆,方法申明必须是 execute(String),笔者相信还会有其他的框架也会有类似的约定和限制;
  4. springboot 的 Application.java 也不能混淆,因为 main 方法不能被重命名,Application.java 类名最好也不要被重命名,因为 maven 中有配;置com.afei.test.Application
  5. 很多注解不能混淆
  6. … …
如下图所示,是默认配置混淆后的 Application.java,很明显,这样的 springboot 工程是无法启动的,因为 main 主方法被混淆为 a 了:
混淆后的 Application.java
另外,由于 ProGuard 默认会将每个子目录下的类都重命名为 a, b, c。例如 com.afei.test.a 下的类会被重命名为 a.class,b.class,c.class,而 com.afei.test.b 下的类也会被重命名为 a.class,b.class,c.class。这样就会导致 spring 初始化加载 bean 时抛出下面的异常:
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name ‘b’ for bean class [com.afei.test.config.bconflicts with existingnon-compatible bean definition of same name and class [com.afei.test.common.b]
        at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:345)
        at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:283)
        at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:135)
        at 

ProGuard 总结

通过上面的分析可知,ProGuard 或者类似原理的代码混淆工具的限制太多太多,对工程目录有严格的要求,而且很多地方需要明确配置不允许混淆,以后有新增的代码,还需要评估是否需要申明不需要混淆。这样分析下来后发现,如果想要使用 ProGuard 混淆服务端的代码,最后几乎只能混淆方法体内的内容。

XJar

针对 ProGuard 或者同原理方案的缺陷,笔者找到了另一种方案:XJar。
官方介绍 XJar 是 Spring Boot JAR 安全加密运行工具,同时支持的原生 JAR。基于对 JAR 包内资源的加密以及拓展 ClassLoader 来构建的一套程序加密启动,动态解密运行的方案,避免源码泄露或反编译。功能特性如下:
  • 无需侵入代码,只需要把编译好的 JAR 包通过工具加密即可。
  • 完全内存解密,杜绝源码以及字节码泄露或反编译。
  • 支持所有 JDK 内置加解密算法。
  • 可选择需要加解密的字节码或其他资源文件,避免计算资源浪费。
详细的介绍和用法请参考 XJar GitHub 主页: https://github.com/core-lib/xjar,介绍的非常详细。

丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:玩转代码混淆工具:ProGuard
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

欢迎 注册账号 登录 发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ

247507792

工作时间:09:00-23:00

新浪微博:点我访问

个人博客:点我访问