[信息安全] 代码混淆原理与Java混淆工具

0 引言

  • 近期一项目上需要用到混淆工具。第一次使用,且感兴趣。现在这套流程和打包脚本都跑通了,故此做个总结。
  • 第1章,偏理论。基本把所有的混淆思路、原理都讲到很细了。不感兴趣、觉得晦涩的朋友,可以直接跳过本章节。
  • 第2章,偏工具、实践。主要用到 proguard

1 概述

1.1 混淆工具的产生背景:反编码、知识产权保护

  • Java程序可以在平台上迅速传播,Java语言很容易被反编码。
  • Java程序混淆也就是在不改变原义的情况下,对Java程序进行混淆,混淆后的程序很难被反编码,或者发编码后的程序可读性非常差,就可以达到保护软件的目的。
  • Java程序混淆技术被广泛地用于软件知识产权领域。

1.2 程序混淆技术的原理

  • 混淆技术实际上就是代码变换技术,通过变换,将原始程序 P 变换成新的程序 P1,它们的功能相似或者相近,代码安全性就非常高了。
  • 如果原始程序 P ,经过混淆变换 T,变换成新的程序 P1。也就是:P1 = T(P)。这个软件混淆变化必须满足以下条件:
    • 如果 P 结束,那么 P1 也必须结束,并且输出结果与 P 相同;
    • 如果 P 无法结束,或者以错误状态结束,那么 P1 也不一定结束;
    • 对于 P 和 P1 的计算任务, P1 比 P 消耗的时间长,而且保持在一个特定的范围内;
    • 攻击者如果想要将 P1 恢复成 P,那么所需要消耗的时间远远大于将P转换成 P1 的时间。

代码混淆技术的通俗意义:
混淆代码只能增加阅读和理解的难度, 并不能百分百保证代码安全。只需达到让窃取人员看到这头痛的代码有99.99999%的冲动放弃阅读,拍桌子说还不如我重写一遍逻辑的效果,混淆技术的目的就达到了。

1.3 程序混淆技术的分类

1.3.1 数据混淆

  • 数据混淆是指对程序的组成元素,如常量变量数据结构进行混淆变换,增大攻击者反编码的难度。
    • 1)分裂和合并变量混淆
      • 分裂变量混淆:指的是保持程序原义的前提下,将一些比较简单的数据结果数据类型分解成一些变量的组合,就可以达到隐藏原始程序的效果。
      • 合并变量混淆:指的是将几个数值变量或者整型数据合并成一个变量或数据,从而达到隐藏原始数据的目的。
    • 2)数组重构混淆
      • 数组是程序中的基本数据结构,对数组进行重构混淆,包括数组分裂变换、数组合并变化、数组平滑变换以及数组折叠变换。
      • 数组分裂变换:指的是将一个数组分裂成两个或两个以上的数组;
      • 数组合并变换:是指将两个或两个以上的数组合并为一个数组;
      • 数组平滑变换:是指减少数组的维数;
      • 数组折叠变换:是指增加数组的维数。
    • 3)将静态数据转换为与程序相关的数据
      • 静态数据,特别是字符串数据,包含大量信息。
      • 通过混淆变换,可以将静态数据转换为一个函数或者一段子程序,在执行程序的时候,可以通过程序调用生成相应的字符串,从而增加程序的复杂程,度。

1.3.2 词法混淆

  • 词法混淆是变换函数和变量的名称,违背Java见名知义的软件原则。
  • 词法混淆是根据Java虚拟机规范中的类文件结构的有关规定,混淆常量池中存储的字段方法以及变量等名称的「CONSTANT_Utf8_info」类型数据。
  • 词法混淆主要有三种万法:
    • 用名称相同,但是类型不同的字段进行替换;
    • 有意义的标识符,用没有任何意义的字符来替换;
    • Java 虚拟机不限制,但是 Java 语言禁止的字符或者字符串 来代替原始的字符或者字符串。
  • 词法变换具有单向性不需要额外的执行代价,所以程序的复杂度几乎不受影响,在实践中应用比较广泛。
  • ...
  • 词法混淆,还包括移除改名
    • 移除,是指将程序中的注释调试信息等格式化信息删除掉。这种方法具有单向性,一旦混淆无法恢复,操作简单,但是强度很差。
    • 改名,是指将程序中的常量名变量名等标识符改为没有意义的标识符

1.3.3 控制混淆

  • 1)循环条件插入变换:通过不透明谓词,把一个循环的终止条件变得更加复杂
  • 2)分支插入变换:对于一串语句,加入一个控制条件,增加程序的复杂程度。
    • 可以加一个不透明的谓词,构造一个不会执行的分支,或者另两个分支上的语句都和原语句相同,从而影响后面语句的执行。
  • 3)将可制约的控制流程转换为不可制约的控制流:利用不透明的谓词,加入一个假分支,将一个循环变换成多个循环,增加控制流图的翻译难度。
  • 4)并行化代码:并行程序比串行程序更加复杂,难以被分析。
    • 使用并行技术,可以构造不透明谓词,混淆程序的控制流。
    • 可以将串行程序并行化,还可以在程序中添加代码,增加程序的理解难度。
  • 5)控制顺序混淆:一般情况下,Java类文件包含了很多控制信息,对于这些控制转换信息进行掩藏。
    • 可以通过打乱表达式、方法的顺序,增加程序的理解难度。

1.3.4 类结构混淆

  • 1)类合并:类合并指的是将两个或两个以上的类合并成一个类,包括各个类所包含的变量和函数,从而破坏系统的类,隐藏系统的整体设计。
    • 根据重命名的变量和方法,如果合并的类中有相同标识符的函数和变量,那么就价格它们改为不重复的变量或函数;
    • 如果合并的类中,构造函数的标识符和参数都是相同的,那么就增加一个伪造的参数;
    • 如果合并的两个类之间存在着继承的关系,那么为了区分标识符相同的函数,可以增加一个布尔型的私有变量。
  • 2)类分裂:类分类指的是将一个类 C 拆分成 C1 和 C2 两个类,并且 C2 是 C1 的子类。
    • 另外还要保证 C 中的变量和方法,由 C1 继承或者由 C2 包含。
  • 3)类型隐藏
    • 在 Java 中,除了类之外,还有接口。接口也是一种类型。
    • 通过接口,可以声明待混淆类的变量和方法,从而实现类型的隐藏。
    • 另外如果引入的所有接口只服务于一个类,那么很容易被识别,为了使自动分析变得困难,可以使引入的接口同时为几个类服务。

