/* * Get an array with all constructors declared by a class. */ ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)) dvmInitClass(gDvm.classJavaLangReflectConstructor); /* * Ordinarily we init the class the first time we resolve a method. * We're bypassing the normal resolution mechanism, so we init it here. */ if (!dvmIsClassInitialized(clazz)) dvmInitClass(clazz); /* * Count up the #of relevant methods. */ size_t count = 0; for (int i = 0; i < clazz->directMethodCount; ++i) { Method* meth = &clazz->directMethods[i]; if ((!publicOnly || dvmIsPublicMethod(meth)) && dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) { count++; } } /* * Create an array of Constructor objects. */ ClassObject* arrayClass = gDvm.classJavaLangReflectConstructorArray; ArrayObject* ctorArray = dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT); if (ctorArray == NULL) return NULL; /* * Fill out the array. */ size_t ctorObjCount = 0; for (int i = 0; i < clazz->directMethodCount; ++i) { Method* meth = &clazz->directMethods[i]; if ((!publicOnly || dvmIsPublicMethod(meth)) && dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) { Object* ctorObj = createConstructorObject(meth); if (ctorObj == NULL) { dvmReleaseTrackedAlloc((Object*) ctorArray, NULL); return NULL; } dvmSetObjectArrayElement(ctorArray, ctorObjCount, ctorObj); ++ctorObjCount; dvmReleaseTrackedAlloc(ctorObj, NULL); } } assert(ctorObjCount == ctorArray->length); /* caller must call dvmReleaseTrackedAlloc */ return ctorArray; }
/* * Convert the method signature to an array of classes. * * The tokenization process may mangle "*pSignature". On return, it will * be pointing at the closing ')'. * * "defClass" is the method's class, which is needed to make class loaders * happy. */ static ArrayObject* convertSignatureToClassArray(char** pSignature, ClassObject* defClass) { char* signature = *pSignature; assert(*signature == '('); signature++; /* count up the number of parameters */ size_t count = 0; char* cp = signature; while (*cp != ')') { count++; if (*cp == '[') { while (*++cp == '[') ; } if (*cp == 'L') { while (*++cp != ';') ; } cp++; } LOGVV("REFLECT found %d parameters in '%s'", count, *pSignature); /* create an array to hold them */ ArrayObject* classArray = dvmAllocArrayByClass(gDvm.classJavaLangClassArray, count, ALLOC_DEFAULT); if (classArray == NULL) return NULL; /* fill it in */ cp = signature; for (size_t i = 0; i < count; i++) { ClassObject* clazz = convertSignaturePartToClass(&cp, defClass); if (clazz == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } LOGVV("REFLECT %d: '%s'", i, clazz->descriptor); dvmSetObjectArrayElement(classArray, i, (Object *)clazz); } *pSignature = cp; /* caller must call dvmReleaseTrackedAlloc */ return classArray; }
/* * private static String[] getClassNameList(int cookie) * * Returns a String array that holds the names of all classes in the * specified DEX file. */ static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args, JValue* pResult) { int cookie = args[0]; DexOrJar* pDexOrJar = (DexOrJar*) cookie; DvmDex* pDvmDex; DexFile* pDexFile; ArrayObject* stringArray; Thread* self = dvmThreadSelf(); if (!validateCookie(cookie)) RETURN_VOID(); if (pDexOrJar->isDex) pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); else pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); assert(pDvmDex != NULL); pDexFile = pDvmDex->pDexFile; int count = pDexFile->pHeader->classDefsSize; stringArray = dvmAllocObjectArray(gDvm.classJavaLangString, count, ALLOC_DEFAULT); if (stringArray == NULL) { /* probably OOM */ ALOGD("Failed allocating array of %d strings\n", count); assert(dvmCheckException(self)); RETURN_VOID(); } int i; for (i = 0; i < count; i++) { const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i); const char* descriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); char* className = dvmDescriptorToDot(descriptor); StringObject* str = dvmCreateStringFromCstr(className); dvmSetObjectArrayElement(stringArray, i, (Object *)str); dvmReleaseTrackedAlloc((Object *)str, self); free(className); } dvmReleaseTrackedAlloc((Object*)stringArray, self); RETURN_PTR(stringArray); }
/* * Convert an array of char* into a String[]. * * Returns NULL on failure, with an exception raised. */ static ArrayObject* convertStringArray(char** strings, size_t count) { Thread* self = dvmThreadSelf(); /* * Allocate an array to hold the String objects. */ ClassObject* stringArrayClass = dvmFindArrayClass("[Ljava/lang/String;", NULL); if (stringArrayClass == NULL) { /* shouldn't happen */ LOGE("Unable to find [Ljava/lang/String;\n"); dvmAbort(); } ArrayObject* stringArray = dvmAllocArrayByClass(stringArrayClass, count, ALLOC_DEFAULT); if (stringArray == NULL) { /* probably OOM */ LOGD("Failed allocating array of %d strings\n", count); assert(dvmCheckException(self)); return NULL; } /* * Create the individual String objects and add them to the array. */ size_t i; for (i = 0; i < count; i++) { Object *str = (Object *)dvmCreateStringFromCstr(strings[i]); if (str == NULL) { /* probably OOM; drop out now */ assert(dvmCheckException(self)); dvmReleaseTrackedAlloc((Object*)stringArray, self); return NULL; } dvmSetObjectArrayElement(stringArray, i, str); /* stored in tracked array, okay to release */ dvmReleaseTrackedAlloc(str, self); } dvmReleaseTrackedAlloc((Object*)stringArray, self); return stringArray; }
/* * Get an array with all methods declared by a class. * * This includes both static and virtual methods, and can include private * members if "publicOnly" is false. It does not include Miranda methods, * since those weren't declared in the class, or constructors. */ ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) dvmInitClass(gDvm.classJavaLangReflectMethod); /* * Count up the #of relevant methods. * * Ignore virtual Miranda methods and direct class/object constructors. */ size_t count = 0; Method* meth = clazz->virtualMethods; for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && !dvmIsMirandaMethod(meth)) { count++; } } meth = clazz->directMethods; for (int i = 0; i < clazz->directMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') { count++; } } /* * Create an array of Method objects. */ ArrayObject* methodArray = dvmAllocArrayByClass(gDvm.classJavaLangReflectMethodArray, count, ALLOC_DEFAULT); if (methodArray == NULL) return NULL; /* * Fill out the array. */ meth = clazz->virtualMethods; size_t methObjCount = 0; for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && !dvmIsMirandaMethod(meth)) { Object* methObj = dvmCreateReflectMethodObject(meth); if (methObj == NULL) goto fail; dvmSetObjectArrayElement(methodArray, methObjCount, methObj); ++methObjCount; dvmReleaseTrackedAlloc(methObj, NULL); } } meth = clazz->directMethods; for (int i = 0; i < clazz->directMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') { Object* methObj = dvmCreateReflectMethodObject(meth); if (methObj == NULL) goto fail; dvmSetObjectArrayElement(methodArray, methObjCount, methObj); ++methObjCount; dvmReleaseTrackedAlloc(methObj, NULL); } } assert(methObjCount == methodArray->length); /* caller must call dvmReleaseTrackedAlloc */ return methodArray; fail: dvmReleaseTrackedAlloc((Object*) methodArray, NULL); return NULL; }
/* * * Get an array with all fields declared by a class. * * This includes both static and instance fields. */ ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) dvmInitClass(gDvm.classJavaLangReflectField); /* count #of fields */ size_t count; if (!publicOnly) count = clazz->sfieldCount + clazz->ifieldCount; else { count = 0; for (int i = 0; i < clazz->sfieldCount; i++) { if ((clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0) count++; } for (int i = 0; i < clazz->ifieldCount; i++) { if ((clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0) count++; } } /* create the Field[] array */ ArrayObject* fieldArray = dvmAllocArrayByClass(gDvm.classJavaLangReflectFieldArray, count, ALLOC_DEFAULT); if (fieldArray == NULL) return NULL; /* populate */ size_t fieldCount = 0; for (int i = 0; i < clazz->sfieldCount; i++) { if (!publicOnly || (clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0) { Object* field = createFieldObject(&clazz->sfields[i], clazz); if (field == NULL) { goto fail; } dvmSetObjectArrayElement(fieldArray, fieldCount, field); dvmReleaseTrackedAlloc(field, NULL); ++fieldCount; } } for (int i = 0; i < clazz->ifieldCount; i++) { if (!publicOnly || (clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0) { Object* field = createFieldObject(&clazz->ifields[i], clazz); if (field == NULL) { goto fail; } dvmSetObjectArrayElement(fieldArray, fieldCount, field); dvmReleaseTrackedAlloc(field, NULL); ++fieldCount; } } assert(fieldCount == fieldArray->length); /* caller must call dvmReleaseTrackedAlloc */ return fieldArray; fail: dvmReleaseTrackedAlloc((Object*) fieldArray, NULL); return NULL; }
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++; } dvmSetObjectArrayElement(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); } } }
/* * Generate an array of StackTraceElement objects from the raw integer * data encoded by dvmFillInStackTrace(). * * "intVals" points to the first {method,pc} pair. * * The returned array is not added to the "local refs" list. */ ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth) { ArrayObject* steArray = NULL; int i; /* init this if we haven't yet */ if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement)) dvmInitClass(gDvm.classJavaLangStackTraceElement); /* allocate a StackTraceElement array */ steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray, stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT); if (steArray == NULL) goto bail; /* * Allocate and initialize a StackTraceElement for each stack frame. * We use the standard constructor to configure the object. */ for (i = 0; i < stackDepth; i++) { Object* ste; Method* meth; StringObject* className; StringObject* methodName; StringObject* fileName; int lineNumber, pc; const char* sourceFile; char* dotName; ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT); if (ste == NULL) goto bail; meth = (Method*) *intVals++; pc = *intVals++; if (pc == -1) // broken top frame? lineNumber = 0; else lineNumber = dvmLineNumFromPC(meth, pc); dotName = dvmDescriptorToDot(meth->clazz->descriptor); className = dvmCreateStringFromCstr(dotName); free(dotName); methodName = dvmCreateStringFromCstr(meth->name); sourceFile = dvmGetMethodSourceFile(meth); if (sourceFile != NULL) fileName = dvmCreateStringFromCstr(sourceFile); else fileName = NULL; /* * Invoke: * public StackTraceElement(String declaringClass, String methodName, * String fileName, int lineNumber) * (where lineNumber==-2 means "native") */ JValue unused; dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init, ste, &unused, className, methodName, fileName, lineNumber); dvmReleaseTrackedAlloc(ste, NULL); dvmReleaseTrackedAlloc((Object*) className, NULL); dvmReleaseTrackedAlloc((Object*) methodName, NULL); dvmReleaseTrackedAlloc((Object*) fileName, NULL); if (dvmCheckException(dvmThreadSelf())) goto bail; dvmSetObjectArrayElement(steArray, i, ste); } bail: dvmReleaseTrackedAlloc((Object*) steArray, NULL); return steArray; }