熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

深入Java字節碼加密

2022-06-13   來源: Java核心技術 

  
  如果我把我的class文件加密在運行時用指定的類加載器(class loader)裝入並解密它這樣子能防止被反編譯嗎?
  
  
  防止JAVA字節碼反編譯這個問題在java語言雛形期就有了盡管市面上存在一些反編譯的工具可以利用但是JAVA程序員還是不斷的努力尋找新的更有效的方法來保護他們的智慧結晶在此我將詳細給大家解釋這一直來在論壇上有爭議的話題
  
  Class文件能被很輕松的重構生成JAVA源文件與最初JAVA字節碼的設計目的和商業交易有緊密地聯系另外JAVA字節碼被設計成簡潔平台獨立性網絡靈活性並且易於被字節碼解釋器和JIT (justintime)/HotSpot 編譯器所分析可以清楚地了解程序員的目的 Class文件要比JAVA源文件更易於分析
  
  如果不能阻止被反編譯的話至少可以通過一些方法來增加它的困難性例如: 在一個分步編譯裡你可以打亂Class文件的數據以使其難讀或者難以被反編譯成正確的JAVA源文件前者可以采用極端函數重載後者用操作控制流建立控制結構使其難以恢復正常次序有更多成功的商業困惑者采用這些或其他的技術來保護自己的代碼
  
  不幸的是哪種方法都必須改變JVM運行的代碼並且許多用戶害怕這種轉化會給他們的程序帶來新的Bug而且方法和字段重命名會調用反射從而使程序停止工作改變類和包的名字會破壞其他的JAVA APIS(JNDI URL providers etc)除了改變名字如果字節碼偏移量和源代碼行數之間的關系改變了在恢復這有異常的堆棧將很困難
  
  於是就有了一些打亂JAVA源代碼的選項但是這將從本質上導致一系列問題的產生
  
  加密而不打亂
  或許上述可能會使你問假如我把字節碼加密而不是處理字節碼並且JVM運行時自動將它解密並裝入類加載器然後JVM運行解密後的字節碼文件這樣就不會被反編譯了對嗎?
  
  考慮到你是第一個提出這種想法的並且它又能正常運行我表示遺憾和不幸這種想法是錯誤的
  
  下面是一個簡單的類編碼器
  
  為了闡明這種思想我采用了一個實例和一個很通用的類加載器來運行它該程序包括兩個類
  
   public class Main
  {
    public static void main (final String [] args)
    { 
      Systemoutprintln (secret result = + MySecretClassmySecretAlgorithm ());
    }
  
  } // End of class
  
  
  package de;
  
  import javautilRandom;
  
  public class MySecretClass
  {
    /**
     * Guess what the secret algorithm just uses a random number generator
     */
    public static int mySecretAlgorithm ()
    {
      return (int) s_randomnextInt ();
    }
  
    private static final Random s_random = new Random (SystemcurrentTimeMillis ());
  
  } // End of class
  我想通過加密相關的class文件並在運行期解密來隱藏deMySecretClass的執行用下面這個工具可以達到效果(你可以到這裡下載Resources)
  
  public class EncryptedClassLoader extends URLClassLoader
  {  
    public static void main (final String [] args)
      throws Exception
    {    
      if (runequals (args []) && (argslength >= ))
      {
        // Create a custom loader that will use the current loader as
        // delegation parent:
        final ClassLoader appLoader =
          new EncryptedClassLoader (EncryptedClassLoaderclassgetClassLoader ()
          new File (args []));
        
        // Thread context loader must be adjusted as well:
        ThreadcurrentThread ()setContextClassLoader (appLoader);
        
        final Class app = appLoaderloadClass (args []);
        
        final Method appmain = appgetMethod (main new Class [] {String []class});
        final String [] appargs = new String [argslength ];
        Systemarraycopy (args appargs appargslength);
        
        appmaininvoke (null new Object [] {appargs});
      }
      else if (encryptequals (args []) && (argslength >= ))
      {
         encrypt specified classes
      }
      else
        throw new IllegalArgumentException (USAGE);    
    }
    
    /**
     * Overrides javalangClassLoaderloadClass() to change the usual parentchild
     * delegation rules just enough to be able to snatch application classes
     * from under system classloaders nose
     */
    public Class loadClass (final String name final boolean resolve)
      throws ClassNotFoundException
    {
      if (TRACE) Systemoutprintln (loadClass ( + name + + resolve + ));
      
      Class c = null;
      
      // First check if this class has already been defined by this classloader
      // instance:
      c = findLoadedClass (name);
      
      if (c == null)
      {
        Class parentsVersion = null;
        try
        {
          // This is slightly unorthodox: do a trial load via the
          // parent loader and note whether the parent delegated or not;
          // what this accomplishes is proper delegation for all core
          // and extension classes without my having to filter on class name:
          parentsVersion = getParent ()loadClass (name);
          
          if (parentsVersiongetClassLoader () != getParent ())
            c = parentsVersion;
        }
        catch (ClassNotFoundException ignore) {}
        catch (ClassFormatError ignore) {}
        
        if (c == null)
        {
          try
          {
            // OK either c was loaded by the system (not the bootstrap
            // or extension) loader (in which case I want to ignore that
            // definition) or the parent failed altogether; either way I
            // attempt to define my own version:
            c = findClass (name);
          }
          catch (ClassNotFoundException ignore)
          {
            // If that failed fall back on the parents version
            // [which could be null at this point]:
            c = parentsVersion;
          }
        }
      }
      
      if (c == null)
        throw new ClassNotFoundException (name);
      
      if (resolve)
        resolveClass (c);
      
      return c;
    }
      
    /**
     * Overrides javanewURLClassLoaderdefineClass() to be able to call
     * crypt() before defining a class
     */
    protected Class findClass (final String name)
      throws ClassNotFoundException
    {
      if (TRACE) Systemoutprintln (findClass ( + name + ));
      
      // class files are not guaranteed to be loadable as resources;
      // but if Suns code does it so perhaps can mine
      final String classResource = namereplace ( /) + class;
      final URL classURL = getResource (classResource);
      
      if (classURL == null)
        throw new ClassNotFoundException (name);
      else
      {
        InputStream in = null;
        try
        {
          in = classURLopenStream ();
  
          final byte [] classBytes = readFully (in);
          

From:http://tw.wingwit.com/Article/program/Java/hx/201311/25555.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.