JVMTI

字节码文件加密

Java语言是编译型和解释型混合,源代码被编译为字节码文件,JVM再将字节码解释给CPU执行

反编译很简单,字节码文件基本上可以等同于源码文件。

对Java的加密可以分为对源码的加密和对字节码的加密,有如下几种方案

  • 源码混淆

    • 对变量、函数、类名等进行替换、混淆

    • 使代码不容易阅读

  • 字节码转换

    • ClassLoader

      • 自定义类加载器,读取字节码后对字节码解密后再加载

    • Instrumentation

      • 自定义ClassFileTransformer,调用transform对字节码进行解密

      • java -javaagent:xx.jar -jar yy.jar

      • 解密后的字节码再载入JVM

    • JVMTI

      • C/C++将解密算法封装到一个动态链接库

      • java -agentpath:xx.dll –jar yy.jar

      • 通过Agent_OnLoad引入ClassFileLoadHook实现运行前字节码的解密

源码混淆只是增加源码阅读障碍,JVM仍能执行

而字节码转换则完全把字节码文件换为一个没有意义的二进制文件,JVM不能执行

Instrumentation/JVMTI将对源码的保护转移到了对加解密算法的保护,如果解密的JavaAgent/动态链接库泄露了,也可能被破解。

另外,运行时JVM中的字节码已经是原本的字节码,因此通过class dump也能拿到源码。

考虑到动态链接库还可以用加壳等方式保护,下面简单实现一个基于JVMTI的加解密

在此之前先介绍一下JVMTI编程,官方文档👉https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html

Agent_Onload

当使用-agentlib:xxx.dll启动Java程序时,JVM会首先从动态链接库中寻找函数Agent_Onload

JVMTI是基于事件驱动的,JVM每执行到一定的逻辑就会主动调用一些事件的回调接口,这些接口可以供开发者扩展自己的逻辑。

一般地,JVMTI程序的加载过程为如下流程

  1. 获取JVMTI环境(JVMTIEnvironment)

  2. 注册所需功能(Capabilities)

  3. 注册事件通知(Event Notification)

  4. 指定事件回调函数(Callback Method)

capabilities

capabilities函数用来使能JVMTI函数和事件,简单来说就是要添加capabilities才能实现对应的功能。

每个JVMTI环境都有它自己的一组capabilities,初始时为空,在OnLoad阶段进行capability的添加。添加capability可能会导致程序执行速度的降低。

capabilities的数据结构定义如下

下面列举一些capabilities

Field
Description

can_get_bytecodes

Can get bytecodes of a method GetBytecodes

can_redefine_classes

Can redefine classes with RedefineClasses

can_get_source_file_name

Can get the source file name of a class

can_access_local_variables

Can set and get local variables

can_generate_breakpoint_events

Can set and thus get Breakpoint events

can_generate_all_class_hook_events

Can generate ClassFileLoadHook events for every loaded class

can_generate_method_entry_events

Can generate method entry events on entering a method

can_retransform_classes

Can retransform classes with RetransformClasses

设置capabilities:

event&callback

agent可以响应程序中发生的event,并调用对应的回调函数进行处理

不同的event对应不同的callback,不同的函数参数保存着event发生时的附加信息

上面的能力表中看到有有些是events结尾的,即event需要的capability

按照如下步骤设置event:

  1. AddCapabilities添加event所需的capabilities

  2. SetEventCallbacks设置event的回调函数

  3. SetEventNotificationMode使能event

回调event

下面是jvmtiEventCallbacks的结构体定义

可以看出,和capability一样,event也是由JVMTI预先定义的,设置相对应的字段即可

设置event回调函数

cb_Xxx为我们实现的回调函数接口的地址

不同事件的回调函数的接口参数不同,比如ClassLoad

当一个类被首次加载时,ClassLoad事件会被触发

使能event

默认所有的event为非使能状态

jvmtiEvent用于标识event,JVMTI_EVENT_回调函数名

使能event:

Build

CLion新建项目

image-20240314200220887

javah -jni ByteCodeEncryptor获取头文件ByteCodeEncryptor.h

将该头文件放到工程目录下

CMakeLists.txt

library.cpp

Build得到一个动态链接库libJVMTICrypt.dll

接着创建一个jar用作测试

manifest

jar -cvfm main.jar manifest -C src .

main.jar进行加密

java -agentlib:libJVMTICrypt -cp main_Encrypted.jar org.demo.Test运行加密的jar

image-20240314204011865

Last updated

Was this helpful?