国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

java jar包加密保護解決方案

2019-11-06 08:01:57
字體:
來源:轉載
供稿:網友

為什么需要保護?

我用java寫了一個程序如下:

12345678910111213141516171819202122232425
package com.monkey.demo;// App.javapublic class App{  static public void main( String args[] ) throws Exception {    System.out.PRintln( "This is your application." );    System.out.print( "Args: " );    for (int a=0; a<args.length; ++a)      System.out.print( args[a]+" " );    System.out.println( "" );        new App().new AppChild().print();        new Foo();    new Bar();  }    public class AppChild{	  public void print(){		  System.out.println("haha ....");	  }  }}

然后編譯生成.class文件后,我發布出去了,別人拿到我的.class文件拖到JD-GUI里面看到的是這樣:

image

玩毛線,看源代碼一樣,當然你也可以使用ProGuard混淆,不過別人有點耐心還是能分析出來的。另外你還可以修改class文件里面的某些字段,這些字段對運行沒有影響,但是能導致別人無法反編譯。這里我們暫且不討論這種方式,分別討論下使用ClassLoaderjvmti對class文件加密解密的方案。

ClassLoader

Java運行時裝入字節碼的機制隱含地意味著可以對字節碼進行修改。JVM每次裝入類文件時都需要一個稱為ClassLoader的對象,這個對象負責把新的類裝入正在運行的JVM。JVM給ClassLoader一個包含了待裝入類(比如java.lang.Object)名字的字符串,然后由ClassLoader負責找到類文件,裝入原始數據,并把它轉換成一個Class對象。

所以我們可以通過自定義一個ClassLoader,然后先對class進行解密之后,再加載到JVM。大致流程如下:

1234567
// 首先創建一個ClassLoader對象ClassLoader myClassLoader = new myClassLoader();// 利用定制ClassLoader對象裝入類文件// 并把它轉換成Class對象Class myClass = myClassLoader.loadClass( "mypackage.MyClass" );// 最后,創建該類的一個實例Object newInstance = myClass.newInstance();

loadClass

在創建自定義的ClassLoader時,只需覆蓋其中的一個,即loadClass,獲取加密后的文件數據解密加載。

