currently i am working on a project where i need to intercept the results of java native method calls for further analysis.
There are multiple ways to achieve that but the way of my choice is: at native binding time, re-bind the addresses of java native methods to the address of my own wrapper-function, which would call the initial native function by itself and then return its result while intercepting the returned variable. The wrapper function should be generic, meaning it would handle the return-type and parameters in a right manner.
In the code below, nativeIntInterceptor is wrapping int returning functions:
typedef jint (JNICALL * intNativeCall) (JNIEnv *env, jclass clazz, jobject obj);
struct binding{
public:
jmethodID methId;
intNativeCall initialMethodAddress;
void* newMethodAddress;
char* name;
char* sig;
binding(jmethodID methId, void* methodAddress, char* name, char* sig){
printf("creating binding for %s ith signature %s \n", name , sig);
this->methId = methId;
this->name = name;
this->sig=sig;
this->initialMethodAddress = (intNativeCall) methodAddress;
this->newMethodAddress =(void*) &nativeIntInterceptor;
}
jint JNICALL nativeIntInterceptor(JNIEnv *env, jclass clazz, jobject obj)
{
printf("EXECUTING %s WITH SIGNATURE %s \n",name,sig);
jint returnVal = initialMethodAddress(env, clazz, obj);
printf("RESULT WAS %d ...\n",returnVal);
return returnVal;
}
};
Now, in my agent, at native binding time, I need to initialize the binding objects in order to bind native functions to the wrapper functions in those objects. Here is the code:
void JNICALL
NativeMethodBind(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
void* address,
void** new_address_ptr)
{
char* name_ptr;
char* signature_ptr;
char* generic_ptr;
jvmti_env->GetMethodName(method, &name_ptr, &signature_ptr, &generic_ptr);
if (strstr(signature_ptr, ")V") != NULL) {
// printf("VOID RETURNING METHOD\n");
}else if(strstr(signature_ptr, ")I") != NULL){
// binding jint returning method
binding* b = new binding(method, address, name_ptr, signature_ptr);
bindings.push_back(*b);
*new_address_ptr = b->newMethodAddress;
}
}
At this point when I the native method is called, the wrapper function ( nativeIntInterceptor ) gets called correctly. However, it doesnt have access to all these local variables and just prints me
EXECUTING (null) WITH SIGNATURE (null)
Basically my wrapper function, when called through a pointer, cannot see the object's local variables and thus cannot call the original native function... Is there a way to resolve this? How can i provide my wrapper function with all the required infos?