1.4 Java程序混淆技术的算法

  • 1)滥用标识符混淆算法:滥用标识符混淆算法是指用同一个标识符代替一个类中出现的所有标识符。主要是通过 Java 中标识符中的重命名来实现的。
  • 2)重载无关联方法算法重载无关联方法算法扩展转换方法重载有很大的关系。
    • 这种方法是将被混淆类中的所有实体相同的标识符来重命名。在Java程序中如果某个编译时刻通过了一个象征性的参考,那么混淆操作一定会被执行,如果用这种方法对已操作程序反编译,并重新执行,就会改变层序的代码。
  • 3)非法标识符替换算法:非法标识符替换算法是将程序中的普通标识符用关键词和非法字符结合的字符串或者纯粹的关键词来代替。这种混淆算法,很容易迷惑大多数的反编译软件。
  • 4)重写静态方法算法:重写静态方法算法可以处理一些被编译过的 Java 类。它的混淆变化非常细微,很难被察觉,从而增加了攻击者的难度。

1.5 Java 程序混淆技术的应用

  • 1)保护 DRM 技术。DRM 技术的安全性和关键数据的保护有很大的关系,采用 Java 程序混淆技术,可以保护 DRM 技术的关键数据信息。
  • 2)保护算法。如果开发者需要讲一个新算法应用到软件中的时候,可以通过代码混淆技术混淆算法的一部分,从而增加算法的理解难度,达到保护算法的目的。
  • 3)代码抗篡改。当进行网络软件操作或者执行相关任务的时候,代码的安全是相当重要的。软件设计者可以采用 JAVA 程序混淆技术保护操作软件,防止外来用户或者恶意主机篡改代码。
  • 4)病毒变种。杀毒技术的重要方法就是模式匹配。对于病毒代码,如果采样混淆技术,那么就可以产生很多不同特征的病毒变种,威胁到网络安全。

1.6 常见的代码混淆工具

Java、Android

  • Proguard
    • 开源的,最流行的Java 字节码和 Android 应用程序优化器和代码混淆器。
    • 不仅可以压缩和优化Java代码,还能有效混淆代码结构,提高安全性。
    • 其支持Android平台,并可以与Eclipse、Maven和Ant等开发工具集成,方便使用。
  • Allatori Java Obfuscator
    • 一款常用的Java代码混淆工具,可以有效保护Java代码不被反编译或逆向工程。
    • 除了混淆功能外,还具有压缩和加密功能,提供了多重安全保障。
  • Virbox Protector
    • 针对Java程序提供了两种保护方式:BCE和VME。
      • BCE方式通过加密Java方法中的JVM字节码,在方法即时编译过程中解密,保证方法在内存中的安全性;
      • VME方式则将JVM字节码转换为自定义的虚拟机指令,运行时跳转至自定义的Native虚拟机中执行,安全性更高,无法被任何已知工具还原出原始Java代码
  • DashO
    • DashO是一款商业Java代码混淆器,提供先进的混淆和优化功能,支持Android、Java SE和Java EE平台。
    • 除了基本的混淆功能外,还具有反调试反动态注入等安全功能,可有效保护Java应用程序免受黑客攻击。

ios

  • Ipa Guard
    • ipaguard是一款免费的Java代码混淆和压缩工具,支持多种开发环境。
    • 无需要ios app源码,直接对ipa文件进行混淆加密。
      • 注:IPA是Apple程序应用文件 iPhone Application 的缩写。
    • 可对IOS ipa 文件的代码,代码库,资源文件等进行混淆保护。
    • 可以根据设置对函数名、变量名、类名等关键代码进行重命名和混淆处理,降低代码的可读性,增加ipa破解反编译难度。
    • 可以对图片,资源,配置等进行修改名称,修改md5。
    • 只要是ipa都可以,不限制OC,Swift,Flutter,React Native,H5类app。
    • 官网: https://ipaguard.com

1.X 小结

  • Java程序混下技术的意义。
    Java 程序混淆技术在不改变程序功能的基础上,增加攻击者对程序反编译的难度,提高软件知识产权的保护力度。
    因此,混淆技术在代码保护方面,应用非常广泛。
    今后的混淆技术不但要加大混淆力度,提高防篡改力度,还要考虑混淆算法的运行负担。

  • Java程序混下技术的局限性——并不保证绝对安全
    尽管Java代码混淆工具可以提高应用程序的安全性,但并不能保证绝对安全。
    因此,在开发Java应用程序时,除了使用混淆工具外,还需采取其他安全措施,如数据加密、权限控制等,以全面保护应用程序的安全。

2 Proguard

2.0 项目简介

  • ProGuard 是一个开源的,最流行的Java 字节码和 Android 应用程序优化器和代码混淆器。
  • 不仅可以压缩和优化Java代码,还能有效混淆代码结构,提高安全性。
  • 主要功能
    • 压缩(Shrink):检测、并移除代码中无用的类、字段、方法和特性(Attribute)。
      • 它支持删除没有用的类,字段,方法与属性。
    • 优化(Optimize):对字节码进行优化,移除无用的指令。
      • 使字节码最大程度地优化,使用简短且无意义的名字来重命名类、字段和方法 。
    • 混淆(Obfuscate):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名。
    • 预检(Preveirfy):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。

ProGuard 会对输入的 JAR 文件按照压缩优化混淆预验证的顺序依次进行处理,最后得到输出 JAR 文件;
ProGuard 使用依赖类库Library jars)来辅助对 Input jars 类之间的依赖关系进行解析,但是依赖类库本身不会被处理,也不会被包含到 Output jars 中。

  • 支持平台
    • 其支持Android平台,并可以与Eclipse、Maven和Ant等开发工具集成,方便使用。

