java vm 加密,JVM层对jar包字节码加密

github

需求

拿到的需求是要对某特定的jar包实现加密保护,jar包需要提供给外部使用,但核心逻辑部分需要保护以免被简单反编译即能看到。

几个思路

大致想到以下几种方式:

1. 混淆器,将jar包混淆后反编译出来的东西看起来就很眼花,但如果耐心一点也是可以看出来的。

2. 对jar包进行加密,然后在Java层重写类加载器对其进行解密,以达到对jar包的加密保护。包括用对称加密算法和非对称加密算法。不管用什么算法,在Java层面的类加载器实现的话,其实也作用不大,因为类加载器本身被反编译出来后就基本暴露无遗了。

3. 可以修改java编译后的class文件的某些属性,以让反编译软件分析不了,但它也不可靠,只要按照class格式深入分析下也能反编译出来。

4. 修改JDK源码,定制JDK就涉及到JVM的整体改动,而且还要求外部使用,不太可行。

5. 利用JDK中JVM的某些类似钩子机制和事件监听机制,监听加载class事件,使用本地方式完成class的解密。C/C++被编译后想要反编译就很麻烦了,另外还能加壳。这里就看看这种方式。

关于JVMTI

JVMTI即JVM Tool Interface,提供了本地编程接口,主要是提供了调试和分析等接口。JVMTI非常强大,通过它能做很多事,比如可以监听某事件、线程分析等等。

那么一般怎么使用JVMTI?一般使用Agent方式来使用,就是通过-agentlib和-agentpath指定Agent的本地库,然后Java启动时就会加载该动态库。这个时刻其实可以看成是JVM启动的时刻,而并非是Java层程序启动时刻,所以此时还不涉及与Java相关的类和对象什么的。

agent动态库被加载后,JVM肯定会指定一个入口函数,该入口函数为:

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm,char *options,void *reserved)1

于是在JVM启动前要做的事都可以放到这个函数中,比如设置jvmtiCapabilities、设置关注的事件、设置事件的回调函数等等。其中关键的jvmtiCapabilities、jvmtiEventCallbacks、jvmtiEvent三个结构体如下,根据实际情况设置。

typedef struct {

unsigned int can_tag_objects : 1;

unsigned int can_generate_field_modification_events : 1;

unsigned int can_generate_field_access_events : 1;

unsigned int can_get_bytecodes : 1;

unsigned int can_get_synthetic_attribute : 1;

unsigned int can_get_owned_monitor_info : 1;

unsigned int can_get_current_contended_monitor : 1;

unsigned int can_get_monitor_info : 1;

unsigned int can_pop_frame : 1;

unsigned int can_redefine_classes : 1;

unsigned int can_signal_thread : 1;

unsigned int can_get_source_file_name : 1;

unsigned int can_get_line_numbers : 1;

unsigned int can_get_source_debug_extension : 1;

unsigned int can_access_local_variables : 1;

unsigned int can_maintain_original_method_order : 1;

unsigned int can_generate_single_step_events : 1;

unsigned int can_generate_exception_events : 1;

unsigned int can_generate_frame_pop_events : 1;

unsigned int can_generate_breakpoint_events : 1;

unsigned int can_suspend : 1;

unsigned int can_redefine_any_class : 1;

unsigned int can_get_current_thread_cpu_time : 1;

unsigned int can_get_thread_cpu_time : 1;

unsigned int can_generate_method_entry_events : 1;

unsigned int can_generate_method_exit_events : 1;

unsigned int can_generate_all_class_hook_events : 1;

unsigned int can_generate_compiled_method_load_events : 1;

unsigned int can_generate_monitor_events : 1;

unsigned int can_generate_vm_object_alloc_events : 1;

unsigned int can_generate_native_method_bind_events : 1;

unsigned int can_generate_garbage_collection_events : 1;

unsigned int can_generate_object_free_events : 1;

unsigned int can_force_early_return : 1;

unsigned int can_get_owned_monitor_stack_depth_info : 1;

unsigned int can_get_constant_pool : 1;

unsigned int can_set_native_method_prefix : 1;

unsigned int can_retransform_classes : 1;

unsigned int can_retransform_any_class : 1;

unsigned int can_generate_resource_exhaustion_heap_events : 1;

unsigned int can_generate_resource_exhaustion_threads_events : 1;

unsigned int : 7;

unsigned int : 16;

unsigned int : 16;

unsigned int : 16;

unsigned int : 16;

unsigned int : 16;

} jvmtiCapabilities;1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

