Skip to content

Instantly share code, notes, and snippets.

@korobochka
Created May 7, 2015 14:47
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save korobochka/3bf2f906f6ab85b22dec to your computer and use it in GitHub Desktop.
JVM TI agent
#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)
{
}
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