Created
May 7, 2015 14:47
-
-
Save korobochka/3bf2f906f6ab85b22dec to your computer and use it in GitHub Desktop.
JVM TI agent
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdlib> | |
#include <iostream> | |
#include <cstring> | |
#include "jni.h" | |
#include "jvmti.h" | |
#include "JClass.h" | |
#include <string> | |
#include <sstream> | |
#include <fstream> | |
using namespace std; | |
static jrawMonitorID lock; | |
static bool started; | |
static void JNICALL | |
native_method(JNIEnv *env, jclass klass) | |
{ | |
cout << "Native C++ method!" << endl; | |
cout.flush(); | |
} | |
static void JNICALL | |
cbVMStart(jvmtiEnv *jvmti, JNIEnv *jni) | |
{ | |
jvmti->RawMonitorEnter(lock); | |
jclass klass = NULL; | |
klass = jni->FindClass("unitleak/LeakAgentInterface"); | |
static JNINativeMethod methods[1] = { | |
{"_someMethod", "()V", (void*)&native_method}, | |
}; | |
jni->RegisterNatives(klass, methods, 1); | |
jfieldID eng = jni->GetStaticFieldID(klass, "engaged", "I"); | |
jni->SetStaticIntField(klass, eng, 1); | |
jvmti->RawMonitorExit(lock); | |
} | |
static void JNICALL | |
cbVMInit(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) | |
{ | |
jvmti->RawMonitorEnter(lock); | |
started = true; | |
jclass obj = jni->FindClass("java/lang/Object"); | |
jvmti->RetransformClasses(1, &obj); | |
jvmti->RawMonitorExit(lock); | |
} | |
static void JNICALL | |
ClassFileLoadHook(jvmtiEnv *jvmti, | |
JNIEnv* 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) | |
{ | |
jvmti->RawMonitorEnter(lock); | |
if(!started) | |
{ | |
jvmti->RawMonitorExit(lock); | |
return; | |
} | |
if(strcmp(name, "java/lang/Object") != 0) { jvmti->RawMonitorExit(lock); return; } | |
cout << "Will redefine!" << endl; | |
JClass* cla = new JClass(); | |
std::istringstream buffer; | |
buffer.rdbuf()->pubsetbuf((char *)class_data, class_data_len); | |
try | |
{ | |
buffer >> cla; | |
JCode* code = cla->get_code_for("<init>", "()V"); | |
if(code != nullptr) | |
{ | |
constant_label method = cla->add_method_constant("unitleak/LeakAgentInterface", "someMethod", "()V"); | |
code->code.insert(code->code.begin(), { | |
code->make_instruction(JOpCode::invokestatic, operand_constant(method)) | |
}); | |
} | |
std::ostringstream ss; | |
ss << cla; | |
auto str = ss.str(); | |
*new_class_data_len = str.length(); | |
jvmti->Allocate(str.length(), new_class_data); | |
memcpy(*new_class_data, str.c_str(), str.length()); | |
} | |
catch(JError& e) | |
{ | |
cout << "Error: " << e.message << endl; | |
} | |
jvmti->RawMonitorExit(lock); | |
cout << "Class will be redefined now!" << endl; | |
cout.flush(); | |
} | |
JNIEXPORT jint JNICALL | |
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) | |
{ | |
jint rc; | |
jvmtiError err; | |
jvmtiEnv *jvmti; | |
jvmtiEventCallbacks callbacks; | |
rc = vm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1); | |
if (rc != JNI_OK) { | |
printf("ERROR: Unable to create jvmtiEnv, GetEnv failed, error=%d\n", rc); | |
return -1; | |
} | |
jvmtiCapabilities capa; | |
(void)memset(&capa, 0, sizeof(capa)); | |
capa.can_generate_all_class_hook_events = 1; | |
capa.can_retransform_classes = 1; | |
capa.can_retransform_any_class = 1; | |
jvmti->AddCapabilities(&capa); | |
(void)memset(&callbacks, 0, sizeof(callbacks)); | |
callbacks.VMStart = &cbVMStart; | |
callbacks.ClassFileLoadHook = &ClassFileLoadHook; | |
callbacks.VMInit = &cbVMInit; | |
err = jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks)); | |
err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_START, (jthread)NULL); | |
err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL); | |
err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, (jthread)NULL); | |
jvmti->CreateRawMonitor("agent lock", &lock); | |
started = false; | |
err = jvmti->AddToBootstrapClassLoaderSearch("leakAgentInterface.jar"); | |
if(err != JVMTI_ERROR_NONE) cout << "Failed to add jar!" << endl; | |
return JNI_OK; | |
} | |
JNIEXPORT void JNICALL | |
Agent_OnUnload(JavaVM *vm) | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package unitleak; | |
public class LeakAgentInterface { | |
private static int engaged; | |
private static native void _someMethod(); | |
public static void someMethod() { | |
if(engaged != 0) | |
{ | |
System.out.println("JAVA: calling native method"); | |
_someMethod(); | |
} | |
else | |
{ | |
System.out.println("JAVA: not engaged"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
can you show your "JClass.h"