java如何防止反编译

综述(写在前面的废话)

Java从诞生以来,其基因就是开放精神,也正所以,其能够获得普遍爱好者的支持和奉献,最终很快发展壮大,以致于有今天之风光!但随着java的应用领域愈来愈广,特别是一些功能要发布到终端用户手中(如Android开发的app),有时候,公司为了商业技术的保密考虑,不但愿这里面的一些核心代码可以被人破解(破解以后,甚至能够被简单改改就发布出去,说严重点,就可能会扰乱公司的正常软件的市场行为),这时候就要求这些java代码不可以被反编译。java

这里要先说一下反编译的现象。由于java一直秉持着开放共享的理念,因此你们也都知道,咱们通常共享一个本身写的jar包时,同时会共享一个对应的source包。但这些依然与反编译没有什么关系,但java的共享理念,不仅是建议咱们这样作,并且它本身也在底层上“强迫”咱们这么作!在java写的.java文件后,使用javac编译成class文件,在编译的过程,不像C/C++或C#那样编译时进行加密或混淆,它是直接对其进行符号化、标记化的编译处理,因而,也产生了一个逆向工程的问题:能够根据class文件反向解析成原来的java文件!这就是反编译的由来。linux

但不少时候,有些公司出于如上述的缘由考虑时,真的不但愿本身写的代码被别人反编译,尤为是那些收费的app或桌面软件(甚至还有一些j2ee的wen项目)!这时候,防止反编译就成了必然!但前面也说过了,由于开放理念的缘由,class是能够被反编译的,那如今有这样的需求以后,有哪些方式能够作到防止反编译呢?通过研究java源代码并进行了一些技术实现(结果发现,之前都有人想到过,因此在对应章节的时候,我会贴出一些写得比较细的文章,而我就简单阐述一下,也算偷个懒吧),我总共整理出如下这几种方式:c++

代码混淆

这种方式的作法正如其名,是把代码打乱,并掺入一些随机或特殊的字符,让代码的可读性大大下降,“曲线救国”似的达到所谓的加密。其实,其本质就是打乱代码的顺序、将各种符号(如类名、方法名、属性名)进行随机或乱命名,使其无心义,让人读代码时很累,进而让人乍一看,觉得这些代码是加过密的!程序员

由其实现方式上可知,其实现原理只是扰乱正常的代码可读性,并非真正的加密,若是一我的的耐心很好,依然能够理出整个程序在作什么,更况且,一个应用中,其核心代码才是人们想去了解的,因此大大缩小了代码阅读的范围!web

固然,这种方式的存在,并且还比较流行,其缘由在于,基本能防范一些技术人员进行反编译(好比说我,让我破解一个混淆的代码,我宁愿本身重写一个了)!并且其实现较为简单,对项目的代码又无开发上的侵入性。目前业界也有较多这类工具,有商用的,也有免费的,目前比较流行的免费的是:proguard(我现象临时用的就是这个)。算法

上面说了,这种方式其实并非真正加密代码,其实代码仍是可以被人反编译(有人可能说,使用proguard中的optimize选项,能够从字节流层面更改代码,甚至可让JD这些反编译软件能够没法获得内容。说得有点道理,但有两个问题:一、使用optimize对JDK及环境要求较高,容易形成混淆后的代码没法正常运行;二、这种方式其实仍是混淆,JD反编译有点问题,能够有更强悍的工具,矛盾哲学在哪儿都是存在的^_^)。那如何能作到个人class代码没法被人反编译呢?那就须要咱们下面的“加密class”!windows

加密class

在说加密class以前,咱们要先了解一些java的基本概念,如:ClassLoader。作java的人已经或者之后会知道,java程序的运行,是类中的逻辑在JVM中运行,而类又是怎么加载到JVM中的呢(JVM内幕之类的,不在本文中阐述,因此点到为止)?答案是:ClassLoader。JVM在启动时是如何初始化整个环境的,有哪些ClassLoader及做用是什么,你们能够本身问度娘,也不在本文中讨论。app