1234567891011121314151617181920212223242526272829
public Class loadClass( String name, boolean resolve )      throws ClassNotFoundException {	try {	  // 我們要創建的Class對象	   Class clasz = null;	  // 如果類已經在系統緩沖之中,不必再次裝入它	  clasz = findLoadedClass( name );	  if (clasz != null)	    return clasz;	  // 下面是定制部分	  byte classData[] = /* 解密加密后的字節數據 */;	  if (classData != null) {	    // 成功讀取字節碼數據,現在把它轉換成一個Class對象	    clasz = defineClass( name, classData, 0, classData.length );	  }	  // 如果上面沒有成功,嘗試用默認的ClassLoader裝入它	  if (clasz == null)	    clasz = findSystemClass( name );	  //如有必要,則裝入相關的類	  if (resolve && clasz != null)	    resolveClass( clasz );	  // 把類返回給調用者	  return clasz;	} catch( IOException ie ) {	  throw new ClassNotFoundException( ie.toString() );	} catch( GeneralSecurityException gse ) {	  throw new ClassNotFoundException( gse.toString() );	}}

上面是一個簡單的loadClass實現,其中涉及到如下幾個方法:

findLoadedClass: 檢查當前要加載的類是否已經加載。defineClass: 獲得原始類文件字節碼數據后,調用defineClass轉換成一個Class對象。findSystemClass: 提供默認ClassLoader支持。resolveClass: 當JVM想要裝入的不僅包括指定的類,而且還包括該類引用的所有其他類時,它會把loadClass的resolve參數設置成true。這時,必須在返回剛剛裝入的Class對象給調用者之前調用resolveClass。

加密、解密

直接使用java自帶加密算法,比如DES。

生成密鑰

123456789
//DES算法要求有一個可信任的隨機數源SecureRandom sr = new SecureRandom();//為選擇的DES算法生成一個KeyGenerator對象KeyGenerator kg = KeyGenerator.getInstance( "DES" );kg.init( sr );// 生成密匙SecretKey key = kg.generateKey();// 獲取密匙數據byte rawKeyData[] = key.getEncoded();

加密數據

123456789101112131415161718
// DES算法要求有一個可信任的隨機數源SecureRandom sr = new SecureRandom();byte rawKeyData[] = /* 用某種方法獲得密匙數據 */;// 從原始密匙數據創建DESKeySpec對象DESKeySpec dks = new DESKeySpec( rawKeyData );// 創建一個密匙工廠,然后用它把DESKeySpec轉換成一個SecretKey對象SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );SecretKey key = keyFactory.generateSecret( dks );// Cipher對象實際完成加密操作Cipher cipher = Cipher.getInstance( "DES" );// 用密匙初始化Cipher對象cipher.init( Cipher.ENCRYPT_MODE, key, sr );// 現在,獲取數據并加密byte data[] = /* 用某種方法獲取數據 */// 正式執行加密操作byte encryptedData[] = cipher.doFinal( data );// 進一步處理加密后的數據doSomething( encryptedData );

解密數據

12345678910111213141516171819
// DES算法要求有一個可信任的隨機數源SecureRandom sr = new SecureRandom();byte rawKeyData[] = /* 用某種方法獲取原始密匙數據 */;// 從原始密匙數據創建一個DESKeySpec對象DESKeySpec dks = new DESKeySpec( rawKeyData );// 創建一個密匙工廠,然后用它把DESKeySpec對象轉換成// 一個SecretKey對象SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );SecretKey key = keyFactory.generateSecret( dks );// Cipher對象實際完成解密操作Cipher cipher = Cipher.getInstance( "DES" );// 用密匙初始化Cipher對象cipher.init( Cipher.DECRYPT_MODE, key, sr );// 現在,獲取數據并解密byte encryptedData[] = /* 獲得經過加密的數據 */// 正式執行解密操作byte decryptedData[] = cipher.doFinal( encryptedData );// 進一步處理解密后的數據doSomething( decryptedData );

實際案例

這里寫了一個簡單的例子,代碼在github。

首先生成密鑰:

123
javac FileUtil.java javac GenerateKey.javajava GenerateKey key.data

然后加密class:

12
javac EncryptClasses.javajava EncryptClasses key.data App.class Foo.class Bar.class

運行加密后的應用:

12
javac MyClassLoader.java -Xlint:unchecked java MyClassLoader key.data App

總的來說ClassLoader在類非常多的情況還是比較麻煩,而且這樣一來自定義的ClassLoader本身就成為了突破口。下面介紹另外一種加密保護的方案。

jvmti

jvmti(JVMTM Tool Interface)是JDK提供的一套用于開發JVM監控,問題定位與性能調優工具的通用變成接口。通過JVMTI,我們可以開發各式各樣的JVMTI Agent。這個Agent的表現形式是一個以c/c++語言編寫的動態共享庫。

JVMTI Agent原理: java啟動或運行時,動態加載一個外部基于JVM TI編寫的dynamic module到Java進程內,然后觸發JVM源生線程Attach Listener來執行這個dynamic module的回調函數。在函數體內,你可以獲取各種各樣的VM級信息,注冊感興趣的VM事件,甚至控制VM的行為。

這里我們只需要監控class的加載信息,而jvmti也提供了這樣的接口,通過下面的方式我們就能監控到class的加載:

123456789101112131415161718192021222324252627282930313233
JNIEXPORT jint JNICALLAgent_OnLoad(    JavaVM *vm,    char *options,    void *reserved){	......	//設置事件回調    jvmtiEventCallbacks callbacks;    (void)memset(&callbacks,0, sizeof(callbacks));     callbacks.ClassFileLoadHook = &MyClassFileLoadHook;    error = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));    ......}void JNICALLMyClassFileLoadHook(    jvmtiEnv *jvmti_env,    JNIEnv* jni_env,    jclass class_being_redefined,    jobject loader,    const char* name,   //class名字    jobject protection_domain,    jint class_data_len,  //class文件數據長度    const unsigned char* class_data,   //class文件數據    jint* new_class_data_len,   //新的class文件數據長度    unsigned char** new_class_data   //新的class文件數據){	......}

通過這樣的方式就能監控到class的加載然后再對其進行解密。

加密、解密

加密class文件

這里簡單的通過遍歷class文件,然后對每個字節進行一個異或處理,具體的加密方法可以自己擴展:

12345678910111213141516171819
extern"C" JNIEXPORT jbyteArray JNICALL Java_Encrypt_encrypt(    JNIEnv * _env,     jobject _obj,    jbyteArray _buf){    jsize len =_env->GetArrayLength(_buf);       unsigned char* dst = (unsigned char*)_env->GetByteArrayElements(_buf, 0); 	 	for (int i = 0; i < len; ++i) 	{ 		dst[i] = dst[i] ^ 0x07; 	}     _env->SetByteArrayRegion(_buf, 0, len, (jbyte *)dst);    return _buf;}

解密class文件

在運行jar文件的時候,加載我們的jvmti agent動態庫進行動態解密:

12345678910111213141516171819202122232425262728293031
void JNICALLMyClassFileLoadHook(    jvmtiEnv *jvmti_env,    JNIEnv* jni_env,    jclass class_being_redefined,    jobject loader,    const char* name,    jobject protection_domain,    jint class_data_len,    const unsigned char* class_data,    jint* new_class_data_len,    unsigned char** new_class_data){    *new_class_data_len = class_data_len;    jvmti_env->Allocate(class_data_len, new_class_data);     unsigned char* my_data = *new_class_data;    if(name&&strncmp(name,"com/monkey/",11)==0){        for (int i = 0; i < class_data_len; ++i)        {            my_data[i] = class_data[i] ^ 0x07;        }    }else{        for (int i = 0; i < class_data_len; ++i)        {            my_data[i] = class_data[i];        }    }}

實際案例

這里寫了一個簡單的例子,代碼在github。

首先加密jar包:

12
javac Encrypt.javajava -Djava.library.path=. -cp . Encrypt -src jardemo.jar

image

然后會得到一個jardemo_encrypt.jar文件,如果現在直接去運行該文件的話肯定是會出錯的,所以要做解密。

先編譯生成一個解密的動態庫libdecrypt.dylib。然后運行:

1
java -jar -agentlib:decrypt jardemo_encrypt.jar

image

總結

總的來說,使用jvmti提供的監控api,方便了我們直接對class的操作,所以第二個方案更好一些,當然其中具體使用怎么樣的加密,以及如何去保證加密不被破解就需要各位發揮自己的空間了。

原文地址:http://www.alonemonkey.com/2016/05/25/encrypt-jar-class/

參考:http://blog.csdn.net/yczz/article/details/39034223

https://www.ibm.com/developerworks/cn/java/l-secureclass/


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 丰顺县| 横峰县| 松江区| 宁夏| 阜城县| 鄂托克前旗| 黄梅县| 长葛市| 虹口区| 华容县| 利津县| 黑龙江省| 金湖县| 扬州市| 宁乡县| 电白县| 赤城县| 赤峰市| 夏河县| 三门县| 沙洋县| 邯郸市| 当雄县| 锦屏县| 桂阳县| 石门县| 菏泽市| 白朗县| 灵武市| 甘孜| 陵川县| 隆回县| 铁岭县| 卫辉市| 陵川县| 长春市| 昆山市| 凌海市| 尤溪县| 盐边县| 永新县|