基于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 <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的咸鱼。

众所周知,Java编译后的Jar包和Class文件,可以轻而易举的使用反编译工具(如JD-GUI)进行反编译,拿到源码。为了保护自己发布的Jar包和Class文件,采用的方式大多是混淆方式,这种方式对于Class文件的密是不彻底的,还是能够通过分析得出核心算法。本工具是采用jvmti方式对Class文件进行密,使用C++生成密和解密库,先用密库对Jar包进行密,将密后的Jar包及解密库文件发布出去,执行时候需要JVM引入解密库文件,解密后执行。c++的.dll文件和.so文件的破解难度是很大的,这就能有效的保护软件和代码的知识产权. 使用方法: 1.打开windows命令行(运行=>cmd=>回车),在命令行中 进入 EncryptJar目录 2.执行 java -jar encrypt.jar 3.输入h,然后回车,可以看到帮助菜单 4.输入3,然后按回车键,进入入jar文件功能 5.输入要密的jar文件的路径 6.提示输入秘钥(key)的时候,直接回车,不要输入任何字符(否则后面classhook将不可解密密后的jar包) 7.输入目标路径(密后的jar文件路径,此处要注意:jar文件名要保持相同,将密后的文件保存到不同的目录) 8.将密后的jar包,替换原来的没有密的jar包,与要发布的程序一起进行发布.(一般替换lib目录下对应的jar包即可) 9.密后的jar包运行方法: windows下: 拷贝libClassHook.dll文件到程序的根目录(通常为要执行的jar程序的根目录) 使用以下命令启动程序: java -agentlib:libClassHook -jar xxxxxxxxxxx.jar 则在运行过程中会自动进行解密操作(解密过程是运行过程中用c++的dll进行解密的,可以有效防止破解class文件) 如果执行过程报错,可将程序根目录添到环境变量path中去 Linux下: 拷贝libClassHook.so到程序的根目录(通常为要执行的jar程序的根目录) 使用以下命令启动程序: java -agentlib:ClassHook -jar xxxxxxxxxxx.jar (这里要删除掉lib,linux系统下会自动补全) 则在运行过程中会自动进行解密操作(解密过程是运行过程中用c++的dll进行解密的,可以有效防止破解class文件) 如果执行过程报错,可以在程序根目录下执行以下语句:export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH 或将libClassHook.so 拷贝到/usr/lib目录中去。 支持操作系统:密请在windows64位系统并安装了64位jdk环境下进行。 需要解密运行的程序支持LINUX(64位)和windows(64位)安装了JDK1.8以上的系统。 测试程序: (t_lib目录下的jar包为经过密的jar包) java -agentlib:libClassHook -jar test.jar
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值