2.1 运行原理

  • ProGuard能够通过移除无用代码使用简短无意义的名称重命名类字段方法。从而能够达到压缩优化混淆代码的目的。最终会获取一个较小的编译文件,并且这个通过ProGuard处理的编译文件更难于进行逆向工程。

  • 总流程

  • 压缩(Shrink):在压缩处理这一步中,用于检测和删除没有使用的类,字段,方法和属性。
  • 优化(Optimize):在优化处理这一步中,对字节码进行优化,并且移除无用指令。
  • 混淆(Obfuscate):在混淆处理这一步中,使用a、b、c等无意义的名称,对类,字段和方法进行重命名。
  • 预检(Preveirfy):在预检这一步中,主要是在Java平台上对处理后的代码进行预检。
      
    对于ProGuard执行流程图如下图所示:

2.2 核心配置

2.2.1 无规则

  • 为了开始学习,先看看默认情况。如果不对任何类指定keep指令,那么ProGuard就会这样做,这是正常的事情——即要压缩(例如,去除无用代码),也要混淆(例如,重命名事物)类和类成员。

2.2.2 keep 选项

对比

作用范围 保持所指定的类、成员 所指定的类、成员在压缩阶段没有被删除,才能被保持
类和成员 -keep -keepnames
仅类成员 -keepclassmembers -keepclassmembernames
类和类成员
(前提:成员都存在)
-kepclasseswithmembers -keepclasswithmembernames

-keep

-keepclassmembers

-keepnames

-keepclassmembernames

-keepclasseswithmembers

  • 这个变体没有表格,因为它与-keep作用一致。区别是它只适用于拥有类规范中所有成员的类。

-keepclasseswithmembernames

  • 同样的,这条规则与-keepnames一致。区别也是它只适用于拥有类规范中所有成员的类。

2.3 基础入门/使用方式

  • ProGuard提供了3种使用方式:
  • 使用ProGuard GUI程序
  • 使用ProGuard命令行
  • 使用ProGuard 插件(多配合Maven打包插件/流程,一体化打包) 【推荐】

Step1 安装 ProGuard 工具

方式1:ProGuard 软件

[1] /bin 目录
proguardgui.sh/bat : GUI 界面的启动脚本
	一个GUI工具,它集成了proguard.bat和retrace.bat,在可视化界面中提供了处理过程的各个步骤的配置项,比在命令行使用更加方便。

proguard.sh/bat : 脚本静默执行的入口脚本
	用于对代码进行压缩、优化、混淆、预检的可执行文件。  

retrace.bat/sh
	用于对混淆后的代码出现的异常或者错误日志进行堆栈还原的可执行文件。  

[2] /lib 目录 :lib目录下是jar文件,与可执行文件相对应有:  
proguard.jar
proguard-ant.jar
retrace.jar  
proguardgui.jar

[3] /docs 目录 : 使用文档,里面有使用手册,不熟悉时可查看其中的文档。

[4] /exmaples 目录 : 使用例子,可结合例子学习和理解proguard的使用。

本次演示是在 Windows 10 Os 下,控制台输入 proguardgui.bat。ProGuard 运行启动成功的界面如下。

方式2:Maven-ProGuard 混淆插件

  • com.github.wvengen.proguard-maven-plugin.version : 2.6.0
<!-- proguard 的第三方混淆打包插件 | https://wvengen.github.io/proguard-maven-plugin/ --><plugin>  
    <groupId>com.github.wvengen</groupId>  
    <artifactId>proguard-maven-plugin</artifactId>  
    <version>${com.github.wvengen.proguard-maven-plugin.version}</version>  
    <executions>  
        <!-- 以下配置说明执行mvn的 package 命令时候,会执行 proguard -->        <execution>  
            <phase>package</phase>  
            <goals>  
                <goal>proguard</goal>  
            </goals>  
        </execution>  
    </executions>  
    <configuration>  
        <!-- 就是输入Jar的名称,我们要知道,代码混淆其实是将一个原始的jar,生成一个混淆后的jar,那么就会有输入输出。 -->  
       <!-- <injar>poc-common-pojo-${project.version}.jar</injar>-->        <!--<injar>poc-common-utils-${project.version}.jar</injar>-->        <injar>${project.build.finalName}-my-sdk-jar.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中配置 -->  
            <!--<option>-allowaccessmodification</option>-->            <!--<option>-keep public class com.xxxx.yyyy.zzzz.sdk.BigdataMessageParseUtils.** { *; }</option>-->        </options>  
    </configuration>  
</plugin>
  • proguard.cfg 的存放路径: ${project.basedir}/proguard.cfg
/my-project
    /src
	    /main
		    /assembly
			    assembly.xml
			/java
				/com.xxxx.yyyy.zzzz...
			/resources
				log4j.properties
				...
	    /test
    pom.xml
    proguard.cfg

Step2 设置混淆策略配置文件

方式1:Pro Guard GUI 方式配置混淆策略配置文件

使用流程与思路

  1. 在 Input/Output 中,Add input 选择要混淆的 JAR 包
    注意:一定要添加待混淆的 JAR 包依赖的所有类库
  2. Add output 指定输出文件名(例如 a.jar);
  3. 压缩(Shrinking)和优化(Optimization)不是本次的重点,略过;
  4. Obfuscation 中有很多混淆规则,可以自行依据官网进行配置调整;
  5. 最后选择 Process,点击右下角的 Process! 即可。

成功操作界面会提示:Processing completed successfully!

方式2:直接编辑【混淆策略配置文件】(proguard.cfg)

  • 新建文件、并编辑hunxiao-demo-for-pojo.proguard.cfg
#-injars C:\Users\kkkkkk\Desktop\混淆试验\poc-common-pojo-1.0.0-SNAPSHOT.jar
#-outjars C:\Users\kkkkkk\Desktop\混淆试验\poc-common-pojo-1.0.0-SNAPSHOT-out.jar

#-libraryjars 'D:\Program\Java\jdk1.8.0_261\jre\lib\rt.jar'
#-libraryjars C:\Users\kkkkkk\Desktop\混淆试验\poc-common-sdk-1.0.0-SNAPSHOT-my-jar-with-dependencies\poc-common-sdk-1.0.0-SNAPSHOT\lib\lombok-1.18.22.jar

# 打印混淆过程的详细信息
-verbose

