static void de_robv_android_xposed_XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject declaredClassIndirect, jint slot) { // Usage errors? if (declaredClassIndirect == NULL) { dvmThrowIllegalArgumentException("declaredClass must not be null"); return; } // Find the internal representation of the method ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect); Method* method = dvmSlotToMethod(declaredClass, slot); if (method == NULL) { dvmThrowNoSuchMethodError("could not get internal representation for method"); return; } if (findXposedOriginalMethod(method) != xposedOriginalMethods.end()) { // already hooked return; } // Save a copy of the original method xposedOriginalMethods.push_front(*((MethodXposedExt*)method)); // Replace method with our own code SET_METHOD_FLAG(method, ACC_NATIVE); method->nativeFunc = &xposedCallHandler; method->registersSize = method->insSize; method->outsSize = 0; if (PTR_gDvmJit != NULL) { // reset JIT cache MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true; } }
static void de_robv_android_xposed_XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect, jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) { // Usage errors? if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) { dvmThrowIllegalArgumentException( "method and declaredClass must not be null"); return; } // Find the internal representation of the method ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef( dvmThreadSelf(), declaredClassIndirect); Method* method = dvmSlotToMethod(declaredClass, slot); if (method == NULL) { dvmThrowNoSuchMethodError( "could not get internal representation for method"); return; } if (xposedIsHooked(method)) { ALOGD("Hook: Ignored! [%s] [%s]\n", declaredClass->descriptor, method->name); // already hooked return; } else { ALOGD("Hook: [%s] [%s]\n", declaredClass->descriptor, method->name); } // Save a copy of the original method and other hook info XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo)); memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct)); hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect)); hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect)); // Replace method with our own code SET_METHOD_FLAG(method, ACC_NATIVE); method->nativeFunc = &xposedCallHandler; method->insns = (const u2*) hookInfo; method->registersSize = method->insSize; method->outsSize = 0; if (PTR_gDvmJit != NULL) { // reset JIT cache MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true; } }
static void xposedCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) { XposedOriginalMethodsIt original = findXposedOriginalMethod(method); if (original == xposedOriginalMethods.end()) { dvmThrowNoSuchMethodError("could not find Xposed original method - how did you even get here?"); return; } ThreadStatus oldThreadStatus = MEMBER_VAL(self, Thread, status); JNIEnv* env = MEMBER_VAL(self, Thread, jniEnv); // get java.lang.reflect.Method object for original method jobject originalReflected = env->ToReflectedMethod( (jclass)xposedAddLocalReference(self, original->clazz), (jmethodID)method, true); // convert/box arguments const char* desc = &method->shorty[1]; // [0] is the return type. Object* thisObject = NULL; size_t srcIndex = 0; size_t dstIndex = 0; // for non-static methods determine the "this" pointer if (!dvmIsStaticMethod(&(*original))) { thisObject = (Object*) xposedAddLocalReference(self, (Object*)args[0]); srcIndex++; } jclass objectClass = env->FindClass("java/lang/Object"); jobjectArray argsArray = env->NewObjectArray(strlen(method->shorty) - 1, objectClass, NULL); while (*desc != '\0') { char descChar = *(desc++); JValue value; Object* obj; switch (descChar) { case 'Z': case 'C': case 'F': case 'B': case 'S': case 'I': value.i = args[srcIndex++]; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, NULL); break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, NULL); break; case '[': case 'L': obj = (Object*) args[srcIndex++]; break; default: ALOGE("Unknown method signature description character: %c\n", descChar); obj = NULL; srcIndex++; } env->SetObjectArrayElement(argsArray, dstIndex++, xposedAddLocalReference(self, obj)); } // call the Java handler function jobject resultRef = env->CallStaticObjectMethod( xposedClass, xposedHandleHookedMethod, originalReflected, thisObject, argsArray); // exceptions are thrown to the caller if (env->ExceptionCheck()) { dvmChangeStatus(self, oldThreadStatus); return; } // return result with proper type Object* result = dvmDecodeIndirectRef(self, resultRef); ClassObject* returnType = dvmGetBoxedReturnType(method); if (returnType->primitiveType == PRIM_VOID) { // ignored } else if (result == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowNullPointerException("null result when primitive expected"); } pResult->l = NULL; } else { if (!dvmUnboxPrimitive(result, returnType, pResult)) { dvmThrowClassCastException(result->clazz, returnType); } } // set the thread status back to running. must be done after the last env->...() dvmChangeStatus(self, oldThreadStatus); }