1. 程式人生 > >用javaagent來對java位元組碼進行變換

用javaagent來對java位元組碼進行變換

1、實現變換的函式
typedef void (JNICALL *jvmtiEventClassFileLoadHook) //類位元組碼變換的回撥介面
    (jvmtiEnv *jvmti_env, //註冊的jvmti
     JNIEnv* jni_env, //JNI環境
     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);

2、註冊回撥事件

jint result = jvm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_1);
jvmtiEventCallbacks callbacks;
callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;
(*jvmtienv)->SetEventCallbacks( jvmtienv,

                                                 &callbacks,

                                                 sizeof(callbacks));

3、啟用類位元組碼載入的hook事件,如果不啟用,註冊的回撥函式將不會被呼叫的

jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);

4、啟用位元組碼變換的標記,如果不啟用,可以呼叫回撥函式,但是裡面進行的位元組碼變換將不起效

jvmtiCapabilities capabilities;
	memset(&capabilities, 0, sizeof(capabilities));
	capabilities.can_retransform_classes = 1;

5、實際的實現,列印載入的類
#include <jvmti.h>
#include <jni.h>
#include <stdio.h>
#include <string.h>

extern "C" 
JNICALL void myClassTransform//類位元組碼變換的回撥介面
    (jvmtiEnv *jvmti_env, //註冊的jvmti
     JNIEnv* jni_env, //JNI環境
     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) {
    	printf("Loading class %s\n", name);
    }

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm,  char *options, void* reserved) {

	jvmtiEnv *jvmti = NULL;
	

	jint error = jvm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_1);

	//載入jvmti環境
	if(error != JNI_OK) {
		printf("Get env fail, errorCode=%d\n", error);
		return JNI_ERR;
	}

	//使用位元組碼變換能力
	jvmtiCapabilities capabilities;
	memset(&capabilities, 0, sizeof(capabilities));
	capabilities.can_retransform_classes = 1;
	capabilities.can_retransform_any_class = 1;
	jvmtiError e2 = jvmti->AddCapabilities(&capabilities);
	if(e2 != JVMTI_ERROR_NONE) {
		printf("AddCapabilities error\n");
		return JNI_ERR;
	}

	//設定回撥
	jvmtiEventCallbacks callbacks;
	memset(&callbacks, 0, sizeof(callbacks));
	callbacks.ClassFileLoadHook = &myClassTransform; //設定類檔案變換的檔案控制代碼

	e2 = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
	if(e2 != JVMTI_ERROR_NONE) {
		printf("setcallback error\n");
		return JNI_ERR;
	}

	e2 = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);


	printf("Register ok\n");

	return JNI_OK;
}

6、執行,注意要用g++進行編譯,如果用gcc進行編譯的,jvmti的方法呼叫,應該形如(*jvmti)->xxx(jvmti,...)

g++ -fPIC -shared -g -o test.dylib -I$JAVA_HOME/include  -I$JAVA_HOME/include/darwin/ test.cpp
javac Test.java
java -agentpath:test.dylib Test

7、關於jvm的這些hook呼叫的實現原理,請看下一篇部落格