-target 1.8

################################### [1 Shrink]
# 关闭压缩 | 压缩(Shrink):用于检测和删除没有使用的类、字段、方法和属性。
-dontshrink

################################### [2 Optimize]
# 关闭优化 | 优化(Optimize):对于字节码进行优化,并且移除无用指令。
-dontoptimize

# 指定压缩级别 [可选]
#-optimizationpasses 5
# 混淆时采用的算法 [可选]
#-optimizations !code/simplification/arithmetic,!field/,!class/merging/

################################### [print]
# 列出未混淆的类和成员(打印出那些被keep住的类和成员,结果输出到指定文件里)
#-printseeds C:\Users\kkkkkk\Desktop\混淆试验\print_seeds.txt
# 列出从 apk 中删除的代码
#-printusage C:\Users\kkkkkk\Desktop\混淆试验\print_unused.txt
# 列出混淆前后的映射,生成映射文件
#-printmapping C:\Users\kkkkkk\Desktop\混淆试验\print_mapping.txt
# 列出配置
#-printconfiguration

################################### [3 Obfuscate] 混淆(Obfuscate):使用a,b,c等名称对类,字段和方法进行重命名
# 不混淆。默认启用混淆,类和类成员名会变成短小且随机的名字。可通过keep选项来保留一些。
# -dontobfuscate

# 使用模糊字典,您可以指定保留关键字的列表,或具有外来字符的标识符
# 例如: 忽略空格,标点符号,重复字和#符号后的注释。
# 注意,模糊字典几乎不改善混淆。 有些编译器可以自动替换它们,并且通过使用更简单的名称再次混淆,可以很简单地撤消该效果。
# 最有用的是指定类文件中通常已经存在的字符串(例如'Code'),从而减少类文件的大小。 仅适用于混淆处理。
# -obfuscationdictionary C:\Users\kkkkkk\Desktop\混淆试验\pro_package.txt

# 指定一个文本文件,其中所有有效词都用作混淆类名。 与-obfuscationdictionary类似。 仅适用于混淆处理。
# -classobfuscationdictionary C:\Users\kkkkkk\Desktop\混淆试验\pro_class.txt

# 指定一个文本文件,其中所有有效词都用作混淆包名称。与-obfuscationdictionary类似。 仅适用于混淆处理。
# -packageobfuscationdictionary C:\Users\kkkkkk\Desktop\混淆试验\pro_func.txt


# 混淆时不使用大小写混合,混淆后的类名为小写(大小写混淆容易导致 class 文件相互覆盖)
-dontusemixedcaseclassnames

# 指定不去忽略非公共的库类(不跳过 library 中的非public的类)
# -dontskipnonpubliclibraryclasses

# 指定不去忽略包可见的库类的成员
# -dontskipnonpubliclibraryclassmembers

# 对于类成员的命名的混淆采取唯一策略
# -useuniqueclassmembernames


# 保留属性:避免混淆泛型、抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 保留属性:Annotation、内部类 不混淆
-keepattributes *Annotation*,InnerClasses
# 对异常、注解信息予以保留
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

# 保留属性:Signature ,否则你的object中含有其他对象的字段的时候会抛出ClassCastException
-keepattributes Signature

# 屏蔽所有警告 (如:找不到引用的类等情况)
# -ignorewarnings
# 屏蔽指定告警
#-dontwarn com.google.android.material.*

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 保留: 所有接口的原始名称不进行混淆
-keepnames interface ** { *; }

# 保留: 所有包中原始接口文件不进行混淆
-keep interface * extends * { *; }

######## 注:混淆语法主要用于定义不需要混淆的代码
# -keeppackagenames
# -keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod

# 保留(不混淆):指定(包/类/接口/枚举类、属性/方法)路径
# -keep class android.support.**{*;}
# -keep interface androidx.** {;}
# -keep class com.jack.bigdata.poc.pojo.**{*;}
#-keep class com.jack.bigdata.poc.pojo.BigdataMessageDto{*;}
#-keep public enum com.jack.bigdata.poc.pojo.BigdataMessageTypeEnum{*;}


# 保护指定的类和类的成员的名称,如果所有指定的类成员出席
#-keepclasseswithmembernames class * {
#	native <methods>;
#}

# 保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在
#-keepclasseswithmembers class * {
#    public <init>(android.content.Context, android.util.AttributeSet);
#}



# [poc-common-pojo]
-keep class com.jack.bigdata.poc.pojo.BigdataMessageHeaderConfigDto{*;}
-keep enum com.jack.bigdata.poc.pojo.BigdataMessageTypeEnum{*;}
-keep class com.jack.bigdata.poc.pojo.BigdataMessageDto{*;}

-keep class com.jack.bigdata.poc.pojo.log.**{*;}
-keep class com.jack.bigdata.poc.pojo.can.**{*;}



# [poc-common-utils]
-keep class com.jack.bigdata.poc.utils.bytes.BytesUtils{*;}
-keep class com.jack.bigdata.poc.utils.net.NetFileUtils{*;}
-keep class com.jack.bigdata.poc.utils.compress.GZipUtils{*;}
-keep class com.jack.bigdata.poc.utils.StringUtils{*;}
-keep class com.jack.bigdata.poc.utils.SecurityUtils{*;}
-keepclassmembers class com.jack.bigdata.poc.utils.dbc.DBCFileUtils{
    public static com.jack.bigdata.poc.pojo.dbc.kcd.NetworkDefinition parseDBCFile(java.io.InputStream);
}

-keepnames class com.jack.bigdata.poc.utils.dbc.DBCFileUtils{*;}
-keepclassmembernames class com.jack.bigdata.poc.utils.dbc.DBCFileUtils {
    public static com.jack.bigdata.poc.pojo.dbc.kcd.NetworkDefinition parseDBCFile(java.io.InputStream);
}



# [poc-common-sdk]
-keep class com.jack.bigdata.poc.sdk.dto.ParsedBigdataMessageDto{*;}
-keep class com.jack.bigdata.poc.sdk.BigdataMessageParser{*;}
-keep class com.jack.bigdata.poc.sdk.BigdataMessageParseUtils{*;}
-keepnames class com.jack.bigdata.poc.sdk.can.CanParseService{*;}
-keepclassmembernames class com.jack.bigdata.poc.sdk.can.CanParseService{*;}

