/* * Invoke a method, using the specified arguments and return type, through * one of the reflection interfaces. Could be a virtual or direct method * (including constructors). Used for reflection. * * Deals with boxing/unboxing primitives and performs widening conversions. * * "invokeObj" will be null for a static method. * * If the invocation returns with an exception raised, we have to wrap it. */ Object* dvmInvokeMethod(Object* obj, const Method* method, ArrayObject* argList, ArrayObject* params, ClassObject* returnType, bool noAccessCheck) { //salma // __android_log_print(ANDROID_LOG_DEBUG, "DVM DEBUG", "dvmInvokeMethod method name = %s\n,clazz name: %s", method->name, method->clazz->descriptor); //end salma ClassObject* clazz; Object* retObj = NULL; Thread* self = dvmThreadSelf(); s4* ins; int verifyCount, argListLength; JValue retval; bool needPop = false; /* verify arg count */ if (argList != NULL) argListLength = argList->length; else argListLength = 0; if (argListLength != (int) params->length) { dvmThrowExceptionFmt(gDvm.exIllegalArgumentException, "wrong number of arguments; expected %d, got %d", params->length, argListLength); return NULL; } clazz = callPrep(self, method, obj, !noAccessCheck); if (clazz == NULL) return NULL; needPop = true; /* "ins" for new frame start at frame pointer plus locals */ ins = ((s4*)self->interpSave.curFrame) + (method->registersSize - method->insSize); verifyCount = 0; //ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins); /* put "this" pointer into in0 if appropriate */ if (!dvmIsStaticMethod(method)) { assert(obj != NULL); *ins++ = (s4) obj; verifyCount++; } /* * Copy the args onto the stack. Primitive types are converted when * necessary, and object types are verified. */ DataObject** args = (DataObject**)(void*)argList->contents; ClassObject** types = (ClassObject**)(void*)params->contents; for (int i = 0; i < argListLength; i++) { int width = dvmConvertArgument(*args++, *types++, ins); if (width < 0) { dvmPopFrame(self); // throw wants to pull PC out of stack needPop = false; throwArgumentTypeMismatch(i, *(types-1), *(args-1)); goto bail; } ins += width; verifyCount += width; } #ifndef NDEBUG if (verifyCount != method->insSize) { ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount, method->insSize, clazz->descriptor, method->name); assert(false); goto bail; } #endif if (dvmIsNativeMethod(method)) { TRACE_METHOD_ENTER(self, method); /* * Because we leave no space for local variables, "curFrame" points * directly at the method arguments. */ (*method->nativeFunc)((u4*)self->interpSave.curFrame, &retval, method, self); TRACE_METHOD_EXIT(self, method); } else { dvmInterpret(self, method, &retval); } /* * Pop the frame immediately. The "wrap" calls below can cause * allocations, and we don't want the GC to walk the now-dead frame. */ dvmPopFrame(self); needPop = false; /* * If an exception is raised, wrap and replace. This is necessary * because the invoked method could have thrown a checked exception * that the caller wasn't prepared for. * * We might be able to do this up in the interpreted code, but that will * leave us with a shortened stack trace in the top-level exception. */ if (dvmCheckException(self)) { dvmWrapException("Ljava/lang/reflect/InvocationTargetException;"); } else { /* * If this isn't a void method or constructor, convert the return type * to an appropriate object. * * We don't do this when an exception is raised because the value * in "retval" is undefined. */ if (returnType != NULL) { retObj = (Object*)dvmBoxPrimitive(retval, returnType); dvmReleaseTrackedAlloc(retObj, NULL); } } bail: if (needPop) { dvmPopFrame(self); } return retObj; }
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); } } }