java中的代码混淆技术

描述

今天和大家一起学习下java中的代码混淆技术,后面你也可以让你的代码不再裸露在外了,让人轻易窥视

代码混淆技术

当需要阅读jar文件的内容时,可能你会使用一些反编译工具,比如jd-gui,但是否有遇到反编译后的内容和想象的不一样,但正常引用该jar又都是正常的?

开始的话

前段时间,使用了docx4j的库来操作.docx文件进行一些复杂的操作,比如对多个docx文件进行合并,在网上找了很多的方式发现最终生成的文档都有很多多余的内容, 导致原本几兆的文件合并后有几十兆,记得docx4j官网有提供商业版本的方法,准备窥探下其源码来研究下,然而当我下载好jar后打开时,我蒙了...

开源

简介

我们知道,一般情况下编译打包后的jar文件可以通过反编译工具看到jar中的接口、类、方法都是可以被,这样相关的代码实现很容易被模仿借鉴,企业的核心代码很可能被人盗用。特别是一些涉密较强或者商业性的行业软件,当被拿到jar并反编译后如同开源一般。那么通过对class文件进行字节码级别的混淆加密,就能够在一定程度防止技术被模仿或复用, 从而对java软件起到很好的保护作用。

实现方式

  1. 对class文件进行加密,但是需要特定的Classloader在加载class时对其解密
  2. 针对class文件反编译原理,通过花指令防止文件被反编译
  3. 基于代码混淆技术,对代码中的包、类、方法等名称进行混淆,从而提高代码阅读成本

示例

今天主要介绍通过第3种方法实现代码混淆,这里主要使用了proguard工具对应的maven插件 proguard-maven-plugin :

Proguard是一个Java类文件压缩器、优化器、混淆器、预校验器。压缩环节会检测以及移除没有用到的类、字段、方法以及属性。优化环节会分析以及优化方法的字节码。混淆环节会用无意义的短变量去重命名类、变量、方法。这些步骤让代码更精简,更高效,也更难被逆向(破解)

比如我们基于Restful开发一个用户服务接口

  1. 可能你的项目结构会是这样的:
packages...
├ entity
|  ├ User
├ dao
|  ├ UserDao
|  ├ impl
|    ├ UserDaoImpl
├ service
|  ├ UserService
|  ├ impl
|    ├ UserServiceImpl
├ web
|  ├ UserController

通过命令mvn package打包后,结构是这样的:

开源

  1. 现在引入proguard:

需要在pom.xml中build标签中加入插件,具体配置如下:

< !-- ProGuard混淆插件-- >
            < plugin >
                < groupId >com.github.wvengen< /groupId >
                < artifactId >proguard-maven-plugin< /artifactId >
                < version >2.6.0< /version >
                < executions >
                    < execution >
                        < !-- 混淆时刻,这里是打包的时候混淆-- >
                        < phase >package< /phase >
                        < goals >
                            < !-- 使用插件的什么功能-- >
                            < goal >proguard< /goal >
                        < /goals >
                    < /execution >
                < /executions >
                < configuration >
                    < !-- 是否将生成的PG文件安装部署-- >
                    < attach >true< /attach >
                    < !-- 对什么东西进行加载,这里仅有classes成功,毕竟你也不可能对配置文件及JSP混淆吧-- >
                    < injar >${project.build.finalName}.jar< /injar >
                    < !--class 混淆后输出的jar包-- >
                    < outjar >${project.build.finalName}-pg.jar< /outjar >
                    < !-- 是否混淆-- >
                    < obfuscate >true< /obfuscate >
                    < !-- 配置一个文件,通常叫做proguard.cfg,该文件主要是配置options选项,也就是说使用proguard.cfg那么options下的所有内容都可以移到proguard.cfg中 -- >
                    < proguardInclude >${project.basedir}/proguard.cfg< /proguardInclude >
                    < !-- 指定生成文件分类 -- >
                    < attachArtifactClassifier >pg< /attachArtifactClassifier >
                    < !-- 额外的jar包,通常是项目编译所需要的jar -- >
                    < libs >
                        < lib >${java.home}/lib/rt.jar< /lib >
                    < /libs >
                    < !-- 对输入jar进行过滤比如,如下配置就是对META-INFO文件不处理。 -- >
                    < inLibsFilter >!META-INF/**< /inLibsFilter >
                    < !-- 这是输出路径配置,但是要注意这个路径必须要包括injar标签填写的jar -- >
                    < outputDirectory >${project.basedir}/target< /outputDirectory >
                    < !--这里特别重要,此处主要是配置混淆的一些细节选项,比如哪些类不需要混淆,哪些需要混淆-- >
                    < options >
                        < !-- 可以在此处写option标签配置,不过我上面使用了proguardInclude,故而我更喜欢在proguard.cfg中配置 -- >
                    < /options >
                < /configuration >
            < /plugin >

其中配置文件proguard.cfg如下:

#指定Java的版本
-target 1.8
#proguard会对代码进行优化压缩,他会删除从未使用的类或者类成员变量等
-dontshrink
#是否关闭字节码级别的优化,如果不开启则设置如下配置
-dontoptimize
#混淆时不生成大小写混合的类名,默认是可以大小写混合
-dontusemixedcaseclassnames
# 对于类成员的命名的混淆采取唯一策略
-useuniqueclassmembernames
#混淆时不生成大小写混合的类名,默认是可以大小写混合
-dontusemixedcaseclassnames
#混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代
-adaptclassstrings
 
#对异常、注解信息予以保留
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# 此选项将保存接口中的所有原始名称(不混淆)-- >
# -keepnames interface ** { *; }
# 此选项将保存所有软件包中的所有原始接口文件(不进行混淆)
#-keep interface * extends * { *; }
#保留参数名,因为控制器,或者Mybatis等接口的参数如果混淆会导致无法接受参数,xml文件找不到参数
-keepparameternames
# 保留枚举成员及方法
-keepclassmembers enum * { *; }
# 不混淆所有类,保存原始定义的注释-
-keepclassmembers class * {
                        @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 *;
                        }
 
#忽略warn消息
-ignorewarnings
#忽略note消息
-dontnote
#打印配置信息
-printconfiguration
  1. 执行打包命令mvc package,可以看到target目录下新增了几个文件
  • obfuscation-pg.jar 混淆处理后的输出jar
  • proguard_map.txt 存放混淆前后类、方法的对应关系
  • proguard_seed.txt 存放保持不变的类 可见包的名称、类名都改成了短字母

开源

实现技术

通过proguard来实现class内容的混淆相对比较简单,当然还有很多其他的技术方法,比如上面说到的对class进行加密这种更安全的技术手段,感兴趣的你可以继续探究。

其他技术

  • Jocky
  • retroguard
  • androidkiller
  • ClassFinal

结束语

此篇文章简单介绍了java中的代码混淆技术,我们可以根据具体的项目需求对编译后的代码进行混淆或加密处理,从而保护自己的劳动成果。开头看到的docx4j企业级功能提供 的jar具体是怎么实现代码保护的,目前还没发现其具体采用了什么技术实现,后面继续研究。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分