#-keep public class com.jack.bigdata.poc.sdk.BigdataMessageParseService {
#    *;
#}

################################### [4 Preverify] 预检(Preverify):主要是在Java平台上对处理后的代码进行预检
# 不进行预校验 , 可加快混淆速度。
-dontpreverify

Step3 执行混淆策略

方式1:基于 ProGuard GUI 运行

  • proguardgui.bat/sh
  • 操作路径:Pro Guard - Load Configuration - Process

方式2:基于CLI命令行工具 运行

  • proguard.bat/sh
  • 命令行(Windows/Mac):
cd D:\Program\ProGuard\proguard-7.5.0\bin

# 如果配置文件与`proguard`脚本文件在同级目录下
.\proguard.bat @hunxiao-demo-for-pojo.proguard.cfg

# 如果配置文件与`proguard`脚本文件不在同级目录下 (绝对路径)
.\proguard.bat @C:\Users\xxxx\Desktop\混淆试验\hunxiao-demo-for-pojo.proguard.cfg

2.4 进阶场景:多个存在依赖关系、且均有需要混淆诉求的子工程,如何利用Maven插件一键打包?

需求分析

  • R1:多个存在依赖关系的子工程
  • R2:这些子工程均有不同的混淆诉求
  • R3:如何使用Maven混淆插件、打包插件进行打包?

工程结构:

/common-parent
	/common-pojo
	/common-utils : 依赖 pojo 子工程
	/common-sdk : 依赖 utils 子工程
	pom.xml

maven 变量:

<maven.compiler.source>8</maven.compiler.source>  
<maven.compiler.target>8</maven.compiler.target>

<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
<maven-assembly-plugin.version>3.4.2</maven-assembly-plugin.version>
<!-- <maven-jar-plugin.version>3.2.0</maven-jar-plugin.version> -->
<!-- <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version> -->

<com.github.wvengen.proguard-maven-plugin.version>2.6.0</com.github.wvengen.proguard-maven-plugin.version>

Step1 依赖的前置基础工程(pojo/utils),利用maven-compiler-plugin插件——正常打包(瘦包)即可

  • 使用普通的打包插件即可:

(这些基础工程,只需这1个插件即可,最终打的是瘦包——即 JAR包内只有本工程自己的程序文件,不包含依赖包的程序文件)

<packaging>jar</packaging>
<build>  
    <plugins>
		<plugin>  
		    <groupId>org.apache.maven.plugins</groupId>  
		    <artifactId>maven-compiler-plugin</artifactId>  
		    <version>${maven-compiler-plugin.version}</version>  
		    <configuration>  
		        <source>${maven.compiler.source}</source>  
		        <target>${maven.compiler.target}</target>  
		    </configuration>  
		</plugin>
	</plugins>
</build>  

Step2.1 最终的子工程(sdk),利用maven-dependency-plugin插件对前置依赖子工程或第三方依赖包的特定类文件【解包】到本工程的target/classes

<build>  
    <plugins>
            <plugin>  
                <groupId>org.apache.maven.plugins</groupId>  
                <artifactId>maven-dependency-plugin</artifactId>  
                <version>${maven-dependency-plugin.version}</version>  
                <executions>  
                    <execution>  
                        <id>unpack</id>  
                        <phase>package</phase>  
                        <goals>  
                            <!--  
                                copy : 拷贝指定jar包到指定目录,与当前工程的依赖没有关系  
                                copy-dependencies : 拷贝依赖jar包到指定目录  
                                unpack : 解压指定jar包到指定目录,与当前工程的依赖没有关系  
                                unpack-dependencies : 解压依赖jar包到指定目录  
                            -->  
                            <!--<goal>unpack</goal>-->                            <goal>unpack-dependencies</goal>  
                        </goals>  
                        <!-- 打包阶段将依赖的zip解压到指定的 bin 目录下 -->  
                        <configuration>  
                            <!-- goal = unpack-dependencies 的配置项 -->  
                            <includeGroupIds>com.jack.bigdata.poc</includeGroupIds> <!-- 指定groupId -->  
                            <!--<includeArtifactIds>example-artifact</includeArtifactIds>--> <!-- 指定artifactId -->  
                            <outputDirectory>${project.build.directory}/classes</outputDirectory> <!-- 解压到的目录 -->  
  
                            <!-- goal = unpack 的配置项 -->  
<!--                            <artifactItems>  
                                <artifactItem>                                    <groupId>com.summer</groupId>                                    <artifactId>maven-assembly-demo</artifactId>                                    <version>${project.parent.version}</version>                                    <classifier>package</classifier>                                    <type>zip</type>                                    <outputDirectory>${project.basedir}/lib</outputDirectory>                                </artifactItem>                            </artifactItems>-->                        </configuration>  
                    </execution>  
                </executions>  
            </plugin>
	</plugins>
</build>    

Step2.2 最终的子工程(sdk),利用maven-assembly-plugin插件——合并前置依赖子工程或第三方依赖包的特定类到本工程内(胖包)

  • 这一步,是接着step2.1,将 classses下的程序文件真正打为一个JAR包。
<build>  
    <plugins>
		<plugin>  
			<groupId>org.apache.maven.plugins</groupId>  
			<artifactId>maven-assembly-plugin</artifactId>  
			<version>${maven-assembly-plugin.version}</version>  
			<configuration>  
				<!-- appendAssemblyId : 是否追加编译ID到JAR包名称中(默认:true) -->  
				<appendAssemblyId>true</appendAssemblyId>  
				<!-- attach: 指定是否将 Java 源代码附加到项目的构件列表中 -->  
				<!--<attach>false</attach>-->                    <!--<finalName>${build.finalName}</finalName>-->                    <finalName>${project.name}-${project.version}</finalName>  
				<tarLongFileMode>gnu</tarLongFileMode>  