typedef struct {

/* 50 : VM Initialization Event */

jvmtiEventVMInit VMInit;

/* 51 : VM Death Event */

jvmtiEventVMDeath VMDeath;

/* 52 : Thread Start */

jvmtiEventThreadStart ThreadStart;

/* 53 : Thread End */

jvmtiEventThreadEnd ThreadEnd;

/* 54 : Class File Load Hook */

jvmtiEventClassFileLoadHook ClassFileLoadHook;

/* 55 : Class Load */

jvmtiEventClassLoad ClassLoad;

/* 56 : Class Prepare */

jvmtiEventClassPrepare ClassPrepare;

/* 57 : VM Start Event */

jvmtiEventVMStart VMStart;

/* 58 : Exception */

jvmtiEventException Exception;

/* 59 : Exception Catch */

jvmtiEventExceptionCatch ExceptionCatch;

/* 60 : Single Step */

jvmtiEventSingleStep SingleStep;

/* 61 : Frame Pop */

jvmtiEventFramePop FramePop;

/* 62 : Breakpoint */

jvmtiEventBreakpoint Breakpoint;

/* 63 : Field Access */

jvmtiEventFieldAccess FieldAccess;

/* 64 : Field Modification */

jvmtiEventFieldModification FieldModification;

/* 65 : Method Entry */

jvmtiEventMethodEntry MethodEntry;

/* 66 : Method Exit */

jvmtiEventMethodExit MethodExit;

/* 67 : Native Method Bind */

jvmtiEventNativeMethodBind NativeMethodBind;

/* 68 : Compiled Method Load */

jvmtiEventCompiledMethodLoad CompiledMethodLoad;

/* 69 : Compiled Method Unload */

jvmtiEventCompiledMethodUnload CompiledMethodUnload;

/* 70 : Dynamic Code Generated */

jvmtiEventDynamicCodeGenerated DynamicCodeGenerated;

/* 71 : Data Dump Request */

jvmtiEventDataDumpRequest DataDumpRequest;

/* 72 */

jvmtiEventReserved reserved72;

/* 73 : Monitor Wait */

jvmtiEventMonitorWait MonitorWait;

/* 74 : Monitor Waited */

jvmtiEventMonitorWaited MonitorWaited;

/* 75 : Monitor Contended Enter */

jvmtiEventMonitorContendedEnter MonitorContendedEnter;

/* 76 : Monitor Contended Entered */

jvmtiEventMonitorContendedEntered MonitorContendedEntered;

/* 77 */

jvmtiEventReserved reserved77;

/* 78 */

jvmtiEventReserved reserved78;

/* 79 */

jvmtiEventReserved reserved79;

/* 80 : Resource Exhausted */

jvmtiEventResourceExhausted ResourceExhausted;

/* 81 : Garbage Collection Start */

jvmtiEventGarbageCollectionStart GarbageCollectionStart;

/* 82 : Garbage Collection Finish */

jvmtiEventGarbageCollectionFinish GarbageCollectionFinish;

/* 83 : Object Free */

jvmtiEventObjectFree ObjectFree;

/* 84 : VM Object Allocation */

jvmtiEventVMObjectAlloc VMObjectAlloc;

} jvmtiEventCallbacks;1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

typedef enum {

JVMTI_MIN_EVENT_TYPE_VAL = 50,

JVMTI_EVENT_VM_INIT = 50,

JVMTI_EVENT_VM_DEATH = 51,

JVMTI_EVENT_THREAD_START = 52,

JVMTI_EVENT_THREAD_END = 53,

JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54,

JVMTI_EVENT_CLASS_LOAD = 55,

JVMTI_EVENT_CLASS_PREPARE = 56,

JVMTI_EVENT_VM_START = 57,

JVMTI_EVENT_EXCEPTION = 58,

JVMTI_EVENT_EXCEPTION_CATCH = 59,

JVMTI_EVENT_SINGLE_STEP = 60,

JVMTI_EVENT_FRAME_POP = 61,

JVMTI_EVENT_BREAKPOINT = 62,

JVMTI_EVENT_FIELD_ACCESS = 63,

JVMTI_EVENT_FIELD_MODIFICATION = 64,

JVMTI_EVENT_METHOD_ENTRY = 65,

JVMTI_EVENT_METHOD_EXIT = 66,

JVMTI_EVENT_NATIVE_METHOD_BIND = 67,

JVMTI_EVENT_COMPILED_METHOD_LOAD = 68,

JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69,

JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70,

JVMTI_EVENT_DATA_DUMP_REQUEST = 71,

JVMTI_EVENT_MONITOR_WAIT = 73,

JVMTI_EVENT_MONITOR_WAITED = 74,

JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75,

JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76,

JVMTI_EVENT_RESOURCE_EXHAUSTED = 80,

JVMTI_EVENT_GARBAGE_COLLECTION_START = 81,

JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82,

JVMTI_EVENT_OBJECT_FREE = 83,

JVMTI_EVENT_VM_OBJECT_ALLOC = 84,

JVMTI_MAX_EVENT_TYPE_VAL = 84

} jvmtiEvent;1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

