如果想要保护自己的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目录,然后添加启动命令参数,然后运行就大功告成啦!