<!--                    <descriptorRefs>  
					&lt;!&ndash;                            官方插件默认提供的定制打包方式: bin,jar-with-dependencies,src,project  
						bin : 类似于默认打包,会将bin目录下的文件打到包中;  
						jar-with-dependencies : 会将所有依赖都解压打包到生成物中;  
						src :只将源码目录下的文件打包;  
						project : 将整个project资源打包。  
					&ndash;&gt;                        <descriptorRef>jar-with-dependencies</descriptorRef>                    </descriptorRefs>-->                    <!-- descriptors 标签与 descriptorRefs 只能二选一,不建议同时存在 -->  
				<descriptors>  
					<descriptor>src/main/assembly/assembly.xml</descriptor>  
				</descriptors>  
				<archive>  
					<manifest>  
						<!--<addClasspath>true</addClasspath>-->  
						<!-- mainClass : 声明入口主类 [可选] -->  
						<mainClass>${application.startupClass}</mainClass>  
						<!-- <classpathPrefix>lib/</classpathPrefix> -->  
						<!--<useUniqueVersions>false</useUniqueVersions>-->                        </manifest>  
				</archive>  
			</configuration>  
			<executions>  
				<execution>  
					<id>make-assembly</id>  
					<phase>package</phase>  
					<goals>  
						<goal>single</goal>  
					</goals>  
				</execution>  
			</executions>  
		</plugin>
	</plugins>
</build> 
  • src/main/assembly/assembly.xml
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"  
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">  
    <!--  
        maveb-jar-plugin : maven默认打包插件,用来创建project jar  
        maven-shade-plugin : 用来打可执行包(executable jar)  
        maven-assembly-plugin : 支持定制化打包方式  
    -->  
    <!-- https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html -->    <!-- https://maven.apache.org/components/plugins-archives/maven-assembly-plugin-2.2.2/descriptor-refs.html [推荐] -->  
    <!-- https://github.com/apache/maven-assembly-plugin -->    <!-- https://github.com/rongfengliang/flatten-maven-plugin-maven-assembly-plugin-learning -->    <!-- https://blog.csdn.net/txhlxy/article/details/136173364 -->    <!-- maven 插件篇(assembly插件) | https://blog.csdn.net/z69183787/article/details/102519491 -->  
    <!-- 自定义 编译 ID -->    <id>my-sdk-jar</id>  
    <!--<id>win64.vm-${build.version}</id>-->  
    <formats>  
        <!-- 需要编译输出的全部格式的包,生成文件位置在 target下。-->  
        <!-- 支持 zip/tar/tar.gz/tar.bz2/jar(默认)/dir/war 等, 可以同时指定多个打包格式,且一种格式对应输出一个包 -->  
        <!-- 目标文件名的默认格式: {finalName:={artifactId}-{version}}−{assemblyId}.jar -->  
        <format>jar</format>  
    </formats>  
  
    <!-- includeBaseDirectory : 表示生成的归纳文件是否包含根目录。如果包含,则:解压时会生成和文件名相同的根目录 -->  
    <includeBaseDirectory>false</includeBaseDirectory>  
  
    <!--  
        dependencySets : 可以将所有的jar文件提取、打包出来,会在打包/归纳文件中生成到指定目录,里面包含了指定的 jar 依赖文件  
        https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html#dependencyset    -->    <dependencySets>  
        <dependencySet>  
            <!-- 对 poc-common-poc/utils/sdk 解包至 classes -->            <!--<outputDirectory>/classes</outputDirectory>-->            <useProjectArtifact>true</useProjectArtifact>  
            <unpack>true</unpack>  
            <scope>runtime</scope>  
            <includes>  
                <include>com.jack.bigdata.poc:*:jar</include>  
            </includes>  
        </dependencySet>  
    </dependencySets>  
</assembly>
  • 输出的JAR包:{project.name}-{version}-my-sdk-jar.jar

JAR包,尚未被混淆。作为中间产物,提供给step2.3使用。

Step2.3 最终的子工程(sdk),利用maven-proguard-plugin插件——执行混淆策略,输出混淆JAR包

  • xxxx-{version}-my-sdk-jar.jar作为输出,混淆后,输出xxxx-{version}.jar
<!-- proguard 的第三方混淆打包插件 | https://wvengen.github.io/proguard-maven-plugin/ --><plugin>  
    <groupId>com.github.wvengen</groupId>  
    <artifactId>proguard-maven-plugin</artifactId>  
    <version>${com.github.wvengen.proguard-maven-plugin.version}</version>  
    <executions>  
        <!-- 以下配置说明执行mvn的 package 命令时候,会执行 proguard -->        <execution>  
            <phase>package</phase>  
            <goals>  
                <goal>proguard</goal>  
            </goals>  
        </execution>  
    </executions>  
    <configuration>  
        <!-- 就是输入Jar的名称,我们要知道,代码混淆其实是将一个原始的jar,生成一个混淆后的jar,那么就会有输入输出。 -->  
       <!-- <injar>poc-common-pojo-${project.version}.jar</injar>-->        <!--<injar>poc-common-utils-${project.version}.jar</injar>-->        <injar>${project.build.finalName}-my-sdk-jar.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中配置 -->  
            <!--<option>-allowaccessmodification</option>-->            <!--<option>-keep public class com.jack.bigdata.poc.sdk.BigdataMessageParseUtils.** { *; }</option>-->        </options>  
    </configuration>  
</plugin>

Step2.4 新建用于发布的子工程(dist),利用maven-assembly-plugin插件——做JAR包、配置文件等归档性质的打包(输出zip等格式)

  • pom.xml引入sdk包,并利用maven-assembly-plugin插件做归档性质的打包。
