static void unregisterJNINativeMethods(Method* methods, size_t count) {
while(count != 0) {
count–;
Method* meth = &methods[count];
if (!dvmIsNativeMethod(meth)) {
continue;
}
if (dvmIsAbstractMethod(meth)) { /* avoid abstract method stubs */
continue;
}
dvmSetNativeFunc(meth, dvmResolveNativeMethod, NULL); // meth->nativeFunc重新指向dvmResolveNativeMethod
}
}
UnregisterNatives
函数会把jclazz
所在类的所有native方法都重新指向为dvmResolveNativeMethod
,所以调用UnregisterNatives 之后不管是静态注册还是动态注册native方法、之前是否执行过,在加载补丁so的时候都会重新去做映射。
所以我们只需要调用:
static void patchNativeMethod(JNIEnv *env, jclass clz) {
env->UnregisterNatives(clz);
}
这里有一个难点,因为native方法是在so库,所以补丁工具很难检测出到底是哪个Java类需要解除native方法的注册。 这个问题暂且放下。
假设我们现在可以知道哪个具体的Java类需要解除注册native方法,然后load补丁库,再次执行该native方法,按照道理来说是可以让native方法实时生效,但是测试发现,在补丁so库重命名的前提下,Java层native方法可能映射到原so库的方法,也可能映射到补丁so库的修复后的新方法。(即时而生效,时而不生效)
首先,静态注册的native方法之前从未执行过的话或者调用了UnregisterJNINativeMethods方法解除注册,那么该方法将指向dvmResolveNativeMethod(meth->nativeFunc = dvmesolveNativeMethod)
,那么真正运行该方法的时候,实际上执行的是dvmResolveNative()
方法。这个函数主要完成Java层的native方法和native层方法的逻辑映射。
void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) {
void* func = lookupSharedLibMethod(method);
… …
if (func != NULL) {
/