Protect your java code

我是Arren :)
    我们知道现在很多的Java反编译器,使得所有的java class文件很容易就被编译成源代码,即使使用了混淆器,但是治标不治本,因为对java虚拟机呈现的总是相同的指令,而java的指令系统就注定了反编译的可行性,以及容易性。
    编译为本机指令的二进制代码的C语言程序,以及汇编程序,在反汇编的时候相对来说很难,而能够分析反汇编结果的人需要更高的技能,如果要反编译,更难,因为C语言程序编译的结果已经导致所有的原始代码信息丢失(非debug编译方式),要反编译为可读的代码很困难。 当然能够直接分析 机器指令的人可以在不反编译的情况下直接读懂程序,这样的人很少,计算机领域也没有几个。
   
    所以我的保护程序是建立在此基础上。

(1)
正常开发我们的java程序,完成测试任务后, 摘除核心部分,或者要保护的部分,或者由于某种原因而不想公开的部分, 打包成jar文件,这个对于java开发来说是很简单的部分。 得到 kernal.jar,文件内容为:
    /kernal/core/util.class
    /kernal/core/main.class
    /kernal/startup.class

其中manifest文件包含了:
    Main-Class:kernal.startup
,只是了此包的可执行类为 kernal.startup.class。 这个类将在此包被加载的时候引导核心程序的执行。

(2)
加密kernal.jar。我们使用RSA算法加密保护一个AES随机密钥,而整个jar文件使用此密钥加密。
加密工具类如下:
/*
 * EncryptFile.java
 *
 * Created on May 24, 2007, 10:41 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 
*/

package  az.pkg.util;

import  java.io.DataOutputStream;
import  java.io.File;
import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.FileOutputStream;
import  java.io.IOException;
import  java.io.OutputStream;
import  java.security.Key;
import  java.security.KeyStore;
import  javax.crypto.Cipher;
import  javax.crypto.CipherOutputStream;
import  javax.crypto.KeyGenerator;
import  javax.crypto.SecretKey;