<build>  
    <plugins>  
        <plugin>  
            <groupId>org.apache.maven.plugins</groupId>  
            <artifactId>maven-assembly-plugin</artifactId>  
            <version>${maven-assembly-plugin.version}</version>  
            <configuration>  
                <!-- appendAssemblyId : 是否追加编译ID到JAR包名称中(默认:true) -->  
                <appendAssemblyId>true</appendAssemblyId>  
                <!-- attach: 指定是否将 Java 源代码附加到项目的构件列表中 -->  
                <!--<attach>false</attach>-->                <!--<finalName>${build.finalName}</finalName>-->                <finalName>${project.name}-${project.version}</finalName>  
                <tarLongFileMode>gnu</tarLongFileMode>  
                <!-- descriptors 标签与 descriptorRefs 只能二选一,不建议同时存在 -->  
                <descriptors>  
                    <descriptor>assembly/assembly.xml</descriptor>  
                </descriptors>  
                <archive>  
                    <manifest>  
                        <!--<addClasspath>true</addClasspath>-->  
                        <!-- mainClass : 声明入口主类 [可选] -->  
                        <!--<mainClass>${application.startupClass}</mainClass>-->                        <!-- <classpathPrefix>lib/</classpathPrefix> -->                        <!--<useUniqueVersions>false</useUniqueVersions>-->                    </manifest>  
                </archive>  
            </configuration>  
            <executions>  
                <execution>  
                    <id>make-assembly</id>  
                    <phase>package</phase>  
                    <goals>  
                        <goal>single</goal>  
                    </goals>  
                </execution>  
            </executions>  
        </plugin>  
  
        <plugin>  
            <groupId>org.apache.maven.plugins</groupId>  
            <artifactId>maven-compiler-plugin</artifactId>  
            <version>${maven-compiler-plugin.version}</version>  
            <configuration>  
                <source>${maven.compiler.source}</source>  
                <target>${maven.compiler.target}</target>  
                <!--<encoding>${project.build.outputEncoding}</encoding>-->  
                <!-- <skipTests>true</skipTests> --><!-- 跳过测试 -->  
                <!--<verbose>true</verbose>-->                    <!--<showWarnings>true</showWarnings>-->                    <!--<fork>true</fork>--><!-- 要使compilerVersion标签生效,还需要将fork设为true,用于明确表示编译版本配置的可用 -->  
                <!--<executable>--><!-- path-to-javac --><!--</executable>--><!-- 使用指定的javac命令,例如:<executable>${JAVA_1_4_HOME}/bin/javac</executable> -->  
                <!--<compilerVersion>${java.version}</compilerVersion>--><!-- 指定插件将使用的编译器的版本 -->  
                <!--<meminitial>128m</meminitial>--><!-- 编译器使用的初始内存 -->  
                <!--<maxmem>512m</maxmem>--><!-- 编译器使用的最大内存 -->  
                <!--<compilerArgument>-verbose -bootclasspath ${java.home}\lib\rt.jar</compilerArgument>--><!-- 这个选项用来传递编译器自身不包含但是却支持的参数选项 -->  
            </configuration>  
        </plugin>  
    </plugins>  
</build>

  • common-dist/assembly/assembly.xml
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"  
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">  
    <!--  
        maveb-jar-plugin : maven默认打包插件,用来创建project jar  
        maven-shade-plugin : 用来打可执行包(executable jar)  
        maven-assembly-plugin : 支持定制化打包方式  
    -->  
    <!-- https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html -->    <!-- https://maven.apache.org/components/plugins-archives/maven-assembly-plugin-2.2.2/descriptor-refs.html [推荐] -->  
    <!-- https://github.com/apache/maven-assembly-plugin -->    <!-- https://github.com/rongfengliang/flatten-maven-plugin-maven-assembly-plugin-learning -->    <!-- https://blog.csdn.net/txhlxy/article/details/136173364 -->    <!-- maven 插件篇(assembly插件) | https://blog.csdn.net/z69183787/article/details/102519491 -->  
    <!-- 自定义 编译 ID -->    <id>my-dist-jar</id>  
    <!--<id>win64.vm-${build.version}</id>-->  
    <formats>  
        <!-- 需要编译输出的全部格式的包,生成文件位置在 target下。-->  
        <!-- 支持 zip/tar/tar.gz/tar.bz2/jar(默认)/dir/war 等, 可以同时指定多个打包格式,且一种格式对应输出一个包 -->  
        <!-- 目标文件名的默认格式: {finalName:={artifactId}-{version}}−{assemblyId}.jar -->  
        <format>zip</format>  
    </formats>  
  
    <!-- includeBaseDirectory : 表示生成的归纳文件是否包含根目录。如果包含,则:解压时会生成和文件名相同的根目录 -->  
    <includeBaseDirectory>false</includeBaseDirectory>  
  
    <!--  
        dependencySets : 可以将所有的jar文件提取、打包出来,会在打包/归纳文件中生成到指定目录,里面包含了指定的 jar 依赖文件  
        https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html#dependencyset    -->    <dependencySets>  
        <dependencySet>  
            <!-- useProjectArtifact : 确定当前项目构建期间生成的工件是否应包含在此依赖项集中 (默认:true) -->  
            <useProjectArtifact>true</useProjectArtifact>  
            <!-- useProjectAttachments : 确定当前项目构建期间生成的附加工件是否应包含在此依赖项集中 (默认:false) -->  
            <useProjectAttachments>false</useProjectAttachments>  
            <!-- outputDirectory : 提取的jar依赖存放的文件夹,当打包成归纳文件时,归纳文件就会生成一个lib的文件夹 (默认值: /) -->  
            <outputDirectory>lib</outputDirectory>  
            <!-- scope : 依赖包的打包范围, system(本地依赖)/provided/runtime(默认)/... -->  
            <scope>runtime</scope>  
            <!-- excludes : 打包时需排除掉的依赖 -->  
            <excludes>  
                <!--<exclude>${groupId}:${artifactId}</exclude>-->  
                <exclude>com.jack.bigdata.poc:poc-common-pojo:jar</exclude>  
                <exclude>com.jack.bigdata.poc:poc-common-utils:jar</exclude>  
                <exclude>com.jack.bigdata.poc:pom-common-dependencies:jar</exclude>  
            </excludes>  
            <!-- includes : 打包时需包含的依赖 -->  
            <includes>  
                <!--<include>${project.groupId}:${project.artifactId}</include>-->  
                <!--<include>com.jack.bigdata.poc:*:jar</include>-->                <!--<include>com.alibaba.*:*:jar</include>-->                <!--<include>com.dalong:*:jar</include>-->            </includes>  
        </dependencySet>  
    </dependencySets>  
  
    <!-- fileSets : 管理一组文件的存放位置 -->  
    <fileSets>  
        <!-- 把项目依赖的jar包,打包进zip包的lib目录 -->  
        <fileSet>  
            <!-- directory : 表示源文件目录 -->  
            <directory>${project.build.directory}/lib</directory>  
            <!-- outputDirectory : 指定文件集合的输出目录,该目录是相对于根目录 -->  
            <outputDirectory>lib</outputDirectory>  
            <!--<outputDirectory>/${project.build.finalName}/lib</outputDirectory>-->  
            <!-- includes/include* : 包含文件 -->  
            <includes>  
                <!--<include>*.dll</include>-->  
                <!--<include>${project.build.finalName}.jar</include>-->            </includes>  
            <directoryMode>0755</directoryMode>  
            <!-- fileMode : 指定文件属性,使用八进制表达,分别为(User)(Group)(Other)所属属性,默认为 0644 -->            <fileMode>0644</fileMode>  
        </fileSet>  
  
        <!-- 把项目的配置文件,打包进zip包的conf目录 -->  
        <fileSet>  
            <!-- ${project.build.directory} 构建目录, default: target -->  
            <directory>${project.build.directory}/conf</directory>  
            <outputDirectory>conf</outputDirectory>  
            <!-- project.build.finalName产出物名称, default: {project.artifactId}-${project.version} -->  
            <!--<outputDirectory>/${project.build.finalName}/conf</outputDirectory>-->            <directoryMode>0755</directoryMode>  
            <fileMode>0644</fileMode>  
        </fileSet>  
  
        <!-- 把项目的脚本文件,打包进zip包的 bin 目录 -->  
        <fileSet>  
            <!--<directory>src/main/assembly/bin</directory>-->  
            <directory>bin</directory>  
            <outputDirectory>/bin</outputDirectory>  
            <!--<outputDirectory>/${project.build.finalName}/bin</outputDirectory>-->  
            <directoryMode>0755</directoryMode>  
            <fileMode>0755</fileMode>  
            <lineEnding>unix</lineEnding>  
            <!--            <includes>-->  
            <!--                <include>**/*.sh</include>-->            <!--                <include>*.cfg</include>-->            <!--                <include>*.cfg</include>-->            <!--            </includes>-->            <excludes>  
                <exclude>**.bat</exclude>  
                <exclude>**.xml</exclude>  
            </excludes>  
        </fileSet>  
        <fileSet>  
            <!--<directory>src/main/assembly/bin</directory>-->  
            <directory>bin</directory>  
            <outputDirectory>/bin</outputDirectory>  
            <!--<outputDirectory>/${project.build.finalName}/bin</outputDirectory>-->  
            <directoryMode>0755</directoryMode>  
            <fileMode>0755</fileMode>  
            <lineEnding>dos</lineEnding>  
            <includes>  
                <include>*.bat</include>  
                <include>**.xml</include>  
            </includes>  
        </fileSet>  
  
        <!-- 创建 logs 空目录 -->  
<!--        <fileSet>  
            <directory>${project.basedir}/target</directory>            <outputDirectory>logs</outputDirectory>            &lt;!&ndash;<outputDirectory>/${project.build.finalName}/logs</outputDirectory>&ndash;&gt;            <excludes>                <exclude>**/*</exclude>            </excludes>            <directoryMode>0755</directoryMode>            <fileMode>0644</fileMode>        </fileSet>-->  
        <!-- 创建 tmp 空目录 -->  
