分享java帶來的快樂

          我喜歡java新東西

          Jocky混淆JAVA代碼

          一、前言

          1.1 什么是Jocky?

             我們知道,Java是一種跨平臺的編程語言,其源碼(.java文件)被編譯成與平臺無關的字節碼(.class文件),然后在運行期動態鏈接。這樣,編譯后的類文件中將包含有符號表,從而使得Java程序很容易被反編譯。相信每一個Java開發人員,都曾經用過諸如Jad之類的反編譯器,對Java的class 文件進行反編譯,從而觀察程序的結構與實現細節。如此一來,對于那些需要嚴格進行知識產權保護的Java應用,如何有效的保護客戶的商業投資,是開發人員經常需要面對的問題。
             于是就出現了Java混淆編譯器,它的作用是打亂class文件中的符號信息,從而使反向工程變得非常困難。
             Jocky就是這樣一款優秀的Java混淆編譯器。

          1.2 為什么需要Jocky?

             目前業界有不少商業的甚或是開源的混淆編譯器,但它們普遍存在一些這樣或者那樣的問題。一般而言,現有的混淆器都是對編譯好的 class文件進行混淆,這樣就需要編譯和混淆兩個步驟。而事實上,并不是所有的符號都需要混淆。如果你開發的是一個類庫,或者某些類需要動態裝載,那些公共API(或者說:那些被publish出來的API)就必須保留符號不變,只有這樣,別人才能使用你的類庫。現有的混淆器提供了GUI或腳本的方式來對那些需要保留的符號名稱進行配置,但如果程序較大時,配置工作將變得很復雜,而程序一旦修改,配置工作又要重新進行。某些混淆器能夠調整字節碼的順序,使反編譯更加困難,但筆者經歷過混淆之后的程序運行出錯的情況。
             而Jocky與其它混淆編譯器最大的不同之處在于:它是直接從源碼上做文章,也就是說編譯過程本身就是一個混淆過程。

          1.3 Jocky是如何工作的?

             Jocky混淆編譯器是在Sun JDK中提供的Java編譯器(javac)的基礎上完成的,修改了其中的代碼生成過程,對編譯器生成的中間代碼進行混淆,最后再生成class文件,這樣編譯和混淆只需要一個步驟就可以完成。另外可以在源程序中插入 符號保留指令 來控制哪些符號需要保留,將混淆過程與開發過程融合在一起,不需要單獨的配置。

          1.4 Jocky的作用

          1.4.1代碼混淆

             如前文所述,混淆編譯是Jocky的首要用途。我們舉一個最簡單的例子,下面的SimpleBean是未經混淆的class文件通過Jad反編譯以后獲得的源文件:

           1public class SimpleBean implements Serializable {
           2  
           3  private String name = "myname";
           4  
           5  private List myList = null;
           6  
           7  public void SimpleBean() {
           8    myList = new ArrayList(10);
           9  }

          10
          11  public void foo1() {
          12    myList.add("name");
          13  }

          14  
          15  private void foo2() {    
          16  }

          17  
          18  private void writeObject(java.io.ObjectOutputStream out)
          19      throws IOException {
          20    
          21  }

          22
          23}
           
          <未混淆的類文件反編譯后的效果>

             下面是經Jocky混淆過的類文件,通過Jad反編譯后產生的源文件:

           1public class SimpleBean implements Serializable {
           2
           3  private String _$2;
           4
           5  private List _$1;
           6
           7  public SimpleBean()     {
           8        _$2 = "myname";
           9        this;
          10        JVM INSTR new #4   <Class ArrayList>;
          11        JVM INSTR dup ;
          12        JVM INSTR swap ;
          13        10;
          14        ArrayList();
          15        _$1;
          16    }
            
          17  public void foo1() {
          18    _$1.add("name");
          19  }

          20
          21  private void _$1() {
          22  }

          23
          24  private void writeObject(ObjectOutputStream objectoutputstream){
          25      throws IOException {
          26  }

          27}
           
          <Jocky混淆過的類文件反編譯的效果>

          1.4.2 支持將JDK 5.0的語法編譯成能夠在JDK 1.4上運行的類文件

             JDK 5.0在語法層面上有許多新增特色,能夠為簡化應用的開發帶來一些便利。譬如Generics、Enhanced for Loop以及 Autoboxing/Unboxing等。但另人遺憾的是,倘若利用這些新的語法開發應用,就意味著不能夠在JDK 1.4上運行,而JDK 1.4畢竟是目前最為普及的VM版本。幸運是,Jocky的另一個特色就是:通過參數配置,能夠把用JDK 5.0語法編寫的應用編譯成JDK 1.4上的類文件版本。我們可以把經過 Jocky編譯的類文件以UltraEdit打開,可以發現在第8個字節上(類文件的major version)的數值是0x30,即十進制的48,這是JDK 1.4所能夠理解的類文件版本(JDK 5.0默認編譯的類文件版本是49)。前提是:應用中不能夠使用JDK 1.4中所沒有的一些API。

          二、Jocky的用法

          2.1 常規用法

             使用Jocky非常簡單,獲得jocky.jar以后,只需要運行java -jar jocky.jar就可以啟動Jocky混淆編譯器,jocky的命令行參數和javac完全相同,但增加了一個新的參數-scramble,它的用法如下:
          -scramble          混淆所有package private或private符號
          -scrambleall       混淆所有符號
          -scramble:
          <level>  混淆相應級別的符號
          其中
          <level>指定混淆級別,可以是以下幾種級別:
          -scramble:none        不進行混淆
          -scramble:private     對所有private訪問級別的元素進行混淆
          -scramble:package     對所有private或package private元素進行混淆
          -scramble:protected   對所有private, package private, protected元素進行混淆
          -scramble:public      對所有的元素都進行混淆
          -scramble:all         相當于-scramble:public
          如果使用-scramble不帶級別參數,則相當于-scramble:package

          2.2 Jocky for Ant

              近年來,Ant已經成為Java應用開發中打包工具的事實上的標準。在應用的開發過程中,我們往往都會有一個Ant腳本,通過該腳本,能夠對應用進行編譯、打包、發布等一系列過程。因此,Jocky的最佳切入點便是對Ant的支持。
              在Ant中使用Jocky非常簡單:

              1. 將lib\jocky-ant.jar copy至ANT_HOME\lib目錄下。

              2. 在ant腳本中加入這樣一行代碼,以引入Jocky Task
            
               <taskdef resource="jockytasks/"> 

              3. 設置Jocky的一些基本屬性,包括: jocky.jar包的位置,以及混淆級別,如下所示:
              
             <jocky jar=" F:\Works2\Jocky\jocky1.0\lib\jocky.jar" enable="true" level="private/"> 

              4. 當設置jocky的enable屬性為true時,此時,Ant腳本中的javac編譯命令,便會被自動替換成Jocky編譯器;當設置enable屬性為false時,javac編譯命令將恢復成正常設置,示例腳本如下:

           1<project name="jocky" default="build"> 
           2<!-- 引入Jocky Ant Task,要確保jocky-ant.jar位于ANT_HOME\lib目錄下 -->
           3<taskdef resource="jockytasks"> </taskdef>
           4<target name="build"> 
           5<!-- 設置jocky.jar的位置以及混淆級別,當enable為true時,javac task將被自動替換成Jocky混淆編譯器 -->
           6<jocky jar=" F:\Works2\Jocky\jocky1.0\lib\jocky.jar" enable=" true" level=" private"> </jocky> 
           7<!-- 下面的編譯,將使用Jocky混淆編譯器 -->
           8<javac destdir="bin2" debug="on" source="1.5" target="1.4"> 
           9<src path="src"></src>
          10</javac>
          11<!-- 當enable為false時,javac task將被恢復成正常設置, Jocky編譯器不再起作用 -->
          12<jocky enable="false"></jocky>
          13<!-- 下面的編譯,將使用正常的Javac編譯器 -->
          14<javac destdir="bin3" debug="on" target="1.4"> 
          15<src path="src"></src>
          16</javac>
          17</target>
          18</project>
          <Jocky的Ant腳本示例>

              注意: Jocky for Ant在Ant 1.6.5上開發,推薦使用該版本。

          2.3 Jocky for Eclipse

              Jocky提供了Eclipse的插件,從而能夠直接在Eclipse中使用Jocky。
              1. Jocky插件的安裝:
              將Jocky插件安裝至Eclipse中非常簡單,只需要將eclipse/plugins/org.apusic.jocky_1.0.0目錄 copy 至 Eclipse的 plugins目錄即可。或者在Eclipse/links文件夾中,通過link方式指定Jocky的插件目錄。

              2. 在Eclipse中使用Jocky:
              在Eclipse中使用Jocky也非常簡單,任何一個Java工程,選中工程通過右鍵菜單,都可以出現Jocky的快捷菜單:

           
          <Jocky在Eclipse中的右鍵菜單>

           
          <Jocky在Eclipse中的屬性設置>


              事實上,在Eclipse中使用Jocky時,Jocky也是首先針對所選工程生成Ant的Build文件(默認名稱jocky_build.xml),然后再通過Ant完成混淆編譯。

              以下是Jocky在Eclipse中自動生成的Ant Build 文件示例:

           1<project basedir="." default="build" name="jocky.example.jocky"> 
           2<property name="jocky.jar" value="f:\EclipseWTP1.0.8\workspace_jdk5_apusicstudio\org.apusic.jocky\jocky.jar"></property>
           3<property name="jocky.output.dir" value="jocky"></property>
           4<property name="jocky.scramble.level" value="package"></property>
           5<property name="target" value="1.4"></property>
           6<path id="project.classpath"> 
           7<pathelement location="bin"></pathelement>
           8</path>
           9<target name="init"> 
          10<jocky jar="${jocky.jar}" level="${jocky.scramble.level}"></jocky>
          11<mkdir dir="${jocky.output.dir}"></mkdir>
          12<mkdir dir="${jocky.output.dir}/bin"></mkdir>
          13</target>
          14<target name="clean"> 
          15<delete dir="${jocky.output.dir}/bin"></delete>
          16<delete dir="${jocky.output.dir}"></delete>
          17</target>
          18<target depends="init" name="build"> 
          19<echo message="${ant.project.name}: ${ant.file}"></echo>
          20<jocky enable="true"></jocky>
          21<javac destdir="${jocky.output.dir}/bin" target="${target}"> 
          22<src path="src"></src>
          23<classpath refid="project.classpath"></classpath>
          24</javac>
          25</target>
          26</project>
          <Jocky在Eclipse中自動生成的Ant腳本示例>

          注1:只支持Eclipse 3.1.1及以上版本。
          注2:如果在Eclipse中找不到Jocky插件,請刪除 Eclipse安裝目錄/configuration/org.eclipse.update 文件夾 (Maybe an eclipse bug?)。

          2.4 如何使用符號保留指令

             除了在命令行用 -scramble 參數控制符號混淆級別外,還可以在源代碼中使用符號保留指令來控制那些符號需要保留。符號保留指令是一個Java文檔注釋指令,可以插入在類和類成員的文檔注釋中,例如:
           1/**
           2 * This class should preserve.
           3 * @preserve
           4 */
           5public class Foo {
           6    /**
           7     * You can specify which field should be preserved.
           8     * @preserve
           9     */
          10    private int x;
          11
          12    /**
          13     * This field is not preserved.
          14     */
          15    private int y;
          16
          17    /**
          18     * You can also preserve methods.
          19     * @preserve
          20     */
          21    public void hello() {}
          22
          23    /**
          24     * This method is not preserved.
          25     */
          26    private void collect() {}
          27
          <使用preserved指令的示例>

             如果沒有@preserve指令,則根據混淆級別及成員的訪問級別來確定符號是否保留。
             對于類的符號保留指令可以附帶一個保留級別參數,來控制類成員的符號保留,包括:
          @preserve            僅對類名進行保留,類成員的保留根據-scramble命令行參數決定
          @preserve public     保留所有public成員
          @preserve protected  保留所有public和protected成員
          @preserve package    保留所有public, protected, package private成員
          @preserve private    保留所有成員
          @preserve all        相當于@preserve private
             事實上,即便不加@preserve指令,Jocky對Java語言特有的一些private級別的方法不進行混淆,譬如,在序列化時有特殊作用的writeObject及readObject方法等。但筆者強烈建議: 針對這些有特殊含義不能夠被混淆的 private級別的方法或者字段,請以@preserve指令予以保護。

          注1:建議通過IDE的JavaDoc設置,來輔助@preserve指令的書寫。

          三、Jocky的限制

             正如前文所說,Jocky是基于源代碼的混淆編譯器,因此,Jocky不支持分別編譯,必須對所有的源文件同時進行混淆編譯。但事實上,倘若混淆級別控制在private級別上,該限制便不復存在。

          posted on 2009-03-14 13:05 強強 閱讀(588) 評論(0)  編輯  收藏 所屬分類: Java

          主站蜘蛛池模板: 隆化县| 芜湖县| 新野县| 原阳县| 泰顺县| 大英县| 万全县| 密山市| 和林格尔县| 巴林左旗| 松阳县| 磐石市| 墨竹工卡县| 正阳县| 宁晋县| 察哈| 汕头市| 东阳市| 育儿| 柳江县| 义马市| 深州市| 营山县| 康保县| 三门峡市| 大埔区| 嵊州市| 桃源县| 扎赉特旗| 长沙县| 旬邑县| 延庆县| 绥棱县| 镇平县| 防城港市| 长沙县| 禹城市| 四子王旗| 江山市| 阳高县| 临沧市|