大家知道java是以虛擬機的方式運行以.class結尾的字節碼文件,虛擬機要解析class文件,必然有一套解析方案,相對應的就可以通過字節碼還原java源代碼,flash的as也是一樣,辛辛苦苦寫完的代碼,人家用一個工具反編譯,一切就都沒有秘密了...悲劇啊!
最好的解決方案莫過於直接改虛擬機,在這里不考慮通用性,公司的服務器只能用改過的虛擬機是沒有問題的,無奈通過兩天的搗鼓在linux下安裝openjdk,本人放棄了,編譯openjdk出現各種問題,需要的很多相關庫在網上下過來編譯不通過,只有放棄了,應該是跟本人用的linux版本有關,向那些編譯過openjdk的朋友致敬!!!
退而求其次,只有用jvmti了.
jvmti是JDK提供的一套用於開發JVM監控, 問題定位與性能調優工具的通用編程接口(API)。
通過jvmti,我們可以開發各式各樣的JVMTI Agent。這個Agent的表現形式是一個以c/c++語言編寫的動態共享庫。
jvmti Agent原理: java啟動或運行時,動態加載一個外部基於JVM TI編寫的dynamic module到Java進程內,然后觸發JVM源生線程Attach Listener來執行這個dynamic module的回調函數。在函數體內,你可以獲取各種各樣的VM級信息,注冊感興趣的VM事件,甚至控制VM的行為。
jvmti正好提供了一個加載class之前的一個事件,函數接口
(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);
// Clear the callbacks structure and set the ones you want.
( void)memset(&callbacks, 0, sizeof(callbacks));
callbacks.ClassFileLoadHook = &cbClassFileLoadHook;//注冊函數,這是咋們關心的
error = jvmti->SetEventCallbacks(&callbacks,(jint) sizeof(callbacks));
事情就比較簡單了,在cbClassFileLoadHook函數里解密加密后的buffer,賦給new_class_data,new_class_data_len就行了
一個是buffer,一個是數據buffer的大小,事情是不是相當的簡單了...
有一點注意,在這里你不能new 一個buffer返回給虛擬機,否則這個buffer誰管理啊 什么時候釋放?
通過虛擬機來申請buffer
好了,就這么簡單,貼出關鍵代碼:
#include <jvmti.h>
jvmtiEnv* m_pJvmTI =NULL;
void JNICALL cbClassFileLoadHook(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)
{
printf( " class name=%s\n ", name);
//此處自己去解密;
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jvmtiEnv *jvmti;
jvmtiError error;
// Create the JVM TI environment (jvmti).
jint result = vm->GetEnv(( void **) &jvmti, JVMTI_VERSION_1_1);
if (result != JNI_OK) {
printf( " ERROR: Unable to access JVMTI!\n ");
return 1;
}
m_pJvmTI =jvmti;
jvmtiCapabilities capabilities;
// Clear the capabilities structure and set the ones you need.
( void)memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_all_class_hook_events = 1;
capabilities.can_tag_objects = 1;
capabilities.can_generate_object_free_events = 1;
capabilities.can_get_source_file_name = 1;
capabilities.can_get_line_numbers = 1;
capabilities.can_generate_vm_object_alloc_events = 1;
// Request these capabilities for this JVM TI environment.
error = jvmti->AddCapabilities(&capabilities);
if (error != JVMTI_ERROR_NONE) {
printf( " ERROR: Unable to AddCapabilities JVMTI!\n ");
return error;
}
jvmtiEventCallbacks callbacks;
// Clear the callbacks structure and set the ones you want.
( void)memset(&callbacks, 0, sizeof(callbacks));
callbacks.ClassFileLoadHook = &cbClassFileLoadHook;
error = jvmti->SetEventCallbacks(&callbacks,(jint) sizeof(callbacks));
if (error!=JVMTI_ERROR_NONE)
{
printf( " ERROR: Unable to SetEventCallbacks JVMTI!\n ");
return error;
}
// For each of the above callbacks, enable this event.
error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
(jthread)NULL);
if (error!=JVMTI_ERROR_NONE)
{
printf( " ERROR: Unable to SetEventNotificationMode JVMTI!\n ");
return error;
}
return JNI_OK; // Indicates to the VM that the agent loaded OK.
}
參考資料:
http://java.sun.com/developer/technicalArticles/J2SE/jvm_ti/
http://docs.oracle.com/javase/1.5.0/docs/guide/jvmti/jvmti.html
加密程序和解密庫
http://files.cnblogs.com/lancao008/declass.rar
ps:
真正用在公司是java服務器的話,可以考慮寫個配置文件通過網卡mac地址通過算法生成一個序列號,在動態庫里算出序列號比較,如果不正確就不加載,然后把動態庫加密,以防很容易就反匯編看出來!!!
linux下編譯:
g++ -I${JAVA_HOME}/include/ -I${JAVA_HOME}/include/linux declass.cpp -shared -o libdeclass.so -m32
附上說明
Hello 為java .class文件
windows
java -agentlib:c:\jvm\deClass Hello
deClass就是deClass.dll,注意不需要加.dll
tomcat
修改tomcat的bin目錄下catalina.bat
set JAVA_OPTS=-agentlib:c:\jvm\deClass
linux下
拷貝libdeclass.so到/lib下
java -agentlib:declass Hello
tomcat:
修改catalina.sh
JAVA_OPTS =-agentlib:declass