JuiceAgent是一个基于JVMTI的工具库,可以在运行时修改、获取Java字节码,即使在DisableAttachMechanism开启时也能运行,使用Dll 注入技术。
本文章不解释dll注入部分,ReflectiveDLLInjection 已经实现dll注入,See Inject.c
截至目前(2025-12-12),JuiceAgent仅支持Windows,故只介绍Windows下的实现
A. 加载器
1. 入口: DllMain
注入任何Dll(以及JNI方式加载dll)的时候,都会触发DllMain, JuiceAgent借此特性来初始化。
使用ReflectiveDLLInjection注入Dll时,可以通过lpReserved 来传递参数,JuiceAgent中传递的参数是当前的运行目录路径。
以下是关键实现代码
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved ) {
switch( fdwReason )
{
case DLL_PROCESS_ATTACH: {
InitLogger();
PLOGI << "DllMain: DLL_PROCESS_ATTACH";
PLOGD.printf("lpReserved: %p", lpReserved);
const char* runtime_dir = (const char*)lpReserved;
PLOGD << "runtime_dir: " << runtime_dir;
// 下一步: 初始化JuiceAgent
InitJuiceAgent(runtime_dir);
break;
}
case DLL_THREAD_ATTACH: {
break;
}
case DLL_THREAD_DETACH: {
break;
}
case DLL_PROCESS_DETACH: {
PLOGI << "DllMain: DLL_PROCESS_DETACH";
PLOGD.printf("lpReserved: %p", lpReserved);
break;
}
}
return TRUE;
}2. 初始化: InitJuiceAgent
这部分以读取配置、获取JNI/JVMTI环境为主。
关键点在于,在jvm进程中可以使用JNI_GetCreatedJavaVMs获取jvm,获取jvm之后可以使用GetEnv来获取jni和jvmti环境,以实现调用Java函数、redefine、retransform等操作。
示例:获取JVM环境,需要在JAVA中运行(即注入到JAVA程序中的DLL将会运行以下代码)
#include <jvm/jni.h>
#include <jvm/jvmti.h>
......
//变量声明
JavaVM *jvm = NULL;
// 获取JVM环境
JNI_GetCreatedJavaVMs(&jvm, 1, NULL);接下来是获取JNI和JVMTI环境
// jvmti变量
jvmtiEnv *jvmti = NULL;
// jni变量
JNIEnv *env = NULL;
// 获取jni环境,版本为1.8
jvm->GetEnv((void**)&env, JNI_VERSION_1_8);
// 获取jvmti环境,版本为1.2
jvm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_2);可选的JNI/JVMTI版本,各个版本编程API各有不同
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006
#define JNI_VERSION_1_8 0x00010008
#define JNI_VERSION_9 0x00090000
#define JNI_VERSION_10 0x000a0000
#define JNI_VERSION_19 0x00130000
#define JNI_VERSION_20 0x00140000
#define JNI_VERSION_21 0x00150000
enum {
JVMTI_VERSION_1 = 0x30010000,
JVMTI_VERSION_1_0 = 0x30010000,
JVMTI_VERSION_1_1 = 0x30010100,
JVMTI_VERSION_1_2 = 0x30010200,
JVMTI_VERSION_9 = 0x30090000,
JVMTI_VERSION_11 = 0x300B0000,
JVMTI_VERSION_19 = 0x30130000,
JVMTI_VERSION_21 = 0x30150000,
JVMTI_VERSION = 0x30000000 + (21 * 0x10000) + ( 0 * 0x100) + 0 /* version: 21.0.0 */
};成功获取环境之后就是注入JuiceLoader Jar
JuiceAgent.jvmti->AddToSystemClassLoaderSearch(InjectionInfo.JuiceLoaderJarPath)之后调用JuiceLoader Init (Java函数),这部分省略。
B. JuiceLoader
JuiceLoader的作用在于提供编程接口,与JVMTI交互。
JuiceLoader Init会加载libjuiceloader,因为直接通过JNI调用java的System.load("xxx.dll"); 会报错java.lang.UnsatisfiedLinkError,所以需要中间层JuiceLoader来加载,这样的做的好处还有可以在程序直接加载libjuiceloader而不用手动注入。
【未完待续】