假设有如下多模块项目:
module-parent
├─module-a
├─module-b
└─module-c
在 混淆技术研究笔记(一)常见工具介绍 中提到,默认只能使用单模块混淆,每个模块构建时的上下文只有自己,无法对其他模块进行处理,虽然 <<inoutpair/>
能配置多个,比如 a 依赖 b,b 依赖 c 的情况,如果配置到 a 中,在 a 中对a,b,c进行混淆,确实能实现对 jar 包内容的混淆,但是当执行 mvn clean deploy
命令时,这种混淆只对 a 自己有效。
主要的原因和 Maven 的生命周期有关,在 module-parent
这一级执行 mvn clean deploy
发布时,Maven会根据模块依赖顺序计算构建的顺序,第一个构建的模块会走完全部的生命周期后,再对第二个模块进行相同的处理,依次执行完全部的模块。
在 a 中配置多个时,当 a 模块执行时,其他模块构建完就已经发布了,对发布后的 b,c 再进行混淆已经没有意义。
该如何实现呢?
Maven在package打包时有个特性,如果源码没有变化就不会再次进行编译,已经构建的 jar 不会被覆盖,Maven只检测源码的变化,不会检测 jar 包的变化,从这点入手就能实现多模块混淆。
上面这种方式执行时,需要避免混淆插件执行两次,因此最好的办法就是在多模块基础上增加一个新的模块,例如 module-yguard
,变成如下结构:
module-parent
├─module-a
├─module-b
├─module-c
└─module-yguard
在module-yguard
中添加a,b,c的依赖,保证最后执行,在module-yguard
中配置yGuard插件,在配置中对a,b,c进行混淆,并且在执行发布命令时通过 -pl !module-yguard
排除混淆模块的构建,避免多次混淆。
在构建时分成两步不会增加更多的时间,也没有变的更复杂,通过这种方式就实现了多模块混淆。
下面是本文示例的配置:
<tasks>
<property name="runtime_classpath" refid="maven.runtime.classpath"/>
<taskdef name="yguard" classname="com.yworks.yguard.YGuardTask" classpath="${runtime_classpath}"/>
<yguard>
<inoutpair in="..\module-b\target\module-b-${project.version}.jar"
out="..\module-b\target\module-b-${project.version}.jar"/>
<inoutpair in="..\module-c\target\module-c-${project.version}.jar"
out="..\module-c\target\module-c-${project.version}.jar"/>
<inoutpair in="..\module-a\target\module-a-${project.version}.jar"
out="..\module-a\target\module-a-${project.version}.jar"/>
<attribute name="SourceFile, LineNumberTable, LocalVariableTable">
<patternset>
<include name="org.example.**"/>
</patternset>
</attribute>
<rename logfile="${project.build.directory}/yguard.log.xml"
replaceClassNameStrings="true">
<keep>
<class classes="none" methods="none" fields="none">
<patternset>
<include name="org.example.a."/>
<include name="org.example.b."/>
<include name="org.example.c.util.FileUtil"/>
</patternset>
</class>
<class classes="private" methods="private" fields="private">
<patternset>
<include name="org.example.c."/>
<exclude name="org.example.c.util.FileUtil"/>
</patternset>
</class>
</keep>
<property name="naming-scheme" value="best"/>
<property name="language-conformity" value="illegal"/>
<property name="overload-enabled" value="true"/>
</rename>
<externalclasses>
<pathelement path="${runtime_classpath}"/>
</externalclasses>
</yguard>
</tasks>
混淆后的代码效果: 使用的jadx反编译工具,一次可以同时查看多个jar。
在这个混淆示例中,相当于只暴露了 UserService
和 User
接口,如果是支持ioc注入的情况下,可以注入 UserService
接口进行使用,但是其他被混淆的就不方便被使用。
虽然这里很简单,但是我实际要处理的这个项目有4级多模块,总共能打包七十几个模块,而且要和旧版打包方式接近,因此耗时很久,最难的就是上一节介绍的,如果有多种混淆配置(<class>
),多种配置之间的排除会很麻烦。
到这里就解决了一个难题,但是更难的还在后头,如何在混淆代码的基础上实现反篡改?
标签:混淆,jar,module,构建,笔记,模块,yguard From: https://blog.51cto.com/isea533/7693617