让咱们从最多见的代码开始,揭开一下ClassLoader的一点点面纱!看下面的代码:jvm

 

Java代码   收藏代码
  1. public class Demo{  
  2.         public static void main(String[] args){  
  3.             System.out.println(“hello world!”);  
  4.         }  
  5.     }  

 

 

上面这段代码,你们都认识。但我要问的是:若是咱们使用javac对其进行编译,而后使用java使其运行(为何不在Eclipse中使用Run as功能呢?由于Eclipse帮咱们封闭,从而简化了太多东西,使咱们忽略了太多的底层细节,只有从原始的操做上,咱们才能看到本质),那么,它是怎么加载到JVM中的?答案是:经过AppClassLoader加载的(相关知识点能够参考:http://hxraid.iteye.com/blog/747625)!若是不相信的话,能够输出一下System.out.println(Thread.currentThrea().getContextLoader());看看。maven

那又有一个新的问题产生了:ClassLoader又是怎样加载class的呢?其实,AppClassLoader继承自java.lang.ClassLoader类,因此,基本操做都在这个类里面,让咱们直接看下面这段核心代码吧:



 

看看这个方法中的逻辑,很是简单,先从内存中找,若是没有,则从父级或根先找,若是没找到,则再从本身的方法里面找!那findClass里面是什么样的呢?很不幸,这个方法是个抽象(abstract)的,也就是使用什么方式加载,由程序使用ClassLoader本身决定!这就给咱们留下了巨大的“”!让咱们看一下很是常见的一个ClassLoader的实现,那就是URLClassLoader(几乎全部的j2ee的web项目的容器使用的ClassLoader都是继承自它),让咱们看一下它的findClass的实现:



 

这个方法里面的逻辑也很简单,从定义的ucp(就是各个jar包或class文件的具体路径)中读取指定的class文件的信息(如字节流之类),而后交给defineClass定义到JVM中,让咱们继续看一下这个方法的核心部分:

 

 

看到这里,已经没有必要再往下面看了(再往下就是native方法了,这是一个重大伏笔哦),咱们要作的手脚就在这里!

手脚怎么作呢?很简单,上面的代码逻辑告诉咱们,ClassLoader只是拿到class文件中的内容byte[],而后交给JVM初始化!因而咱们的逻辑就简单了:只要在交给JVM时是正确的class文件就好了,在这以前是什么样子无所谓!因此,咱们的加密的整个逻辑就是:

    1. 在编译代码时(如使用ant或maven),使用插件将代码进行加密(加密方式本身选),将class文件里面的内容读取成byte[],而后进行加密后再写回到class文件(这时候class文件里面的内容不是标准的class,没法被反编译了)
    2. 在启动项目代码时,指定使用咱们自定义的ClassLoader就好了,而自定义的部分,主要就是在这里作解密工做!

如此,搞定!以上的作法比较完整的阐述,能够仔细阅读一下这篇文章:http://www.ibm.com/developerworks/cn/java/l-secureclass/文章中的介绍。

经过这个方法貌似能够解决代码反编译的问题了!错!这里有一个巨大的坑!由于咱们自定义的ClassLoader是不能加密的,要否则JVM不认识,就全歇菜了!若是我来反编译,呵呵,我只要反编译一下这个自定义的ClassLoader,而后把里面解密后的内容写到指定的文件中保存下来,再把这个加了逻辑的自定义ClassLoader放回去运行,你猜结果会怎样?没错,你会想死!由于你好不容易想出来的加密算法,结果人家根本不须要破解,直接就绕过去了!

如今,让咱们总结一下这个方法的优缺点:实现方式简单有效,同时对代码几乎没有侵入性,不影响正常开发与发布。缺点也很明显,就是很容易被人破解!

固然啦,关于缺点问题,你也能够这么干:先对全部代码进行混淆、再进行加密,保证:一、不容易找到咱们自定义的那个ClassLoader;二、就算找到了,破解了,代码可读性仍是不好,让你看得吐血!(有一篇文章,我以为写得不错,你们能够看一看:http://cjnetwork.iteye.com/blog/851544)

嗯,我以为这个方法很好,我本身也差点被这个想法感动了,可是,做为一个严谨的程序员,我真的不肯意留下一个隐患在这里!因此,我继续思索!

高级加密class

前面咱们说过有个伏笔来着,还记得吧?没错,就是那个native!native定义的方法是什么方法?就是咱们传说中的JNI调用!前面介绍过的有一篇文章中提到过,其实jvm的真实身份并非java,而是c++写的jvm.dll(windows版本下),java与dll文件的调用就是经过JNI实现的!因而,咱们就能够这样想:JNI能够调用第三方语言的类库,那么,咱们可不能够把解密与装载使用第三方语言写(如C++,由于它们生成的库是很差反编译的),这样它能够把解密出来的class内容直接调jvm.dll的加载接口进行初始化成class,再返回给咱们的ClassLoader?这样,咱们自定义的ClassLoader只要使用JNI调用这个第三方语言写的组件,整个解密过程,都在黑盒中进行,别人就无从破解了!

嗯,这个方法真的很不错的!但也有两个小问题:1.使用第三方语言写,得会第三方语言,我说的会,是指很溜!2.对于不一样的操做系统,甚至同一操做系统不一样的版本,均可能要有差别化的代码生成对应环境下的组件(如window下是exe,linux是so等)!若是你不在意这两个问题,我以为,这个方式真的挺不错的。但对于我来讲,个人信条是,越复杂的方式越容易出错!我我的比较崇尚简洁的美,因此,这个方法我不会轻易使用!

对了,若是你们以为这个方法还算可行的话,能够推荐一个我无心中看到的东西给你们看看(我都没有用过的):jinstall,还有一个叫:http://download.csdn.net/detail/yzjcnlpj/3296134

更改JVM

看到这个标题,我想你可能会震惊。是的,你没看错,作为一个程序员,是应该要具备怀疑一切、敢想敢作的信念。若是你有意留心的话,你会发现JVM版本在业界其实也有好几个版本的,如:Sun公司的、IBM的、Apache的、Google的……

因此,不要阻碍本身的想象力,如今没有这个能力,并不表明不可能。因此,我想到,若是我把jvm改了,在里面对加载的类进行解密,那不就能够了吗?我在设计构思过程当中,忽然发现:人老了就是容易糊涂!前面使用第三方语言实现解密的两个问题,正好也是更改JVM要面对的两个问题,并且还有一个更大的问题:这个JVM就得跟着这个项目处处走啊!有一个相似的思路是这样:

http://wenku.baidu.com/link?url=T0LOwOI5DDFRoFGJp_vzwq8x6OADAcnHcNBOFqij5jz7Rvt1wsjLe8Qa2sJFUBQha88A5csGqDH5yXIjWVV4i54x-iXtYpPSO9kJtQLhvNy

因而,我把构思与设计从头又想了想,终于……放弃了!

设想

前面能够说,我能想到的方式,我都想到了,都不能令我满意。因而我在想,有没有一个让我感受代价又小,但却无隐患的方式呢?

我如今还没想出来,只能待续……

(不日即有答案,由于进度日期将近……等有答案,必定补上,与你们讨论,或者你们给些意见,谢谢!)

    

总结

通过对java不断深刻研究,以及多年来对java知识的积累,最重要的是,一种遇到困难时执着向前的精神,最终使得问题的解决愈来愈完善!虽然走过弯路,但每条弯路上都有许多收获,就像我曾经作得的分享里面说的:同一个现象,不一样的视野将获得不一样的结果!如何提高本身视野?就在于平时不断的自我追求和强烈的求知欲!

这个总结,算是自诩一下,也算是给新入行的“菜鸟”们一个建议吧~

相关文章
相关标签/搜索