/* * Return a new Object[] array with the contents of "args". We determine * the number and types of values in "args" based on the method signature. * Primitive types are boxed. * * Returns NULL if the method takes no arguments. * * The caller must call dvmReleaseTrackedAlloc() on the return value. * * On failure, returns with an appropriate exception raised. */ static ArrayObject* boxMethodArgs(const Method* method, const u4* args) { const char* desc = &method->shorty[1]; // [0] is the return type. /* count args */ size_t argCount = dexProtoGetParameterCount_fnPtr(&method->prototype); /* allocate storage */ ArrayObject* argArray = dvmAllocArrayByClass_fnPtr(classJavaLangObjectArray, argCount, ALLOC_DEFAULT); if (argArray == NULL) return NULL; Object** argObjects = (Object**) (void*) argArray->contents; /* * Fill in the array. */ size_t srcIndex = 0; size_t dstIndex = 0; while (*desc != '\0') { char descChar = *(desc++); jvalue value; switch (descChar) { case 'Z': case 'C': case 'F': case 'B': case 'S': case 'I': value.i = args[srcIndex++]; argObjects[dstIndex] = (Object*) dvmBoxPrimitive_fnPtr(value, dvmFindPrimitiveClass_fnPtr(descChar)); /* argObjects is tracked, don't need to hold this too */ dvmReleaseTrackedAlloc_fnPtr(argObjects[dstIndex], NULL); dstIndex++; break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; argObjects[dstIndex] = (Object*) dvmBoxPrimitive_fnPtr(value, dvmFindPrimitiveClass_fnPtr(descChar)); dvmReleaseTrackedAlloc_fnPtr(argObjects[dstIndex], NULL); dstIndex++; break; case '[': case 'L': argObjects[dstIndex++] = (Object*) args[srcIndex++]; LOGD("boxMethodArgs object: index = %d", dstIndex - 1); break; } } return argArray; }
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); }
/* * Return a new Object[] array with the contents of "args". We determine * the number and types of values in "args" based on the method signature. * Primitive types are boxed. * * Returns NULL if the method takes no arguments. * * The caller must call dvmReleaseTrackedAlloc() on the return value. * * On failure, returns with an appropriate exception raised. */ static ArrayObject* boxMethodArgs(const Method* method, const u4* args) { const char* desc = &method->shorty[1]; // [0] is the return type. ArrayObject* argArray = NULL; int argCount; Object** argObjects; bool failed = true; /* count args */ argCount = dexProtoGetParameterCount(&method->prototype); /* allocate storage */ argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount, kObjectArrayRefWidth, ALLOC_DEFAULT); if (argArray == NULL) goto bail; argObjects = (Object**) argArray->contents; /* * Fill in the array. */ int srcIndex = 0; argCount = 0; while (*desc != '\0') { char descChar = *(desc++); JValue value; switch (descChar) { case 'Z': case 'C': case 'F': case 'B': case 'S': case 'I': value.i = args[srcIndex++]; argObjects[argCount] = (Object*) dvmWrapPrimitive(value, dvmFindPrimitiveClass(descChar)); /* argObjects is tracked, don't need to hold this too */ dvmReleaseTrackedAlloc(argObjects[argCount], NULL); argCount++; break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; argObjects[argCount] = (Object*) dvmWrapPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(argObjects[argCount], NULL); argCount++; break; case '[': case 'L': argObjects[argCount++] = (Object*) args[srcIndex++]; break; } } failed = false; bail: if (failed) { dvmReleaseTrackedAlloc((Object*)argArray, NULL); argArray = NULL; } return argArray; }
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); } } }