目前我正在开发一个项目,我需要拦截java本机方法调用的结果以进行进一步分析。
有多种方法可以实现这一点,但我选择的方式是:在本机绑定时,将java本机方法的地址重新绑定到我自己的包装函数的地址,这将调用初始本机函数本身然后在拦截返回的变量时返回其结果。包装函数应该是通用的,这意味着它将以正确的方式处理返回类型和参数。
在下面的代码中,nativeIntInterceptor包装int返回函数:
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;
}
};
现在,在我的代理中,在本机绑定时,我需要初始化绑定对象,以便将本机函数绑定到这些对象中的包装函数。这是代码:
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;
}
}
此时调用本机方法时,将正确调用包装函数(nativeIntInterceptor)。但是,它不能访问所有这些局部变量,只打印我
EXECUTING(null)WITH SIGNATURE(null)
基本上我的包装函数,当通过指针调用时,无法看到对象的局部变量,因此无法调用原始的本机函数...有没有办法解决这个问题?如何为我的包装函数提供所有必需的信息?