/* * This is the common message body for proxy methods. * * The method we're calling looks like: * public Object invoke(Object proxy, Method method, Object[] args) * * This means we have to create a Method object, box our arguments into * a new Object[] array, make the call, and unbox the return value if * necessary. */ static void proxyInvoker(const u4* args, JValue* pResult, const Method* method, Thread* self) { Object* thisObj = (Object*) args[0]; Object* methodObj = NULL; ArrayObject* argArray = NULL; Object* handler; Method* invoke; ClassObject* returnType; int hOffset; JValue invokeResult; /* * Retrieve handler object for this proxy instance. */ hOffset = dvmFindFieldOffset(thisObj->clazz, "h", "Ljava/lang/reflect/InvocationHandler;"); if (hOffset < 0) { LOGE("Unable to find 'h' in Proxy object\n"); dvmAbort(); } handler = dvmGetFieldObject(thisObj, hOffset); /* * Find the invoke() method, looking in "this"s class. (Because we * start here we don't have to convert it to a vtable index and then * index into this' vtable.) */ invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); if (invoke == NULL) { LOGE("Unable to find invoke()\n"); dvmAbort(); } LOGV("invoke: %s.%s, this=%p, handler=%s\n", method->clazz->descriptor, method->name, thisObj, handler->clazz->descriptor); /* * Create a java.lang.reflect.Method object for this method. * * We don't want to use "method", because that's the concrete * implementation in the proxy class. We want the abstract Method * from the declaring interface. We have a pointer to it tucked * away in the "insns" field. * * TODO: this could be cached for performance. */ methodObj = dvmCreateReflectMethodObject((Method*) method->insns); if (methodObj == NULL) { assert(dvmCheckException(self)); goto bail; } /* * Determine the return type from the signature. * * TODO: this could be cached for performance. */ returnType = dvmGetBoxedReturnType(method); if (returnType == NULL) { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGE("Could not determine return type for '%s'\n", desc); free(desc); assert(dvmCheckException(self)); goto bail; } LOGV(" return type will be %s\n", returnType->descriptor); /* * Convert "args" array into Object[] array, using the method * signature to determine types. If the method takes no arguments, * we must pass null. */ argArray = boxMethodArgs(method, args+1); if (dvmCheckException(self)) goto bail; /* * Call h.invoke(proxy, method, args). * * We don't need to repackage exceptions, so if one has been thrown * just jump to the end. */ dvmCallMethod(self, invoke, handler, &invokeResult, thisObj, methodObj, argArray); if (dvmCheckException(self)) goto bail; /* * Unbox the return value. If it's the wrong type, throw a * ClassCastException. If it's a null pointer and we need a * primitive type, throw a NullPointerException. */ if (returnType->primitiveType == PRIM_VOID) { LOGVV("+++ ignoring return to void\n"); } else if (invokeResult.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowException("Ljava/lang/NullPointerException;", "null result when primitive expected"); goto bail; } pResult->l = NULL; } else { if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) { dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;", ((Object*)invokeResult.l)->clazz->descriptor); goto bail; } } bail: dvmReleaseTrackedAlloc(methodObj, self); dvmReleaseTrackedAlloc((Object*)argArray, self); }
static void dexspyCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) { OriginalMethodsIt original = findOriginalMethod(method); if (original == dexspyOriginalMethods.end()) { dvmThrowNoSuchMethodError("could not find Dexspy original method - how did you even get here?"); return; } ThreadStatus oldThreadStatus = self->status; JNIEnv* env = self->jniEnv; // get java.lang.reflect.Method object for original method jobject originalReflected = env->ToReflectedMethod( (jclass)dexspyAddLocalReference(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*) dexspyAddLocalReference(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++, dexspyAddLocalReference(self, obj)); } // call the Java handler function jobject resultRef = env->CallStaticObjectMethod( dexspyClass, dexspyHandleHookedMethod, 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); }
static void xposedCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) { if (!xposedIsHooked(method)) { dvmThrowNoSuchMethodError( "could not find Xposed original method - how did you even get here?"); return; } XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns; Method* original = (Method*) hookInfo; Object* originalReflected = hookInfo->reflectedMethod; Object* additionalInfo = hookInfo->additionalInfo; // 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*) args[0]; srcIndex++; } ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT); if (argsArray == NULL) { return; } 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, self); break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, self); break; case '[': case 'L': obj = (Object*) args[srcIndex++]; break; default: ALOGE("Unknown method signature description character: %c\n", descChar); obj = NULL; srcIndex++; } xposedSetObjectArrayElement(argsArray, dstIndex++, obj); } // call the Java handler function JValue result; dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result, originalReflected, (int) original, additionalInfo, thisObject, argsArray); dvmReleaseTrackedAlloc(argsArray, self); // exceptions are thrown to the caller if (dvmCheckException(self)) { return; } // return result with proper type ClassObject* returnType = dvmGetBoxedReturnType(method); if (returnType->primitiveType == PRIM_VOID) { // ignored } else if (result.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowNullPointerException("null result when primitive expected"); } pResult->l = NULL; } else { if (!dvmUnboxPrimitive(result.l, returnType, pResult)) { dvmThrowClassCastException(result.l->clazz, returnType); } } }