大家知道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之前的一个事件,函数接口
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,
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
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,
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.
}
加密程序和解密库
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