具体实现

编写我们的agent动态库,使之在JVM加载时完成一些逻辑,从前面也知道,主要就是在Agent_OnLoad函数中编写逻辑,先获取jvmtiEnv,在通过它设置jvmtiCapabilities,完了再设置回调函数及需要监听的事件。这里关注的事class文件加载时事件。

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 = &ClassDecryptHook;

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;

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

回调函数,即是上面指定的回调函数,它对应监听的事件,会在对应的事件发生事被调用。这里处理逻辑其实就是判断如果是某包下的类就对其进行解密,否则不处理。

void JNICALL ClassDecryptHook(

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* _data = *new_class_data;

if (name&&strncmp(name, "com/seaboat/", 11) == 0) {

for (int i = 0; i < class_data_len; i++)

{

_data[i] = class_data[i] - 4;

}

}

else {

for (int i = 0; i < class_data_len; ++i)

{

_data[i] = class_data[i];

}

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

额外写一个加密程序对某jar包加密进行加密处理,这里同样用本地库方式,但加密解密动态库不要一起对外发布,还有Java层调用本地库加密的程序也不要对外发布。

Java层

public class ByteCodeEncryptor {

static{

System.loadLibrary("ByteCodeEncryptor");

}

public native static byte[] encrypt(byte[] text);

public static void(String[] args){

try {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

byte[] buf = new byte[1024];

File srcFile = new File(fileName);

File dstFile = new File(fileName.substring(0, fileName.indexOf("."))+"_encrypted.jar");

FileOutputStream dstFos = new FileOutputStream(dstFile);

JarOutputStream dstJar = new JarOutputStream(dstFos);

JarFile srcJar = new JarFile(srcFile);

for (Enumeration enumeration = srcJar.entries(); enumeration.hasMoreElements();) {

JarEntry entry = enumeration.nextElement();

InputStream is = srcJar.getInputStream(entry);

int len;

while ((len = is.read(buf, 0, buf.length)) != -1) {

baos.write(buf, 0, len);

}

byte[] bytes = baos.toByteArray();

String name = entry.getName();

if(name.endsWith(".class")){

try {

bytes = ByteCodeEncryptor.encrypt(bytes);

} catch (Exception e) {

e.printStackTrace();

}

}

JarEntry ne = new JarEntry(name);

dstJar.putNextEntry(ne);

dstJar.write(bytes);

baos.reset();

}

srcJar.close();

dstJar.close();

dstFos.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

本地库

void encode(char *str)

{

unsigned int m = strlen(str);

for (int i = 0; i < m; i++)

{

str[i] = str[i]+4;

}

}

extern"C" JNIEXPORT jbyteArray JNICALL

Java_com_seaboat_bytecode_ByteCodeEncryptor_encrypt(JNIEnv * env, jclass cla,jbyteArray text)

{

char* dst = (char*)env->GetByteArrayElements(text, 0);

encode(dst);

env->SetByteArrayRegion(text, 0, strlen(dst), (jbyte *)dst);

return text;

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

通过agentlib参数启动Java,实现字节码解密,从而实现字节码保护。

java -agentlib:xxxxx\ByteCodeEncryptor -cp test_encrypted.jar com.seaboat.AA1

反编译前后效果

117799086_1_20171201034408400

117799086_2_20171201034408744

可能报错

下面的错误说明编译的是32位的动态库,不能再64位操作系统运行,可以到vs的vc目录下执行

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>vcvarsall.bat amd64,重新编译64位的动态库即可。

Error occurred during initialization of VM

Could not find agent library D:\kuaipan\workspace\CPP-workspace\Project3\ByteCodeEncryptor on the library path, with error: Can't load IA 32-bit .dll on a AMD 64-bit platform1

2

========广告时间========

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以到 https://item.jd.com/12185360.html 进行预定。感谢各位朋友。

=========================

欢迎关注:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值