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以前,咱們要先了解一些java的基本概念,如:ClassLoader。作java的人已經或者之後會知道,java程序的運行,是類中的邏輯在JVM中運行,而類又是怎麼加載到JVM中的呢(JVM內幕之類的,不在本文中闡述,因此點到爲止)?答案是:ClassLoader。JVM在啓動時是如何初始化整個環境的,有哪些ClassLoader及做用是什麼,你們能夠本身問度娘,也不在本文中討論。app
讓咱們從最多見的代碼開始,揭開一下ClassLoader的一點點面紗!看下面的代碼:jvm
上面這段代碼,你們都認識。但我要問的是:若是咱們使用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文件就好了,在這以前是什麼樣子無所謂!因此,咱們的加密的整個邏輯就是:
如此,搞定!以上的作法比較完整的闡述,能夠仔細閱讀一下這篇文章:http://www.ibm.com/developerworks/cn/java/l-secureclass/文章中的介紹。
經過這個方法貌似能夠解決代碼反編譯的問題了!錯!這裏有一個巨大的坑!由於咱們自定義的ClassLoader是不能加密的,要否則JVM不認識,就全歇菜了!若是我來反編譯,呵呵,我只要反編譯一下這個自定義的ClassLoader,而後把裏面解密後的內容寫到指定的文件中保存下來,再把這個加了邏輯的自定義ClassLoader放回去運行,你猜結果會怎樣?沒錯,你會想死!由於你好不容易想出來的加密算法,結果人家根本不須要破解,直接就繞過去了!
如今,讓咱們總結一下這個方法的優缺點:實現方式簡單有效,同時對代碼幾乎沒有侵入性,不影響正常開發與發佈。缺點也很明顯,就是很容易被人破解!
固然啦,關於缺點問題,你也能夠這麼幹:先對全部代碼進行混淆、再進行加密,保證:一、不容易找到咱們自定義的那個ClassLoader;二、就算找到了,破解了,代碼可讀性仍是不好,讓你看得吐血!(有一篇文章,我以爲寫得不錯,你們能夠看一看:http://cjnetwork.iteye.com/blog/851544)
嗯,我以爲這個方法很好,我本身也差點被這個想法感動了,可是,做爲一個嚴謹的程序員,我真的不肯意留下一個隱患在這裏!因此,我繼續思索!
前面咱們說過有個伏筆來着,還記得吧?沒錯,就是那個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版本在業界其實也有好幾個版本的,如:Sun公司的、IBM的、Apache的、Google的……
因此,不要阻礙本身的想象力,如今沒有這個能力,並不表明不可能。因此,我想到,若是我把jvm改了,在裏面對加載的類進行解密,那不就能夠了嗎?我在設計構思過程當中,忽然發現:人老了就是容易糊塗!前面使用第三方語言實現解密的兩個問題,正好也是更改JVM要面對的兩個問題,並且還有一個更大的問題:這個JVM就得跟着這個項目處處走啊!有一個相似的思路是這樣:
http://wenku.baidu.com/link?url=T0LOwOI5DDFRoFGJp_vzwq8x6OADAcnHcNBOFqij5jz7Rvt1wsjLe8Qa2sJFUBQha88A5csGqDH5yXIjWVV4i54x-iXtYpPSO9kJtQLhvNy
因而,我把構思與設計從頭又想了想,終於……放棄了!
前面能夠說,我能想到的方式,我都想到了,都不能令我滿意。因而我在想,有沒有一個讓我感受代價又小,但卻無隱患的方式呢?
我如今還沒想出來,只能待續……
(不日即有答案,由於進度日期將近……等有答案,必定補上,與你們討論,或者你們給些意見,謝謝!)
通過對java不斷深刻研究,以及多年來對java知識的積累,最重要的是,一種遇到困難時執着向前的精神,最終使得問題的解決愈來愈完善!雖然走過彎路,但每條彎路上都有許多收穫,就像我曾經作得的分享裏面說的:同一個現象,不一樣的視野將獲得不一樣的結果!如何提高本身視野?就在於平時不斷的自我追求和強烈的求知慾!
這個總結,算是自詡一下,也算是給新入行的「菜鳥」們一個建議吧~