基于jvmti方式加密java

    大家知道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之前的一个事件,函数接口

 

 
typedef void (JNICALL  *jvmtiEventClassFileLoadHook)
    (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);
 
    jvmtiEventCallbacks callbacks;
    // Clear the callbacks structure and set the ones you want.
    (void)memset(&callbacks,0sizeof(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

 

m_pJvmTI->Allocate(newlen,new_class_data);

 

好了,就这么简单,贴出关键代码:

 

#include <jni.h>
#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,0sizeof(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,0sizeof(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

 

 

posted @ 2012-03-29 15:31 蓝云@ 阅读(...) 评论(...) 编辑 收藏