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

- 日理万妓 2022-06-06 23:00 191阅读 0赞

如果想要保护自己的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初始化完毕事件,并执行自己的方法,以下是具体代码

  1. #include <jvmti.h>
  2. #include <string>
  3. #include <cstring>
  4. #include <iostream>
  5. #include <list>
  6. #include <map>
  7. #include <set>
  8. #include <stdlib.h>
  9. #include <jni_md.h>
  10. #include <string>
  11. typedef struct tagHeader
  12. {
  13. int nSeqNum;//分割二进制 用来判断是否为0xbfbafeca
  14. int nHash;//这个应该是对象的hash值
  15. char strPass[16];
  16. } Header; //这个是对象头
  17. std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
  18. {
  19. while (true) {
  20. std::string::size_type pos(0);
  21. if ((pos = str.find(old_value)) != std::string::npos)
  22. str.replace(pos, old_value.length(), new_value);
  23. else break;
  24. }
  25. return str;
  26. }
  27. char * jstringTostring(JNIEnv* env, jstring jstr)
  28. {
  29. char* rtn = NULL;
  30. jclass clsstring = env->FindClass("java/lang/String");
  31. jstring strencode = env->NewStringUTF("utf-8");
  32. jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
  33. jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
  34. jsize alen = env->GetArrayLength(barr);
  35. jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
  36. if (alen > 0)
  37. {
  38. rtn = (char*)malloc(alen + 1);
  39. memcpy(rtn, ba, alen);
  40. rtn[alen] = 0;
  41. }
  42. env->ReleaseByteArrayElements(barr, ba, 0);
  43. return rtn;
  44. }
  45. //(Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class;
  46. //(String,byte[],int,int,ProtectionDomainm,string)class
  47. //返回值为class类型
  48. jclass MyDefine1Class(JNIEnv * env, jobject obj, jstring name, jbyteArray b, jint off, jint len, jobject protection_doamin, jstring source)
  49. {
  50. jbyte * buff = env->GetByteArrayElements(b, NULL);
  51. char* buff1 = (char*)env->GetByteArrayElements(b, NULL);
  52. Header header;
  53. jclass descClazz;
  54. memcpy(&header, buff, sizeof(header));//将对象头部单独放到header中
  55. char * className = jstringTostring(env, name); //
  56. std::string realName(className);
  57. replace_all(realName, ".", "/");
  58. //如果发现加载的class以0xbfbafeca开头的就认为这个class已经被加密了,然后进行解密
  59. if (header.nSeqNum == 0xbfbafeca)
  60. {
  61. //解密,将0xbfbafeca变为0xbebafeca
  62. header.nSeqNum = header.nSeqNum & 0xFEFFFFFF;
  63. memcpy(buff, &header.nSeqNum, sizeof(header.nSeqNum));
  64. }
  65. //class被解密后或者没有被加密的class,直接就正常可以加载class
  66. descClazz = env->DefineClass(realName.c_str(), obj, buff, len);
  67. free(className);
  68. env->ReleaseByteArrayElements(b, buff, 0);
  69. return descClazz;
  70. }
  71. //JVM初始化完毕后要进行操作的函数
  72. void JNICALL cbVminit(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread)
  73. {
  74. printf("cd VMinit\n");
  75. jclass clazz = jni_env->FindClass("java/lang/ClassLoader");
  76. if (clazz != NULL)
  77. {
  78. //通过JNI的RegisterNatives方法将ClassLoader的defineClass1重写以实现解密
  79. //其实就是自己写一个defineClass方法,加到ClassLoader类中,这里是class格式的一些规则,具体可以看一些class格式
  80. JNINativeMethod methods[] = {
  81. { "defineClass1", "(Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class;", (void*)MyDefine1Class },
  82. };
  83. jni_env->RegisterNatives(clazz, methods, 1);
  84. }
  85. }
  86. JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
  87. // nothing to do
  88. }
  89. //代理加载时执行
  90. JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
  91. // nothing to do
  92. jvmtiEnv *jvmti = NULL;
  93. jvmtiError error;
  94. // 获取JVM环境
  95. jint result = jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);
  96. if (result != JNI_OK) {
  97. printf("ERROR: Unable to access JVMTI!\n");
  98. }
  99. jvmtiCapabilities capabilities;//方便判断SetEventNotificationMode是否要开启
  100. (void)memset(&capabilities, 0, sizeof(capabilities));
  101. //capabilities.can_generate_all_class_hook_events = -1;
  102. capabilities.can_tag_objects = 1;
  103. capabilities.can_generate_object_free_events = 1;
  104. capabilities.can_get_source_file_name = 1;
  105. capabilities.can_get_line_numbers = 1;
  106. capabilities.can_generate_vm_object_alloc_events = 1;
  107. error = jvmti->AddCapabilities(&capabilities);
  108. if (error != JVMTI_ERROR_NONE)
  109. {
  110. printf("ERROR: Unable to AddCapabilities JVMTI!\n");
  111. return error;
  112. }
  113. //定义要监听的模块,这里定义了监听JVM初始化完毕模块
  114. jvmtiEventCallbacks callbacks;
  115. (void)memset(&callbacks, 0, sizeof(callbacks));
  116. callbacks.VMInit = &cbVminit;//赋值JVM初始化完毕以后进行操作函数
  117. error = jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks));
  118. if (error != JVMTI_ERROR_NONE)
  119. {
  120. printf("ERROR: Unable to SetEventCallbacks JVMTI!\n");
  121. return error;
  122. }
  123. //定义了监听模块以后,这里还需要开启你要监听的模块,是必须的
  124. error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
  125. if (error != JVMTI_ERROR_NONE)
  126. {
  127. printf("ERROR: Unable to SetEventNotificationMode JVMTI!\n");
  128. return error;
  129. }
  130. return JNI_OK;
  131. }

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

发表评论

表情:
评论列表 (有 0 条评论,191人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android 防止编译

    作为Android应用开发者,不得不面对一个尴尬的局面,就是自己辛辛苦苦开发的应用可以被别人很轻易的就反编译出来。 Google似乎也发现了这个问题,从SDK2.3开始我们可