基于jvmti和c++实现的class加密解密(一)

    任何任务都不会平白无故的产生,都是有需求的驱动。本文主要的产生原因是公司的web项目要进行产品化。将项目卖给多家并进行防反编译,本文在写作和实现的时候用到了晚上的资源。如有原作者有需要请联系我。本人将予以注明出处。

    作为一个没有任何密码学的人,刚开始接受这个任务的时候其实我是拒绝的。但是,最后还是落到了我的头上。那么开始吧,作为一个java程序员。我的第一反应是用java进行加密base64或des之类的加密算法进行加密。虽然实现了加密,但是存在的问题是解密类不能加密,解密的密钥在解密类中写的清清楚楚。利用该方案实现加密解密的方式为,写一个加密类将所有的要加密的class都进行循环遍历加密。然后通过重新classloader实现解密。但是重新的classloader是非加密的。所以忽略该方法。

    继续进行资料的收集会发现,java从1.5版本之后提供了JNI(Java Native Interface)JAVA本地接口。可以通过c++或c进行解密。但是这个也会遇到上一个方案所面临的问题。虽然解密的密码是写在c或c++的底层。但是调用jni的类是不加密的,可以通过改写这个jni类将解密后的class保存到本地。所以这个方案也被pass了。

    继续收集资料返现,java提供了jvmti接口来监控虚拟机的运行。引用方式为在java参数中设置-agentlib: 来进行调用。到这里,我们已经接近了加密功能的一般,通过agentlib进行解密的好处是在参数中直接可以调用.dll 或.so来进行解密。不会有没加密的class的问题。通过监听class加载的方式实现,将解密后的class直接放入虚拟机中不会存在。将解密后的class保存到本地的问题。所以得操作都是在dll中进行的。

   

#include 
#include 
 
#include 
#include 
#include 
#include "des.h"
void JNICALL
MyClassFileLoadHook(
    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;
    for(int i=0; iGetEnv((void **)&jvmti, JVMTI_VERSION);
    if(JNI_OK!=ret)
    {
        printf("ERROR: Unable to access JVMTI!\n");
        return ret;
    }
 
    jvmtiCapabilities capabilities;
    (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;
 
    jvmtiError error = jvmti->AddCapabilities(&capabilities);
    if(JVMTI_ERROR_NONE!=error)
    {
        printf("ERROR: Unable to AddCapabilities JVMTI!\n");
        return error;
    }
 
    jvmtiEventCallbacks callbacks;
    (void)memset(&callbacks,0, sizeof(callbacks));
 
    callbacks.ClassFileLoadHook = &MyClassFileLoadHook;
    error = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
    if(JVMTI_ERROR_NONE!=error){
        printf("ERROR: Unable to SetEventCallbacks JVMTI!\n");
        return error;
    }
 
    error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
    if(JVMTI_ERROR_NONE!=error){
        printf("ERROR: Unable to SetEventNotificationMode JVMTI!\n");
        return error;
    }
 
    return JNI_OK;
}

    上面的MyClassFileLoadHook这个方法是来进行解密的。其中下面的代码是来进行判断哪些是需要解密的,哪些是不需要解密的class。(没错就是用的我的英文名字……O(∩_∩)O哈哈~开玩笑)。DES_Decrypt3是进行解密的方式要传递的参数 加密后的2进制class,返回后的解密的clss,加密之后的class长度,class名字(含有包路径),额~~无用参数(本来是想返回解密后的文件长度的但是jint类型向下传递时遇到些问题,通过方法返回值绕过了这个问题。)

if(strstr(keyBlock,"yangmo") ) {
/int lengthLLL = 0;
   int b =DES_Decrypt3(class_data,my_data, class_data_len,name,lengthLLL);
  if(b!=0)
   *new_class_data_len = b; 
   }

    因为解密的程序是要生成jvmti能识别的动态库,因此需要引入JAVA_HOME/include/和JAVA_HOME/include/win32如下图所示。本人用的是dev-c++进行开发的。vs太高大上了搞不懂。


左面为加密后的截图,右面为加密前的截图。前16个字节没有进行加密,第17-24个字节是区分class加密没加密的标识符

    这个基本就是解密的流程了,具体解密代码就不贴了。有解密必然是有加密。下篇博客就是加密了。写的有些乱望大家海涵啊!

    作为一个有追求的懒人,一直都是写博客的打算。巴特,因为人懒所以一直都没有实现。今天,写下人生之中第一批博客。算是……算是2016的年终总结好了。一个很懒会喊666的咸鱼。

你可能感兴趣的:(基于jvmti和c++实现的class加密解密(一))