使用jvmti实现class加密,防止反编译

来源:互联网 发布:河北大学网络拓扑图 编辑:程序博客网 时间:2024/05/09 07:46

如果想要保护自己的java代码不被别人反编译,则可以使用下面的jvmti对Class加密,然后在类加载器加载时再进行加密,逃避反编译。
jvmti是java自带的强大工具,我们可以通过jvmti做一些操作(例如:hook class加载,jvm启动前做什么事情,或者jvm初始化时做事情等等)
步骤如下:
1.对class进行加密
2.创建本地方法DLL,实现解密代码
(1)添加jvmti头文件
(2)监听jvm初始化完毕事件,并执行自己的方法
(3)在class类加载的时候实现解密
3.添加解密组件,其实也就是添加启动命令参数

1.对class进行加密,这里只是演示最简单的加密,将java魔数修改,就可以做到防止编译了,当然如果老手一看就知道怎么解密了,具体的加密可以再自己搞。
正常class文件(开头4字节为0xbebafeca):
这里写图片描述
加密后的class文件(开头文件为0xbfbafeca):
这里写图片描述

2.创建本地方法DLL,实现解密代码
(1)添加jvmti头文件,找到自己java的安装路径,然后引入

这里写图片描述
(2)监听jvm初始化完毕事件,并执行自己的方法,以下是具体代码

#include <jvmti.h>#include <string>#include <cstring>#include <iostream>#include <list>#include <map>#include <set>#include <stdlib.h>#include <jni_md.h>#include <string>typedef struct tagHeader{    int nSeqNum;//分割二进制 用来判断是否为0xbfbafeca    int nHash;//这个应该是对象的hash值    char strPass[16];} Header; //这个是对象头std::string& replace_all(std::string&   str, const  std::string&   old_value, const std::string&   new_value){    while (true)   {        std::string::size_type   pos(0);        if ((pos = str.find(old_value)) != std::string::npos)            str.replace(pos, old_value.length(), new_value);        else   break;    }    return   str;}char * jstringTostring(JNIEnv* env, jstring jstr){    char* rtn = NULL;    jclass clsstring = env->FindClass("java/lang/String");    jstring strencode = env->NewStringUTF("utf-8");    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");    jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);    jsize alen = env->GetArrayLength(barr);    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);    if (alen > 0)    {        rtn = (char*)malloc(alen + 1);        memcpy(rtn, ba, alen);        rtn[alen] = 0;    }    env->ReleaseByteArrayElements(barr, ba, 0);    return rtn;}//(Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class;//(String,byte[],int,int,ProtectionDomainm,string)class//返回值为class类型jclass MyDefine1Class(JNIEnv * env, jobject obj, jstring name, jbyteArray b, jint off, jint len, jobject protection_doamin, jstring source){    jbyte * buff = env->GetByteArrayElements(b, NULL);    char*  buff1 = (char*)env->GetByteArrayElements(b, NULL);    Header header;    jclass descClazz;    memcpy(&header, buff, sizeof(header));//将对象头部单独放到header中    char * className = jstringTostring(env, name); //    std::string realName(className);    replace_all(realName, ".", "/");    //如果发现加载的class以0xbfbafeca开头的就认为这个class已经被加密了,然后进行解密    if (header.nSeqNum == 0xbfbafeca)    {        //解密,将0xbfbafeca变为0xbebafeca        header.nSeqNum = header.nSeqNum & 0xFEFFFFFF;        memcpy(buff, &header.nSeqNum, sizeof(header.nSeqNum));    }    //class被解密后或者没有被加密的class,直接就正常可以加载class    descClazz = env->DefineClass(realName.c_str(), obj, buff, len);    free(className);    env->ReleaseByteArrayElements(b, buff, 0);    return descClazz;}//JVM初始化完毕后要进行操作的函数void JNICALL cbVminit(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread){    printf("cd VMinit\n");    jclass clazz = jni_env->FindClass("java/lang/ClassLoader");    if (clazz != NULL)    {        //通过JNI的RegisterNatives方法将ClassLoader的defineClass1重写以实现解密        //其实就是自己写一个defineClass方法,加到ClassLoader类中,这里是class格式的一些规则,具体可以看一些class格式        JNINativeMethod methods[] = {            { "defineClass1", "(Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class;", (void*)MyDefine1Class },        };        jni_env->RegisterNatives(clazz, methods, 1);    }}JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {    // nothing to do}//代理加载时执行JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {    // nothing to do    jvmtiEnv *jvmti = NULL;    jvmtiError error;    // 获取JVM环境    jint result = jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);    if (result != JNI_OK) {        printf("ERROR: Unable to access JVMTI!\n");    }    jvmtiCapabilities capabilities;//方便判断SetEventNotificationMode是否要开启    (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;    error = jvmti->AddCapabilities(&capabilities);    if (error != JVMTI_ERROR_NONE)     {        printf("ERROR: Unable to AddCapabilities JVMTI!\n");        return error;    }    //定义要监听的模块,这里定义了监听JVM初始化完毕模块    jvmtiEventCallbacks callbacks;    (void)memset(&callbacks, 0, sizeof(callbacks));    callbacks.VMInit = &cbVminit;//赋值JVM初始化完毕以后进行操作函数    error = jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks));    if (error != JVMTI_ERROR_NONE)    {        printf("ERROR: Unable to SetEventCallbacks JVMTI!\n");        return error;    }    //定义了监听模块以后,这里还需要开启你要监听的模块,是必须的    error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);    if (error != JVMTI_ERROR_NONE)    {        printf("ERROR: Unable to SetEventNotificationMode JVMTI!\n");        return error;    }    return JNI_OK;}

3.添加解密组件,其实也就是添加启动命令参数
将生成的DLL放到lib目录,然后添加启动命令参数,然后运行就大功告成啦!
这里写图片描述

原创粉丝点击