大家好,你的月亮我的心,我是博主小阿金,欢迎各位工友。 我们公司的测试同学,绝对是我的一生之敌,平常没事就喜欢给找点难搞的小bug来提提兴趣,这不昨天又给抛出来一个安全测试bug,闲着没事把我们的后端jar反编译了,拿到源码就是咔咔一顿测试,结果可想而知,博主这就来折腾反编译的工作了,虽然有点烦,但是这位仁兄提出的问题也不无道理,值得深究一下。
一. 混淆
当前的世界抄袭不断,当然本着开源的精神,我们也提倡代码共享,但是绝对拒绝代码抄袭,为了防止世界被破坏,为了守护世界的和平,自然而然衍生了许多代码混淆的技术,那么何为代码混淆呢?
答案就是一种通过对代码进行修改和重构,使其变得难以理解和逆向工程的技术。主要被用于保护软件知识产权和防止恶意攻击者对代码进行逆向分析。
二. 选择工具
工欲善其事必先利其器,要想种地必须先挑好武器,在Java开发中,有一些常用的代码混淆工具,如ProGuard、Allatori Java Obfuscator、DashO等。这些工具可以帮助开发者对Java代码进行混淆处理,保护代码的安全性。综合以上对比,本次选择ProGuard这款工具进行讲解,毕竟群众的眼睛是雪亮的,而且他还是免费的!!!
反编译工具下载:http://java-decompiler.github.io/ 解压后直接打开jd-gui.exe文件即可,然后把打包好的jar直接丢进去,就可以完美的看到反编译后的代码结构了。
三. 代码混淆
3.1配置混淆规则
第一步, 在项目根路径下,新增一份混淆规则文件 proguard.cfg
#指定Java的版本
-target 1.8
#proguard会对代码进行优化压缩,他会删除从未使用的类或者类成员变量等
-dontshrink
#是否关闭字节码级别的优化,如果不开启则设置如下配置
-dontoptimize
#混淆时不生成大小写混合的类名,默认是可以大小写混合
-dontusemixedcaseclassnames
# 对于类成员的命名的混淆采取唯一策略
-useuniqueclassmembernames
# 保留异常、注解信息
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# 保留接口中的所有原始名称(不混淆)
-keepnames interface ** { *; }
# 保留参数名,因为控制器,或者Mybatis等接口的参数如果混淆会导致无法接受参数,xml文件找不到参数
-keepparameternames
# 保留枚举成员及方法
-keepclassmembers enum * { *; }
# 保留特定注解的类和方法
-keepclasseswithmembers class * {
@org.springframework.context.annotation.Bean <methods>;
@org.springframework.beans.factory.annotation.Autowired <fields>;
@org.springframework.beans.factory.annotation.Value <fields>;
@org.springframework.stereotype.Service <fields>;
@org.springframework.stereotype.Component <fields>;
}
# 忽略note消息
-dontnote
# 打印配置信息
-printconfiguration
# 保留主应用程序入口点
-keep public class com.xx.xxApplication {
public static void main(java.lang.String[]);
}
3.2引入插件依赖
注意proguard依赖顺序应该在maven-plugin之上,否则编译不会生效。
<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>
<!-- 这是输出路径配置,但是要注意这个路径必须要包括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.5.15</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>com.xx.xxApplication</mainClass>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
经过如上配置后可以发现,maven管理页面已经出现了proguard打包程序,proguard报红不要怕,一般是idea缓存问题导致,清除一下缓存重启idea就可以了。
3.3验证
3.3.1:问题一
本来想着配置完事,直接打包应该就没问题了,可惜天不遂人愿,一点打包就会莫名其妙的报错,而且没有一点头绪,本着用新不用旧的问题,博主升级了一下依赖,发现完美的解决了这个问题,有相同问题的小伙伴可以尝试一下,把proguard版本升级到2.6.1。
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<version>2.6.1</version>
3.3.2:问题二
完美打好jar包之后,拿到加密后的jar扔到反编译工具测试一下,也是完美的通过,最后在运行的时候又报了一个错误,
错误信息如下:
Annotation-specified bean name 'a' for bean class [com.document.a.a.b.a] conflicts with existing, non-compatible bean definition of same name and class [com.document.a.a.a.a]
这意味着在混淆过程中,com.document.a.a.b.a
和 com.document.a.a.a.a
被重命名为相同的名称 a
,从而导致Spring容器中的Bean名称冲突。
为了避免这种情况,我们需要修改ProGuard的配置,以确保这些特定的类和包不被混淆。可以在proguard.cfg文件中添加如下配置后,问题完美解决。
# 保留特定包中的类(假设你有一个特定的包需要完全保留)
-keep class com.document.web.** { *; }
到此项目完美成功的运行起来,如有帮助到你,欢迎点赞评论转发哟!