赞
踩
任何任务都不会平白无故的产生,都是有需求的驱动。本文主要的产生原因是公司的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 <stdlib.h>
- #include <string.h>
-
- #include <jvmti.h>
- #include <jni.h>
- #include <jni_md.h>
- #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; i<class_data_len; i++)
- my_data[i] = class_data[i];
- char keyBlock[8];
- memcpy(keyBlock,class_data+16,8);
- if(strstr(keyBlock,"yangmo") ) {
- // printf("%s\n",keyBlock);
- // DES_Encrypt2(class_data,my_data,class_data_len,name);
- int lengthLLL = 0;
- int b =DES_Decrypt3(class_data,my_data, class_data_len,name,lengthLLL);
- // printf("wwwwwwwwwwww%d\n",b);
- // printf("bbbbbbbbbbbbbbbbbbbbbb%s%d\n",name,b);
- if(b!=0)
- *new_class_data_len = b;
- }
- }
-
- JNIEXPORT jint JNICALL
- Agent_OnLoad(
- JavaVM *vm,
- char *options,
- void *reserved
- )
- {
- jvmtiEnv *jvmti;
-
- jint ret = vm->GetEnv((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的咸鱼。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。