/**
 * Encrypt file as secret.
 *
 * The private key is in file "{AZ_HOME}/_KERNAL/keys/checker_rsa.jsk".
 *
 * 
@author  Arren
 
*/
public   class  EncryptFile {
    
    
/**
     * Creates a new instance of EncryptFile
     
*/
    
public  EncryptFile() {
    }
    
    
private   void  combine(Key key)  throws  FileNotFoundException, IOException, Exception{
        
// @todo set this array with the file you want to combine.      
        String file_to_enc="G:/PJ/TT/dist/TT.jar";
        
//@todo end.
        
        File src_file 
= new File(file_to_enc);
        
if(!src_file.exists() || !src_file.isFile()) return;
        
        FileOutputStream fos
=null;
        
try{  
            System.out.println(
"Encrypt file:"+src_file.getCanonicalPath());
            
            
/// cipher for encrypt content of the file.
            KeyGenerator kg = KeyGenerator.getInstance("AES");
            SecretKey seckey
=kg.generateKey();
            Cipher content_cipher
=Cipher.getInstance("AES/ECB/PKCS5Padding");
            content_cipher.init(Cipher.ENCRYPT_MODE, seckey);
            
            
/// Get the Cipher for wrap the secret-key.
            System.out.println("Key we use:" + key.getAlgorithm());
            Cipher cipher 
= Cipher.getInstance("RSA");
            cipher.init(Cipher.WRAP_MODE, key);
            System.out.println(
"Key Cipher got:"+cipher.getAlgorithm());
            
byte[] b_sec_key = cipher.wrap(seckey);            
            
            
/// Open the destination file.
            /// Where do we store the output file.
            File ofile=null;
            fos
=new FileOutputStream(ofile=new File(src_file.getParent(), src_file.getName()+"_enc"));
            System.out.println(
"Output file="+ofile.getCanonicalPath());            
                
            
/// Store the secret-key in file first.
            DataOutputStream dos=new DataOutputStream(fos);
            dos.writeInt(b_sec_key.length);
            dos.write(b_sec_key);
            dos.flush();
            
            
/// Create ciphered stream object.
            javax.crypto.CipherOutputStream cos = new CipherOutputStream(fos, content_cipher);
            System.out.println(
"Ciphered content stream got");
            
            
byte[] buffer = new byte[2048];
            
int n=0;
            n
+=combine_child_file(cos, src_file, buffer);
            cos.close();
            System.out.println(n
+" bytes written.");
        }
        
finally{
            
if(fos!=null){fos.close();}
        }        
    }
        
    
private int combine_child_file(OutputStream os, File file, byte[] buffer) throws Exception {
        FileInputStream fis 
= null
        
int buf_sz =buffer.length;
        
int n=0, r=0;
        
try{
            fis
=new FileInputStream(file);
            
while(-1!=(r = fis.read(buffer))){
                os.write(buffer, 
0, r);
                os.flush();
                n
+=r;
            }
        }
finally{
            
if(fis!=null) fis.close();
        }
        
return n;
    }
    
    
public static void main(String [] args){
        String home
=System.getenv("AZ_HOME");
        System.out.println(
"AZ_HOME:"+home);
        File _kernal 
= new File(home, "_KERNAL");        
        
        File jks 
= new File(_kernal, "keys/checker_rsa.jks");        
        FileInputStream fis 
= null;
        
try{
            KeyStore store 
= KeyStore.getInstance("JKS");
            fis 
=new FileInputStream(jks);
            store.load(fis, 
"howtofuckyou".toCharArray());
            Key key
=store.getKey("checker""howtofuckyou".toCharArray());
            
            
new EncryptFile().combine(key);
        
        }
catch(Exception e){
            
if(fis!=null){
                
try {fis.close();
                } 
catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            System.out.println(
"failed to combine files:"+e.getMessage());
        }
    }    
}

加密的结果是一个文件,因为使用了RSA算法保护密钥,因此即使拿到此文件也无法获取其中的内容。 将此文件命名为 resource, 并和java程序的其他部分一起打包, 形成testproject.jar, 内容如下:
    /test/pj/aaa.class
    /test/pj/bbb.class
    /resource
   
现在kernal.jar成了整体程序的一个资源文件,且是加密的。
其中比如 test.aaa.class调用了: org.az.io.LibLoader.load();
启动核心部分。

(3)
加密包加载程序。
是一个Java程序,一个类, 如下:
/*
 * FuckYou.java
 *
 * Created on May 23, 2007, 9:59 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 
*/

package  az.jni;

import  java.io.ByteArrayInputStream;
import  java.io.DataInputStream;
import  java.io.File;
import  java.io.FileOutputStream;
import  java.io.IOException;
import  java.io.InputStream;
import  java.lang.reflect.Method;
import  java.security.InvalidKeyException;
import  java.security.Key;
import  java.security.NoSuchAlgorithmException;
import  java.security.cert.Certificate;
import  java.security.cert.CertificateException;
import  java.security.cert.CertificateFactory;
import  java.util.jar.Attributes;
import  java.util.jar.JarEntry;
import  java.util.jar.JarInputStream;
import  java.util.jar.Manifest;
import  javax.crypto.Cipher;
import  javax.crypto.CipherInputStream;
import  javax.crypto.NoSuchPaddingException;


/**
 * This class is wrapped in "commons-util".
 * for more detailed information please 
@see  "org.az.io.LibLoader".
 *
 * This file is extract files been encrypted by "az.pkg.util.EncryptFile".
 *
 * 
@author  Arren
 
*/
public   class  FuckYou  extends  ClassLoader {
    
    
/**
     * Creates a new instance of FuckYou
     
*/
    
public  FuckYou() {
        
super (FuckYou. class .getClassLoader());
        System.out.println(
" [FuckYou] when you read this, i'm begin to fuck you :) " );
        
try {
            init();            
        }
catch (Exception e){
            System.out.println(
" [FuckYou] i failed to fuck you : " + e.getMessage());
        }
finally {
            Runtime.getRuntime().gc();
        }
    }
    
    
private   void  init()  throws  IOException, InvalidKeyException, CertificateException, NoSuchAlgorithmException, NoSuchPaddingException, Exception{
        
        
// / Get the encrypted stream.
        InputStream in  =  getClass().getResourceAsStream( " /resource " );
        
if (in != null ){
            System.out.println(
" [FuckYou] resource got :  " + in);
        }
else {
            System.out.println(
" [FuckYou] no resource. finished. " );
            
return ;
        }
        
        
// / Load certificate to encrypt the resource.
         // / this byte array is the stream of the certificate.
         byte [] _file_content = {
            (
byte ) 0x30 ,( byte ) 0x82 ,( byte ) 0x1 ,( byte ) 0xe5 ,( byte ) 0x30 ,( byte ) 0x82 ,( byte ) 0x1 ,( byte ) 0x4e ,( byte ) 0xa0 ,( byte ) 0x3 ,( byte ) 0x2 ,( byte ) 0x1 ,( byte ) 0x2 ,( byte ) 0x2 ,( byte ) 0x4 ,( byte ) 0x46 ,( byte ) 0x55 ,( byte ) 0xa ,( byte ) 0x27 ,( byte ) 0x30 ,( byte ) 0xd ,( byte ) 0x6 ,( byte ) 0x9 ,( byte ) 0x2a ,( byte ) 0x86 ,( byte ) 0x48 ,( byte ) 0x86 ,( byte ) 0xf7 ,( byte ) 0xd ,( byte ) 0x1 ,( byte ) 0x1 ,( byte ) 0x5 ,( byte ) 0x5 ,( byte ) 0x0 ,( byte ) 0x30 ,( byte ) 0x36 ,( byte ) 0x31 ,( byte ) 0xb ,( byte ) 0x30 ,( byte ) 0x9 ,( byte ) 0x6 ,( byte ) 0x3 ,( byte ) 0x55 ,( byte ) 0x4 ,( byte ) 0x6 ,( byte ) 0x13 ,( byte ) 0x2 ,( byte ) 0x43 ,( byte ) 0x4e ,( byte ) 0x31 ,( byte ) 0xb ,( byte ) 0x30 ,( byte ) 0x9 ,( byte ) 0x6 ,( byte ) 0x3 ,( byte ) 0x55 ,( byte ) 0x4 ,( byte ) 0xa ,( byte ) 0x13 ,( byte ) 0x2 ,( byte ) 0x61 ,( byte ) 0x7a ,( byte ) 0x31 ,( byte ) 0x1a ,( byte ) 0x30 ,( byte ) 0x18 ,( byte ) 0x6 ,( byte ) 0x3 ,( byte ) 0x55 ,( byte ) 0x4 ,( byte ) 0x3 ,( byte ) 0x13 ,( byte ) 0x11 ,( byte ) 0x61 ,( byte ) 0x7a ,( byte ) 0x2e ,( byte ) 0x64 ,( byte ) 0x6f ,( byte ) 0x63 ,( byte ) 0x6d ,( byte ) 0x61 ,( byte ) 0x6e ,( byte ) 0x2e ,( byte ) 0x63 ,( byte ) 0x68 ,( byte ) 0x65 ,( byte ) 0x63 ,( byte ) 0x6b ,( byte ) 0x65 ,( byte ) 0x72 ,( byte ) 0x30 ,( byte ) 0x20 ,( byte ) 0x17 ,( byte ) 0xd ,( byte ) 0x30 ,( byte ) 0x37 ,( byte ) 0x30 ,( byte ) 0x35 ,( byte ) 0x32 ,( byte ) 0x34 ,( byte ) 0x30 ,( byte ) 0x33 ,( byte ) 0x34 ,( byte ) 0x34 ,( byte ) 0x33 ,( byte ) 0x39 ,( byte ) 0x5a ,( byte ) 0x18 ,( byte ) 0xf ,( byte ) 0x32 ,( byte ) 0x31 ,( byte ) 0x30 ,( byte ) 0x37 ,( byte ) 0x30 ,( byte ) 0x34 ,( byte ) 0x33 ,( byte ) 0x30 ,( byte ) 0x30 ,( byte ) 0x33 ,( byte ) 0x34 ,( byte ) 0x34 ,( byte ) 0x33 ,( byte ) 0x39 ,( byte ) 0x5a ,( byte ) 0x30 ,( byte ) 0x36 ,( byte ) 0x31 ,( byte ) 0xb ,( byte ) 0x30 ,( byte ) 0x9 ,( byte ) 0x6 ,( byte ) 0x3 ,( byte ) 0x55 ,( byte ) 0x4 ,( byte ) 0x6 ,( byte ) 0x13 ,( byte ) 0x2 ,( byte ) 0x43 ,( byte ) 0x4e ,( byte ) 0x31 ,( byte ) 0xb ,( byte ) 0x30 ,( byte ) 0x9 ,( byte ) 0x6 ,( byte ) 0x3 ,( byte ) 0x55 ,( byte ) 0x4 ,( byte ) 0xa ,( byte ) 0x13 ,( byte ) 0x2 ,( byte ) 0x61 ,( byte ) 0x7a ,( byte ) 0x31 ,( byte ) 0x1a ,( byte ) 0x30 ,( byte ) 0x18 ,( byte ) 0x6 ,( byte ) 0x3 ,( byte ) 0x55 ,( byte ) 0x4 ,( byte ) 0x3 ,( byte ) 0x13 ,( byte ) 0x11 ,( byte ) 0x61 ,( byte ) 0x7a ,( byte ) 0x2e ,( byte ) 0x64 ,( byte ) 0x6f ,( byte ) 0x63 ,( byte ) 0x6d ,( byte ) 0x61 ,( byte ) 0x6e ,( byte ) 0x2e ,( byte ) 0x63 ,( byte ) 0x68 ,( byte ) 0x65 ,( byte ) 0x63 ,( byte ) 0x6b ,( byte ) 0x65 ,( byte ) 0x72 ,( byte ) 0x30 ,( byte ) 0x81 ,( byte ) 0x9f ,( byte ) 0x30 ,( byte ) 0xd ,( byte ) 0x6 ,( byte ) 0x9 ,( byte ) 0x2a ,( byte ) 0x86 ,( byte ) 0x48 ,( byte ) 0x86 ,( byte ) 0xf7 ,( byte ) 0xd ,( byte ) 0x1 ,( byte ) 0x1 ,( byte ) 0x1 ,( byte ) 0x5 ,( byte ) 0x0 ,( byte ) 0x3 ,( byte ) 0x81 ,( byte ) 0x8d ,( byte ) 0x0 ,( byte ) 0x30 ,( byte ) 0x81 ,( byte ) 0x89 ,( byte ) 0x2 ,( byte ) 0x81 ,( byte ) 0x81 ,( byte ) 0x0 ,( byte ) 0xa0 ,( byte ) 0x67 ,( byte ) 0x10 ,( byte ) 0x49 ,( byte ) 0x8c ,( byte ) 0x1f ,( byte ) 0x6b ,( byte ) 0xea ,( byte ) 0xb6 ,( byte ) 0x4c ,( byte ) 0xcd ,( byte ) 0xbd ,( byte ) 0xba ,( byte ) 0x8e ,( byte ) 0x29 ,( byte ) 0xbd ,( byte ) 0x65 ,( byte ) 0x3e ,( byte ) 0x15 ,( byte ) 0x3b ,( byte ) 0x8c ,( byte ) 0x38 ,( byte ) 0xd2 ,( byte ) 0x72 ,( byte ) 0x8a ,( byte ) 0x7c ,( byte ) 0x61 ,( byte ) 0x9e ,( byte ) 0x53 ,( byte ) 0x74 ,( byte ) 0xe7 ,( byte ) 0xab ,( byte ) 0x37 ,( byte ) 0xff ,( byte ) 0x8e ,( byte ) 0x42 ,( byte ) 0x78 ,( byte ) 0x85 ,( byte ) 0xd1 ,( byte ) 0x23 ,( byte ) 0x9f ,( byte ) 0xc0 ,( byte ) 0xef ,( byte ) 0xc4 ,( byte ) 0xc6 ,( byte ) 0xa0 ,( byte ) 0x7c ,( byte ) 0xb9 ,( byte ) 0x68 ,( byte ) 0xc2 ,( byte ) 0xa8 ,( byte ) 0x5a ,( byte ) 0x3c ,( byte ) 0xff ,( byte ) 0x5c ,( byte ) 0x4d ,( byte ) 0xf ,( byte ) 0xaa ,( byte ) 0x57 ,( byte ) 0x16 ,( byte ) 0x6c ,( byte ) 0x3c ,( byte ) 0x50 ,( byte ) 0xc3 ,( byte ) 0x45 ,( byte ) 0xc9 ,( byte ) 0x21 ,( byte ) 0x7d ,( byte ) 0x12 ,( byte ) 0x81 ,( byte ) 0x60 ,( byte ) 0x91 ,( byte ) 0x36 ,( byte ) 0xa0 ,( byte ) 0xd ,( byte ) 0x8c ,( byte ) 0xff ,( byte ) 0xb4 ,( byte ) 0xf4 ,( byte ) 0xc4 ,( byte ) 0xf3 ,( byte ) 0x9d ,( byte ) 0x9b ,( byte ) 0xe7 ,( byte ) 0xef ,( byte ) 0xd5 ,( byte ) 0x9c ,( byte ) 0x60 ,( byte ) 0xad ,( byte ) 0x40 ,( byte ) 0x4b ,( byte ) 0xc0 ,( byte ) 0x8b ,( byte ) 0x4b ,( byte ) 0xc9 ,( byte ) 0x5e ,( byte ) 0x24 ,( byte ) 0xac ,( byte ) 0xbe ,( byte ) 0x54 ,( byte ) 0x2e ,( byte ) 0x2f ,( byte ) 0x3a ,( byte ) 0x68 ,( byte ) 0xa3 ,( byte ) 0xf1 ,( byte ) 0x6 ,( byte ) 0xf6 ,( byte ) 0x4b ,( byte ) 0x90 ,( byte ) 0x9c ,( byte ) 0x1b ,( byte ) 0xff ,( byte ) 0xcc ,( byte ) 0x71 ,( byte ) 0x90 ,( byte ) 0x88 ,( byte ) 0x6 ,( byte ) 0x37 ,( byte ) 0x4e ,( byte ) 0x9b ,( byte ) 0x26 ,( byte ) 0xf0 ,( byte ) 0xd3 ,( byte ) 0xb ,( byte ) 0x7d ,( byte ) 0xd9 ,( byte ) 0x93 ,( byte ) 0x2 ,( byte ) 0x3 ,( byte ) 0x1 ,( byte ) 0x0 ,( byte ) 0x1 ,( byte ) 0x30 ,( byte ) 0xd ,( byte ) 0x6 ,( byte ) 0x9 ,( byte ) 0x2a ,( byte ) 0x86 ,( byte ) 0x48 ,( byte ) 0x86 ,( byte ) 0xf7 ,( byte ) 0xd ,( byte ) 0x1 ,( byte ) 0x1 ,( byte ) 0x5 ,( byte ) 0x5 ,( byte ) 0x0 ,( byte ) 0x3 ,( byte ) 0x81 ,( byte ) 0x81 ,( byte ) 0x0 ,( byte ) 0x60 ,( byte ) 0x26 ,( byte ) 0xf0 ,( byte ) 0x37 ,( byte ) 0xf9 ,( byte ) 0x8c ,( byte ) 0x91 ,( byte ) 0x28 ,( byte ) 0x2c ,( byte ) 0xae ,( byte ) 0x7a ,( byte ) 0x6a ,( byte ) 0x90 ,( byte ) 0xe7 ,( byte ) 0x44 ,( byte ) 0x6a ,( byte ) 0x37 ,( byte ) 0x27 ,( byte ) 0xd0 ,( byte ) 0x11 ,( byte ) 0xcc ,( byte ) 0x1c ,( byte ) 0x69 ,( byte ) 0x48 ,( byte ) 0x20 ,( byte ) 0x1f ,( byte ) 0x11 ,( byte ) 0x7b ,( byte ) 0x61 ,( byte ) 0x4a ,( byte ) 0x46 ,( byte ) 0x86 ,( byte ) 0x89 ,( byte ) 0xe4 ,( byte ) 0x73 ,( byte ) 0x42 ,( byte ) 0x85 ,( byte ) 0xc6 ,( byte ) 0xc6 ,( byte ) 0x3b ,( byte ) 0x14 ,( byte ) 0x7 ,( byte ) 0xe7 ,( byte ) 0x3c ,( byte ) 0x3c ,( byte ) 0x24 ,( byte ) 0xec ,( byte ) 0x30 ,( byte ) 0xe7 ,( byte ) 0x51 ,( byte ) 0x48 ,( byte ) 0x1a ,( byte ) 0x12 ,( byte ) 0x2a ,( byte ) 0x95 ,( byte ) 0xca ,( byte ) 0x53 ,( byte ) 0x5f ,( byte ) 0xf ,( byte ) 0xf8 ,( byte ) 0xfc ,( byte ) 0xd5 ,( byte ) 0xc9 ,( byte ) 0xfc ,( byte ) 0xe1 ,( byte ) 0xd ,( byte ) 0xee ,( byte ) 0x69 ,( byte ) 0xec ,( byte ) 0x8e ,( byte ) 0xed ,( byte ) 0x8f ,( byte ) 0xfc ,( byte ) 0x1b ,( byte ) 0x74 ,( byte ) 0xf0 ,( byte ) 0x40 ,( byte ) 0x6d ,( byte ) 0xd8 ,( byte ) 0x3e ,( byte ) 0xf0 ,( byte ) 0x88 ,( byte ) 0xa0 ,( byte ) 0xd6 ,( byte ) 0xac ,( byte ) 0x18 ,( byte ) 0x72 ,( byte ) 0xe0 ,( byte ) 0x52 ,( byte ) 0x9f ,( byte ) 0x73 ,( byte ) 0xd9 ,( byte ) 0x92 ,( byte ) 0xfa ,( byte ) 0x27 ,( byte ) 0xe2 ,( byte ) 0x31 ,( byte ) 0xc9 ,( byte ) 0xef ,( byte ) 0x4 ,( byte ) 0x4c ,( byte ) 0x4f ,( byte ) 0xb9 ,( byte ) 0x6f ,( byte ) 0xcd ,( byte ) 0x3b ,( byte ) 0x1d ,( byte ) 0x33 ,( byte ) 0x63 ,( byte ) 0x2a ,( byte ) 0x15 ,( byte ) 0xa5 ,( byte ) 0x1b ,( byte ) 0xd8 ,( byte ) 0x6 ,( byte ) 0x1e ,( byte ) 0xa ,( byte ) 0x1a ,( byte ) 0x66 ,( byte ) 0xe9 ,( byte ) 0x4 ,( byte ) 0xcf ,( byte ) 0xfd ,( byte ) 0x3f ,( byte ) 0xac ,( byte ) 0xce ,( byte ) 0xde ,( byte ) 0xbe
        };
        
        
/** We generate the certificate with its binary content,
         * we use cert's publick decrypt the file.
         *
*/
        CertificateFactory cf
= CertificateFactory.getInstance( " X.509 " );
        Certificate cert
= cf.generateCertificate( new  ByteArrayInputStream(_file_content));
        System.out.println(
" [FuckYou] certificate got. " );
        Cipher key_cipher 
=  Cipher.getInstance( " RSA " );
        key_cipher.init(Cipher.UNWRAP_MODE, cert);
        
        
// / Data reader, used to read key -size.
        DataInputStream dis  =   new  DataInputStream(in);
        
        
// / Read a integer value,
         // / this is the size of the secret-key.
         int  key_sz = dis.readInt();
        
if (key_sz < 0   ||  key_sz > 2048 throw   new  java.lang.Exception( " Illegal file: key size invalid. " );
        
byte [] key = new   byte [key_sz];
        
if (key_sz != in.read(key))  throw   new  java.lang.Exception( " Illegal file: file size too small. " );
        
        
// / now the key we got is encrypted by the RSA key,
         // / we must decrypt it to use.
        Key sec_key  =  key_cipher.unwrap(key,  " AES " , Cipher.SECRET_KEY);
        java.util.Arrays.fill(key, 
0 , key_sz, ( byte ) 0 );
        key 
=   null ;
        System.out.println(
" [FuckYou] secret key got " );
        
        
// / ok
         // / we got the secret key.
        Cipher file_cipher  =  Cipher.getInstance( " AES/ECB/PKCS5Padding " );
        file_cipher.init(Cipher.DECRYPT_MODE, sec_key);
        CipherInputStream cis 
=   new  CipherInputStream(in, file_cipher);
        System.out.println(
" [FuckYou] cipher stream got " );
        
//          // / write the file to disk,
//          // / or use it in memory.
//         FileOutputStream fos=null;
//         try{
//             fos=new FileOutputStream(new File("g:/xxx.jar"));
//             byte[] buffer = new byte[2048];
//             int r=0;
//             while(-1!=(r=cis.read(buffer))) fos.write(buffer, 0, r);
//         }finally{
//             if(fos!=null){fos.close(); fos=null;}
//         }
        System.out.println( " [FuckYou] load module from resources " );
        
try {
            load_jar_file(cis, 
true );
        }
finally {
            cis.close();
            System.out.println(
" [FuckYou] module loaded. " );
        }
    }
    
    
private   void  load_jar_file(InputStream jar_file,  boolean  load_or_write)  throws  IOException, Exception{
        JarInputStream jis 
=   new  JarInputStream(jar_file);
        String tmp
= null ;
        
final   int  MAX_SIZE  =   1024 * 128 long  size = 0 ;
        
byte  [] buffer  =   new   byte [MAX_SIZE];
        
        
// / Read each entry in the jar file,
         // / every entry should be an .class object.
        JarEntry entry = jis.getNextJarEntry();
        
while (entry != null ){
            
if ( ! entry.isDirectory()  &&  (tmp = entry.getName()).endsWith( " .class " )){
                
// / Check entry size.
                size = entry.getSize();
                
if (size < 64   ||  size > MAX_SIZE){
                    System.out.println(
" [FuckYou] module component is too large. ignored. " );
                }
                
else   if (load_or_write){
                    
int  p = tmp.indexOf( " .class " );
                    load_class(jis, tmp.substring(
0 , p).replace( ' / ' , ' . ' ), buffer, MAX_SIZE);
//                 }else{
//                     write_class(jis, tmp, buffer);
                }
            }
            entry
= jis.getNextJarEntry();
        }
        
        
if (load_or_write){
            System.out.println(
" [FuckYou] trying to bootstrap module... " );
            
// / if Main-Class defined,
             // / we create an instance of it.
            Manifest mf = jis.getManifest();
            
if (mf != null ){
                Attributes attrs
= mf.getMainAttributes();
                String mc
= attrs.getValue(Attributes.Name.MAIN_CLASS);
                
if (mc != null ){
                    System.out.println(
" [FuckYou] found entry point : " + mc);
                    
try {
                        Class cls
= Class.forName(mc,  true this );
                        System.out.println(
" [FuckYou] got class object: " + cls);
                        Method m
= cls.getDeclaredMethod(  " main " new  Class[]{String[]. class });
                        System.out.println(
" [FuckYou] found 'main' method: " + m);
                        m.invoke(
null , (Object) null );
                        System.out.println(
" [FuckYou] 'main' method invoked. " );
                    }
catch (Exception e){
                        e.printStackTrace();
                        System.out.println(
" [FuckYou] failed to bootstrap module by main(): " + e.getMessage());
                    }
                }
else {
                    System.out.println(
" [FuckYou] no entry point. no need to bootstrap. " );
                }
            }
// .if.
        }
    }
    
    
/**
     * Load a class definition from stream
     *
*/
    
private   void  load_class(InputStream cls_stream, String cls_name,  byte [] buffer,  int  bl )  throws  Exception{
        System.out.println(
" [FuckYou] find component : " + cls_name);
        
int  off = 0 , r = 0 ;
        
while ( - 1 != (r = cls_stream.read(buffer, off, bl - off))){
            off
+= r;
        }
        System.out.println(
" [FuckYou] component size:  " + String.valueOf(off));
        Class cls 
=   super .defineClass(cls_name, buffer,  0 , off);
    }
//     
//     private void write_class(InputStream cls_stream, String cls_name, byte[] buffer ) throws Exception{
//         File file = new File(cls_name);
//         File dir = file.getParentFile();
//         if(!dir.exists()) dir.mkdirs();
//         FileOutputStream fos = null;
//         try{
//             fos = new FileOutputStream(file);
//             int r=0;
//             while(-1!=(r=cls_stream.read(buffer))) fos.write(buffer, 0, r);
//         }
//         finally{
//             if(fos!=null){fos.close();}
//         }
//     }
}

此类是 核心程序的加载类,其中包含了解密/resource用的RSA公钥,即字节流 byte [] _file_content 。关于把任意文件转换成 字节数组定义形式,我提供了工具类。

(4)
保护Fuckyou.class文件。我们对此文件进行加密以及编码。打开文件流为 fis, 加密输出为 writer:
private   static   void  encrypt(InputStream fis,  int  l, Writer writer)  throws  Exception{
        writer.write(
" byte[] _file_content={ " );
        
int  i = 0 , a = 0 , code = 0 ;
        
if ( - 1 != (a = fis.read())){
            code 
=  a  ^   0x11 ;
            writer.write(
" (byte)0x " ); writer.write(Integer.toHexString(code));            
            
while ( - 1 != (a = fis.read())){
                code 
=  a  ^   0x11 ;
                writer.write(
" ,(byte)0x " ); writer.write(Integer.toHexString(code)); 
            }
        }
        writer.write(
" }; " );
    }
输出为加密并编码的 byte数组定义文件。 注意到,我们使用了简单的字节异或操作来加密。这样子比较简单。方法类似可替换。

(5)
至此,核心程序kernal.jar已经加密,而加密包引导程序也已经准备好。注意:(2),(3),(4)步骤中的类都是我们加密工具的一部分,因此普通用户只需要完成步骤(1)的任务即可,所以程序是通用的。

(6)
引导加密程序,我们需要使用C语言代码,调用JNI。
/*
 * LibLoader.java
 *
 * Created on May 24, 2007, 9:42 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 
*/

package  org.az.io;

import  java.io.File;
import  java.io.FileOutputStream;
import  java.io.IOException;
import  java.io.InputStream;

/**
 *
 * 
@author  Arren
 
*/
public   class  LibLoader {
    
    
/** * Cache the specified stream to a file on disk,
     *   later Java system could load it as library.
     **
*/
    
private   static  String keep_file(String to, InputStream from){
        FileOutputStream fos
= null ;
        
try {
            File file
= new  File(System.getProperty( " java.io.tmpdir " ),to);
            fos
= new  FileOutputStream(file);
            
int  a = 0 ;
            
while ( - 1 != (a = from.read())) fos.write(a);
            fos.close(); fos
= null ;
            
            String path 
=  file.getCanonicalPath();
            
return  path;
        }
catch (Exception e){
            
return  to;
        }
finally {
            
if (fos != null ){
                
try  {
                    fos.close();
                } 
catch  (IOException ex) {
                    System.out.println(
" [commons-util] failed to close the lib-file. " );
                }
            }
        }
// finally.
    }    
    
    
static {
        String path 
=   null ;
        
try {
            InputStream is
= LibLoader. class .getResourceAsStream( " /libloader.dll " );
            path 
=  keep_file( " libldr.dll " , is);
            is.close();
            
// / Load the library.
            System.load(path);
        }
catch (Exception e){
            System.out.println(
" [commons-util] failed to load system library |  "   +  e.getMessage());
        }
    }
// .static.
    
    
    
    
private   static  LibLoader _instance  =   new  LibLoader();
    
/**
     * Creates a new instance of LibLoader
     
*/
    
private  LibLoader() {
    }
    
    
public   synchronized   static   void  load(){
        
try {
            System.out.println(
" [commons-util] spawn libs... " );
            _instance.spawn( _instance.getClass().getClassLoader());
        }
catch (Exception e){
            System.out.println(
" [commons-util] failed to spawn more libs |  " + e.getMessage());
        }
finally {
            System.out.println(
" [commons-util] complete. " );
        }
    }
    
    
private   native   void  spawn(Object loader);
}

注意其中的 private   native   void  spawn(Object loader) 方法。
创建C语言动态库项目,使用如下程序:libloader.c,
#include  " LibLoader.h "

// @author : arren zhang.

const char * CLASS_NAME="az/jni/FuckYou";
typedef char byte;

// The encrypted "az.jni.Fuckyou" class.
byte _file_content[]={
    (byte)0xa3,(byte)0x97,(byte)0xd3, ...
};

/* *
 Define a class in JVM space.
 This class will work for us.
 
 @param loader The ClassLoader object used to load this new defined class.
 @return The new defined Class.
 *
*/
jclass defineMyClass(JNIEnv 
*  env, jobject loader){
    
char   *  buf = _file_content;
    
int  sz = sizeof (_file_content), i = 0 ;
    
for (i = 0 ;i < sz;i ++ ){
        buf[i]
= buf[i] ^ 0x11 ;
    }
    jclass cls
= ( * env) -> DefineClass(env, CLASS_NAME, loader, buf, sz);
    
return  cls;
}


JNIEXPORT 
void  JNICALL Java_org_apache_commons_io_LibLoader_spawn
  (JNIEnv 
*  env, jobject obj, jobject cls_loader)
{   
    
///  Load the class, and create new instance of it.
    jclass cls  =  defineMyClass(env, cls_loader);
    jmethodID m_init
= ( * env) -> GetMethodID(env, cls,  " <init> " " ()V " );
    
if (m_init){
        jobject new_obj
=  ( * env) -> NewObject(env, cls, m_init);
    }
    
    
///  Exception check
    jthrowable err = ( * env) -> ExceptionOccurred(env);
    
if (err){
        jint r
= ( * env) -> Throw(env, err);
        printf(
" commons-util: exception rethrowed:%d " , r);
    }
    
return ;
}
编译结果为libloader.dll.  我们把此文件和 LibLoader .class一起打包成libloader.jar。这个文件就是提供给最终程序的。
至此,保护程序的唯一入口就是 libloader.dll文件,也是保护程序的弱点,但是所有的安全程序都有一个弱点,即使全部使用 RSA加密,也存在一个问题,你如何存放RSA私钥,如果你加密此私钥,那么你加密此私钥的密钥又放在何处? 所以最后总是有一个弱点。
当然可以用硬件之类的保护此弱点,但我在此介绍方法而已。当然攻破C程序难度不小,相对安全。

LibLoader.class 和libloader.dll一起打包后得到, libloader.jar.

(7)最后发布程序
至此我们得到的文件包括:
testproject.jar, 其中包含了加密过的kernal.jar。
libloader.jar,加密程序,可以通用。

这就是所有了,保护程序其实是和程序主体部分分离的,只需要在发布之前进行保护。当然如果你已经公布了核心部分的代码,那保护就没有必要了:)

猜你喜欢

转载自blog.csdn.net/arrenzhang/article/details/1626687