<!--        <fileSet>  
            <directory>${project.basedir}/target</directory>            <outputDirectory>tmp</outputDirectory>            &lt;!&ndash;<outputDirectory>/${project.build.finalName}/tmp</outputDirectory>&ndash;&gt;            &lt;!&ndash;<outputDirectory>${project.build.directory}/tmp</outputDirectory>&ndash;&gt;            <excludes>                &lt;!&ndash; 创建空目录,需使用<排除> **/* </排除>,否则子目录中的文件可能会被复制到文件夹中 &ndash;&gt;                <exclude>**/*</exclude>            </excludes>            <directoryMode>0755</directoryMode>            <fileMode>0644</fileMode>        </fileSet>-->  
        <!-- ${project.basedir} 项目根目录 -->  
        <!-- 把项目相关的说明文件,打包进zip文件的doc目录 -->  
        <!--        <fileSet>                    <directory>${project.basedir}</directory>                    <outputDirectory>/${project.build.finalName}/doc</outputDirectory>                    <includes>                        <include>README*</include>                        <include>LICENSE*</include>                        <include>NOTICE*</include>                    </includes>                </fileSet>-->        <fileSet>  
            <directory>${project.basedir}/../doc</directory>  
            <outputDirectory>doc</outputDirectory>  
            <!--<outputDirectory>/${project.build.finalName}/doc</outputDirectory>-->  
            <includes>  
                <include>**/*</include>  
                <!--<include>README*</include>-->  
                <!--<include>LICENSE*</include>-->                <!--<include>NOTICE*</include>-->            </includes>  
        </fileSet>  
  
        <!-- init 下的数据库脚本 打包进zip文件的 scripts 目录 -->  
        <fileSet>  
            <directory>${project.basedir}/../init</directory>  
            <outputDirectory>init</outputDirectory>  
            <!--<outputDirectory>/${project.build.finalName}/init</outputDirectory>-->  
            <includes>  
                <include>**/*</include>  
            </includes>  
        </fileSet>  
    </fileSets>  
</assembly>
  • 最终输出:xxxx-{version}-my-dist-jar.zip
目录结构:
xxxx-{version}-my-dist-jar.zip
	/bin
	/doc
	/init
	/lib

混淆效果

  • 混淆效果

2.X 推荐文献

Y FAQ

Q:Maven打包插件的区别?maveb-compiler/jar/shade/assembly-plugin

  • maveb-compiler-plugin : 普通的打包方式(默认,瘦包)
  • maveb-jar-plugin : maven默认打包插件,用来创建project jar (默认,瘦包)
  • maven-shade-plugin : 用来打可执行包(executable jar,支持打胖包)
  • maven-assembly-plugin : 支持定制化打包方式(支持打胖包)

X 参考文献

IOS : ipa代码混淆保护工具(Ipa Guard、ipaguard.exe)

posted @ 2024-07-25 13:53  千千寰宇  阅读(1980)  评论(0)    收藏  举报